これはJakarta EE / Java EE Advent Calendar 2025の7日目の記事です。
昨日は@kazumura によるJava系各種Landscape #AI - Qiitaで、
明日はQuarkusの記事です。(雑)
そろそろ上にあるリンクを編集するのも面倒くさくなってきましたが、皆様いかがお過ごしでしょうか?
Jakarta EE 11仕様のJPAでは大きな機能が非推奨化されました。
なんと、昔ながらの日付APIが非推奨化されました。
Deprecates usage of Calendar, Date, Time, Timestamp, Temporal, MapKeyTemporal and TemporalType in new applications in favour of java.time API
今後は、Java 8から導入されたDate and Time APIを使用する事が推奨されます。LocalDateなどです。なお、JPAではJava EE 8の頃からDate and TimeAPIをサポートしていますので、すでに使われている方も多いのではないかと思います。
古い日付APIが使われなくなると、java.util.Dateとjava.sql.Dateとjava.sql.Timestampの微妙な挙動の差に悩まされたりしなくなるのですが、多くのアプリケーションはいまだに古い日付APIを使い続けてると思いますので、対応を考える必要が出てきそうですね。
ただ、現実問題としては仕様としては将来的に削除されたとしても実装側では残り続けそうな気はします。さすがに今の使われ方を考えると消せない気がする・・・・・
ということで、簡単ですがここまでで。
これはJakarta EE / Java EE Advent Calendar 2025の5日目の記事です。
昨日は@megascus によるJBoss EAP 8に環境変数を使って値を設定する/7以前と挙動が変わっているので注意 - 赤帽エンジニアブログで、明日は@kazumura による記事です。
Jakarta EE 11ではJakarta Data が追加されたことが大きく取り上げられていますが、実は、そちらのベースとなる機能であるJakarta Persistenceの方も多数の機能が追加されています。
この記事では、Jakarta Persistence 3.2で増えたJPQLの構文を紹介します。
JPQLでは複数のSELECT文を結合する構文が追加されました。次のキーワードが使用できるようになっています。
UNION, UNION ALL, INTERSECT, INTERSECT ALL, EXCEPT, EXCEPT ALL
完全なシンタックスは次のように定義されています。
select_statement ::= unionunion ::= intersection | union {UNION [ALL] | EXCEPT [ALL]} intersectionintersection ::= query_expression | intersection INTERSECT [ALL] query_expressionquery_expression ::= select_query | (union)これらの意味はSQLで使われているキーワードと全く同じです。
文字列の結合に || が使えるようになりました。CONCATを書かなくてもよくなります。
エンティティのIDおよびエンティティのバージョンを返すファンクションが追加されました。
次のように使用できます。
DELETEfrom EmployeeWHERE id(this) = :idAND version(this) = :version
エンティティではIDやversion付けのための属性については、@Idや@Versionというアノテーションを付けて管理をします。それらについて、今まではアノテーションが付いているにもかかわらず、JPQLで直接指定する必要がありました。それが直接指定しなくても良くなったことになります。
文字列と数値を切り替えるcastファンクションが追加されました。構文は次のようになります。
string_cast_function::= CAST(scalar_expression AS STRING)arithmetic_cast_function::= CAST(string_expression AS {INTEGER | LONG | FLOAT | DOUBLE})ただし、キャストの結果がどうなるかについてはデータベースに依存するようです。
left、rightは1番目の文字列から2番目の引数の数値分の部分文字列を返し、replaceは1番目の文字列から2番目の文字列を検索し、3番目の文字列に置き換えます。
order byでnullを先に表示するか、後に表示するかが指定できるようになりました。また、order byにスカラー式を指定できるようになりました。
新しいシンタックスは次のようになります。
orderby_clause ::= ORDER BY orderby_item {, orderby_item}* orderby_item ::= orderby_expression [ASC | DESC] [NULLS {FIRST | LAST}] orderby_expression ::= state_field_path_expression | general_identification_variable | result_variable | scalar_expressionということで、簡単にJPQLで増えた構文について説明しました。特に、UNION系が増えたことは嬉しい人は非常に嬉しいんじゃないでしょうか。
JPQLで説明しましたが、これらについてはCriteriaQueryでも対応するメソッドが追加されています。
Jakarta PersistenceはHibernateチームの尽力もあり、一番アクティブに更新されている機能ですので、ぜひお試しください。
Jakarta EE 11(Servlet 6.1)でHttpSessionに対する新しい制約が増えていました。
HttpSessionをHttpRequestのスコープ外から使用する事が禁止されました。そのため、外部から使用できるようにHttpSession.Accessorというインターフェースが増えています。
業務要件などにより特定の条件で一括してログアウトしたい場合とかのためにHttpSessionの一覧を取っておきたい場合があったのですが、GCとかで不都合があったんでしょうねぇ。。。
ということで、試してみようと思います。
Rootにアクセスしてセッションを作成するクラス。
import java.io.IOException;import jakarta.servlet.ServletException;import jakarta.servlet.annotation.WebServlet;import jakarta.servlet.http.HttpServlet;import jakarta.servlet.http.HttpServletRequest;import jakarta.servlet.http.HttpServletResponse;@WebServlet(urlPatterns ="/")publicclass Rootextends HttpServlet {@Overrideprotectedvoid doGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException { req.getSession(true); resp.getWriter().write("accessed!"); }}
Listenerを作ってAccessorをキャッシュします。ついでに古い機能が動くかどうかを試すためにHttpSessionもキャッシュします。
import java.util.ArrayList;import java.util.Collections;import java.util.List;import jakarta.servlet.annotation.WebListener;import jakarta.servlet.http.HttpSession;import jakarta.servlet.http.HttpSession.Accessor;import jakarta.servlet.http.HttpSessionEvent;import jakarta.servlet.http.HttpSessionListener;@WebListenerpublicclass MySessionListenerimplements HttpSessionListener {publicstatic List<Accessor> accessors = Collections.synchronizedList(new ArrayList<>());//正しいやり方publicstatic List<HttpSession> sessions = Collections.synchronizedList(new ArrayList<>());//今後Invalidになる@Overridepublicvoid sessionCreated(HttpSessionEvent se) { accessors.add(se.getSession().getAccessor()); sessions.add(se.getSession()); }//sessionDestroyedでListから取り除くのは省略}
/listには一覧をループしてIDを表示するようなプログラムを書いてます。
import java.io.IOException;import java.io.PrintWriter;import jakarta.servlet.ServletException;import jakarta.servlet.annotation.WebServlet;import jakarta.servlet.http.HttpServlet;import jakarta.servlet.http.HttpServletRequest;import jakarta.servlet.http.HttpServletResponse;@WebServlet(urlPatterns ="/list")publicclass GetSessionListextends HttpServlet {@Overrideprotectedvoid doGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {final PrintWriter writer = resp.getWriter(); writer.write("Accessor:"); MySessionListener.accessors.forEach(t -> t.access(s -> writer.write(s.getId()+"\r\n"))); writer.write("HttpSession:"); MySessionListener.sessions.forEach(s -> writer.write(s.getId()+"\r\n")); }}
curl http://localhost:8080/curl http://localhost:8080/curl http://localhost:8080/curl http://localhost:8080/
で、/listにアクセスします。
Accessor:A3B1369126D75435EEE5FB975B91C5C7F9EC68EC8447FF7EE0948561B867ABBC5C74C432AB5199D634F282D149C5A5FD2F4E054EDF06AF15DC43E6132A8F74C9HttpSession:A3B1369126D75435EEE5FB975B91C5C7F9EC68EC8447FF7EE0948561B867ABBC5C74C432AB5199D634F282D149C5A5FD2F4E054EDF06AF15DC43E6132A8F74C9
同じセッションオブジェクトにアクセスできてそうです。
また、現時点のTomcatでは、少なくともSessionが生きている段階において、HttpSessionを直接キャッシュして使用しても即座にエラーになることはなさそうです。
ただ、このやり方でAccessorを経由してHttpSessionにアクセスしてSessionを無効化してもHttpServletRequest#logout()は呼び出せていないため、SSOを使用している場合でのログアウトには問題が発生することがあります。また、仕組み的にHttpSessionからHttpServletRequestオブジェクトにアクセスすることは出来ません。IDPの方での手当を考えないとですかねぇ。
Quarkus 3.13.3 released - Maintenance release というのがありました。
これって何が原因かというと、使用しているライブラリの乱数生成にLinuxの/dev/random にアクセスして乱数を取得するものがあったことです。
Linuxの/dev/random はデバイスの入力などをもとに完全な乱数を生成することができるものでした。問題点として、デバイスの入力などを基に乱数を生成するので、デバイスからの入力が十分に無い場合、例えばサーバー用途でユーザーからの入力が行われない場合は乱数の生成ができずにブロックされてしまいます。つまり、/dev/random にアクセスしても応答が返ってきません。
古くからこの問題は良く知られており、よく知られた解決策としては/dev/urandom を使用する事です。
Java でもFAQレベルで多くの記事が見つかります。
/dev/random について詳しく知りたい人は以下の記事でも。
で、最近Linux kernelの5系の途中からこの/dev/random がブロックしなくなりました。RHELだと8まではブロックして、9からはブロックしなくなります。
で、何が発生するかというと、RHEL 9や他の最新のLinuxでは動いたものが8以前、もしくは他のLinuxのちょっと古い環境では動かなくなる可能性があるという事です。
当たった人南無。
Quarkusのテストでこれが見つからなかった原因かどうかは知らないけどね。
ひとつ前の記事でWildFly-27.0.0.1.Beta1を試してみたら簡単に動いてしまったのでKotlinでも試してみました。
WildFly-27.0.0.1.Beta1が立ち上がってる状態で、pom.xmlを書き換えます。
基本的にKotlinの公式サイトに載っているものをそのまま追加しています。(maven-compiler-pluginが古かったのだけ更新)
https://kotlinlang.org/docs/maven.html
<?xml version="1.0" encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"><modelVersion>4.0.0</modelVersion><groupId>dev.megascus</groupId><artifactId>jakartaee10</artifactId><version>0.0.1-SNAPSHOT</version><packaging>war</packaging><name>jakartaee10</name><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.release>11</maven.compiler.release><kotlin.compiler.incremental>true</kotlin.compiler.incremental><kotlin.version>1.7.20</kotlin.version></properties><dependencies><dependency><groupId>jakarta.platform</groupId><artifactId>jakarta.jakartaee-api</artifactId><version>10.0.0</version></dependency><dependency><groupId>org.jetbrains.kotlin</groupId><artifactId>kotlin-stdlib</artifactId><version>${kotlin.version}</version></dependency></dependencies><build><finalName>${project.artifactId}</finalName><plugins><plugin><artifactId>maven-war-plugin</artifactId><version>3.3.2</version><configuration><failOnMissingWebXml>false</failOnMissingWebXml></configuration></plugin><plugin><groupId>org.wildfly.plugins</groupId><artifactId>wildfly-maven-plugin</artifactId><version>3.0.0.Final</version></plugin><plugin><groupId>org.jetbrains.kotlin</groupId><artifactId>kotlin-maven-plugin</artifactId><version>${kotlin.version}</version><executions><execution><id>compile</id><goals><goal>compile</goal></goals><configuration><sourceDirs><sourceDir>${project.basedir}/src/main/kotlin</sourceDir><sourceDir>${project.basedir}/src/main/java</sourceDir></sourceDirs></configuration></execution><execution><id>test-compile</id><goals><goal>test-compile</goal></goals><configuration><sourceDirs><sourceDir>${project.basedir}/src/test/kotlin</sourceDir><sourceDir>${project.basedir}/src/test/java</sourceDir></sourceDirs></configuration></execution></executions></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.9.0</version><executions><!-- Replacing default-compile as it is treated specially by maven --><execution><id>default-compile</id><phase>none</phase></execution><!-- Replacing default-testCompile as it is treated specially by maven --><execution><id>default-testCompile</id><phase>none</phase></execution><execution><id>java-compile</id><phase>compile</phase><goals><goal>compile</goal></goals></execution><execution><id>java-test-compile</id><phase>test-compile</phase><goals><goal>testCompile</goal></goals></execution></executions></plugin></plugins></build></project>
で、前回のTestServletをKotlinで書きなおします。書き直してsrc/main/kotlinに移して、古いものを削除しています。
import java.io.IOException;import jakarta.servlet.ServletException;import jakarta.servlet.annotation.WebServlet;import jakarta.servlet.http.Cookie;import jakarta.servlet.http.HttpServlet;import jakarta.servlet.http.HttpServletRequest;import jakarta.servlet.http.HttpServletResponse;@WebServlet(name ="test", urlPatterns = arrayOf("/*"))class HelloWorld : HttpServlet() {@Overridepublicoverridefun service(req: HttpServletRequest, res: HttpServletResponse) {val cookie = Cookie("TEST","VALUE") cookie.setAttribute("Max-Age","999") res.addCookie(cookie) res.getWriter().write("hello world from kotlin!") }}
最後にmvn wildfly:deploy でデプロイすると普通にアクセスできるようになりました。

WildFlyもαからβに無事になり、そこそこ安定してそうなので試してみました。思っていたよりはすんなり動いてよかったです。
公式サイトにリンクが張られているので、ダウンロードします。
ダウンロードしたら適当な場所に解凍します。
実行する場合はbinの中に入っているstandalone.bat/standalone.ps1/standalone.shをOSに合わせて実行するのが一番早いです。
Javaが入っていない場合は先にJavaをダウンロードしてインストールしている必要があります。
※この記事では、Windows上でjava-11-openjdk-11.0.16-2を使用しています。あと、mavenも。
起動すると以下のようにログが流れて起動します。
Calling "C:\apps\wildfly-27.0.0.Beta1\bin\standalone.conf.bat"Setting JAVA property to "C:\Program Files\RedHat\java-11-openjdk-11.0.16-2\bin\java"=============================================================================== JBoss Bootstrap Environment JBOSS_HOME: "C:\apps\wildfly-27.0.0.Beta1" JAVA: "C:\Program Files\RedHat\java-11-openjdk-11.0.16-2\bin\java" JAVA_OPTS: "-server -Dprogram.name=standalone.bat -Xms64M -Xmx512M -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true --add-exports=java.desktop/sun.awt=ALL-UNNAMED --add-exports=java.naming/com.sun.jndi.ldap=ALL-UNNAMED --add-exports=java.naming/com.sun.jndi.url.ldap=ALL-UNNAMED --add-exports=java.naming/com.sun.jndi.url.ldaps=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.lang.reflect=ALL-UNNAMED --add-opens=java.base/java.security=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.util.concurrent=ALL-UNNAMED --add-opens=java.management/javax.management=ALL-UNNAMED --add-opens=java.naming/javax.naming=ALL-UNNAMED "===============================================================================19:38:37,719 INFO [org.jboss.modules] (main) JBoss Modules version 2.0.3.Final19:38:38,206 INFO [org.jboss.msc] (main) JBoss MSC version 1.4.13.Final19:38:38,211 INFO [org.jboss.threads] (main) JBoss Threads version 2.4.0.Final(中略)19:38:40,595 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: WildFly Full 27.0.0.Beta1 (WildFly Core 19.0.0.Beta18) started in 3107ms - Started 290 of 563 services (357 services are lazy, passive or on-demand) - Server configuration file in use: standalone.xml19:38:40,596 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0060: Http management interface listening on http://127.0.0.1:9990/management19:38:40,597 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0051: Admin console listening on http://127.0.0.1:9990
デフォルトでローカルホストの8080で立ち上がるのでアクセスしてみます。

Jakarta EE 10のdependencyがあるので、それを使います。それ以外はwildfly-maven-pluginが入っている以外は普通のJakarta EE(Java EE)の最小限のpom.xmlです。
<?xml version="1.0" encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"><modelVersion>4.0.0</modelVersion><groupId>dev.megascus</groupId><artifactId>jakartaee10</artifactId><version>0.0.1-SNAPSHOT</version><packaging>war</packaging><name>jakartaee10</name><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.release>11</maven.compiler.release></properties><dependencies><dependency><groupId>jakarta.platform</groupId><artifactId>jakarta.jakartaee-api</artifactId><version>10.0.0</version></dependency></dependencies><build><finalName>${project.artifactId}</finalName><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.9.0</version></plugin><plugin><artifactId>maven-war-plugin</artifactId><version>3.3.2</version><configuration><failOnMissingWebXml>false</failOnMissingWebXml></configuration></plugin><plugin><groupId>org.wildfly.plugins</groupId><artifactId>wildfly-maven-plugin</artifactId><version>3.0.0.Final</version></plugin></plugins></build></project>
最低限のサーブレットと、Jakarta EE 10で新しく生えたAPIを使いたかったので、Cookie#setAttributeを呼び出してみています。
ネームスペースがjavaxからjakartaに変更されているのに注意してください。
package dev.megascus.jakartaee10;import java.io.IOException;import jakarta.servlet.ServletException;import jakarta.servlet.annotation.WebServlet;import jakarta.servlet.http.Cookie;import jakarta.servlet.http.HttpServlet;import jakarta.servlet.http.HttpServletRequest;import jakarta.servlet.http.HttpServletResponse;@WebServlet(name ="test", urlPatterns ="/*")publicclass TestServletextends HttpServlet {@Overridepublicvoid service(HttpServletRequest req, HttpServletResponse res)throws ServletException, IOException { Cookie cookie =new Cookie("TEST","VALUE"); cookie.setAttribute("Max-Age","999"); res.addCookie(cookie); res.getWriter().write("hello world!"); }}
デプロイは簡単で以下コマンドを打つだけです。
mvn wildfly:deploy
デプロイ先にアクセスしてみると確かにhello world!と表示されています。また、Cookieも追加されていました。


ということで、Jakarta EE 10がリリースされて、すでにベータ版のリリースは始まっています。早めに試してみてはいかがでしょうか。
引用をストックしました
引用するにはまずログインしてください
引用をストックできませんでした。再度お試しください
限定公開記事のため引用できません。