Jakarta Persistence 3.1 -> 3.2 変更点まとめ

blog1.mammb.com


Jakarta Persistence 3.1 -> 3.2 の変更は Github Project で管理されている。

マイナーバージョンアップになっているが、数多くの変更点があるので、ここにまとめる(2023年11月現在)。


Java レコードのサポート

#400 allow Java records as embeddable types

Java レコードが @IdClass@Embeddable として利用可能となった。


検索オプション FindOption の追加

#383 typesafe way to pass options and an EntityGraph to find() #65 Need another property to make lazy loading of attributes easy with entity graphs

FindOption(これ自体は単なるマーカーインターフェース) が追加され、検索オプションなどを指定可能となった。

以下のようなオプションを

public enum CacheRetrieveMode implements FindOption { ... }
public enum CacheStoreMode implements FindOption, RefreshOption { ... }
public enum LockModeType implements FindOption, RefreshOption { ... }
public enum PessimisticLockScope implements FindOption, RefreshOption, LockOption { ... }
public class Timeout implements FindOption, RefreshOption, LockOption { ... }

以下の追加されたメソッドで指定可能。

public interface EntityManager extends AutoCloseable {
  <T> T find(Class<T> entityClass, Object primaryKey, FindOption... options);
  <T> T find(EntityGraph<T> entityGraph, Object primaryKey, FindOption... options);
  void lock(Object entity, LockModeType lockMode, LockOption... options);
  void refresh(Object entity, RefreshOption... options);
}

これにより、以下のようにオプションを指定したクエリが可能となる。

Book book = em.find(Book.class, bookId, 
                    PESSIMISTIC_WRITE, 
                    CacheStoreMode.BYPASS, 
                    Timeout.ms(200));


@EntityResult でロックモードを指定可能に

#472 add lockMode to @EntityResult

EntityResult に以下の属性が追加され、ロックモードを指定可能となった。

@Target({}) 
@Retention(RUNTIME)
public @interface EntityResult {
  LockModeType lockMode() default LockModeType.OPTIMISTIC;
}


order-by 式での nulls first と nulls last サポート

#76 Allow specification for null handling in order-by expressions (JPQL and Criteria API)

CriteriaBuilder に以下が追加され、nulls first と nulls last を指定可能となった。

public interface CriteriaBuilder {
    Order asc(Expression<?> expression, Nulls nullPrecedence);
    Order desc(Expression<?> expression, Nulls nullPrecedence);
}

Nulls は以下の enum 値。

public enum Nulls {
    NONE, FIRST, LAST
}

JPQL では、NULLS FIRST / NULLS LAST を指定可能。


スタティックメタモデルに EntityType を追加

#475 add reference to ManagedType to static metamodel

スタティックメタモデルに以下のような EntityType(ManagedType) が追加されるようになり、Metamodel を介さずに直接取得可能となった。

EntityType<Book> bookType = Book_.class_;

Hibernate 実装からのポート。


スタティックメタモデルに名前付き定数の追加

#459 named queries, entity graphs, and result set mappings in static metamodel generation

スタティックメタモデルに名前付きクエリやエンティティグラフ、リザルトセットマッピングの定数が追加されるようになり、以下のように利用可能となった。

em.createQuery(Book_.QUERY_ALL_BOOKS).getResultList();


EntityManager.getReference(detachedEntity) の追加

#489 Add getReference(detachedEntity) to EntityManager

EntityManager に参照取得の簡易メソッドが追加された。

public interface EntityManager extends AutoCloseable {
    public <T> T getReference(T entity);
}

以下のコードのショートカットとして利用できる。

em.getReference(entity.getClass(),entity.getId())


persistence.xml レス EntityManagerFactory 生成

#358 Add XML-less config to replace persistence.xml and orm.xml #149 Simplify programmatic bootstrapping of an EntityManagerFactory #114 Allow full configuration of EntityManagerFactory without persistence.xml

PersistenceConfiguration が追加され、以下のように persistence.xml 無しで EntityManagerFactory の構築が可能となった。

DataSource datasource = (DataSource)
        new InitialContext().lookup("java:global/jdbc/MyOrderDB");
EntityManagerFactory emf =
        new PersistenceConfiguration()
                .name("OrderManagement")
                .jtaDataSource(datasource)
                .mappingFile("ormap.xml")
                .managedClass(Order.class)
                .managedClass(Customer.class)
                .createEntityManagerFactory();


DDL 生成でオプション文字を追加可能に

#353 support for conditional index

DDL 生成のアノテーションでオプション文字を指定可能となった。 例えば条件付きインデックスを以下のように指定。

@Index(name = "orders_unbilled_index ", 
      columnList = "order_nr", 
      options = "where billed is not true")

Table Column などの多くのアノテーションに options 属性が追加された。


SchemaManager API の追加

#399 SchemaManager #53 Schema generation with existing EMF

テスト時などにプログラムよりスキーマを管理できる SchemaManager が追加された。

SchemaManagerEntityManagerFactory から取得できる。

public interface EntityManagerFactory extends AutoCloseable {
  SchemaManager getSchemaManager();
}

SchemaManager では以下の操作が可能。

public interface SchemaManager {
    void create(boolean createSchemas);
    void drop(boolean dropSchemas);
    void validate() throws SchemaValidationException;
    void truncate();
}


jakarta.persistence.schema-generation.database.action に validate が追加

#68 update and/or validate javax.persistence.schema-generation.database.action

jakarta.persistence.schema-generation.database.actionvalidate を追加(永続化ユニットに属するエンティティによってマッピングされたデータベースオブジェクトが、期待される定義を持っていることを検証)(この動作はプロバイダ固有)。 指定可能な値は、none create drop-and-create drop validate となる。


JavaSE 環境におけるJPAトランザクション操作

#410 add methods for managing Session/Transaction lifecycle to SessionFactory #204 Consider adding convenience methods for wrapping transactions in Java SE environments

JavaSE 環境で JPAによるトランザクション操作を行う場合、以下のようなボイラープレートコードが必要であった。

var entityManager = factory.createEntityManager();
var transaction = entityManager.getTransaction();
try {
    transaction.begin();
    // do some work...
    transaction.commit();
}
catch (Exception e) {
    if (transaction.isActive()) {
        try {
            transaction.rollback();
        }
        catch (Exception x) {
            e.addSuppressed(x);
        }
   }
   throw e; 
}
finally {
    entityManager.close();
} 

EntityManagerFactory に以下のメソッドが追加され、

public interface EntityManagerFactory extends AutoCloseable {
  public void runInTransaction(Consumer<EntityManager> work);
  public <R> R callInTransaction(Function<EntityManager,R> work);
}

以下のようにトランザクション操作が可能となった。

entityManagerFactory.runInTransaction(entityManager -> {
    User user = em.createQuery
        ("SELECT u FROM User u WHERE u.name=:name AND u.pass=:pass", User.class)
        .setParameter("name", args[0])
        .setParameter("pass", args[1])
        .getSingleResult();
    user.setPassword(args[2]);
})


JDBC 操作手段の提供

#432 easy access to the JDBC connection associated with the EM #483 javax.sql dependence

JPA の transaction/connection に関連付けてJDBCコードを実行する手段がなかったため、EntityManager にアクセス手段を追加。

public interface EntityManager extends AutoCloseable {
  public <C> void runWithConnection(ConnectionConsumer<C> action);
  public <C, T> T callWithConnection(ConnectionFunction<C, T> function);
}

以下のインターフェースを経由してコネクションに対する操作が可能となる。

public interface ConnectionConsumer<C> {
    void accept(C connection) throws Exception;
}
public interface ConnectionFunction<C, T> {
    T apply(C connection) throws Exception;
}


EntityManagerFactory から Persistence Unit 名を取得可能に

#530 Add accessor for PU name to EntityManagerFactory

以下のメソッドが追加され、Persistence Unit 名を取得できるようになった。

public interface EntityManagerFactory extends AutoCloseable {
    String getName();
}

これは、主にフレームワーク開発者やテスト、またはロギングでの使用を想定したもの。


@Convert アノテーションの raw types を改善

#56 @Convert annotation's converter property should be ...

以下の rawタイプを修正

@Repeatable(Converts.class)
@Target({METHOD, FIELD, TYPE}) @Retention(RUNTIME)
public @interface Convert {
    Class converter() default void.class;
}
Class<? extends AttributeConverter> converter() default AttributeConverter.class;

この変更に合わせて、その他多くの rawタイプ使用箇所が修正された。


CriteriaQuery.subquery(EntityType) の追加

#71 Add subquery(EntityType) to javax.persistence.criteria.CriteriaQuery

EntityMode.MAP などを使った動的なエンティティに対するサブクエリを作成する方法が無かったため、以下のメソッドが追加された。

public interface CommonAbstractCriteria {
    <U> Subquery<U> subquery(EntityType<U> type);
}


生成IDを @PrePersist 時に参照可能とする

#133 Make TABLE-generated IDs available on PrePersist

生成されたID値を @PrePersist メソッド内で利用可能となった(EclipseLinkでは従来より可能)。

利用可能なIDは、GenerationType.SEQUENCE GenerationType.TABLE GenerationType.UUID で定義されたケースであり、GenerationType.IDENTITY の場合は対象外。

これにより、以下のように生成されたID値を利用できる。

public class MyEntity {

    @PrePersist
    private void onPrePersist() {
        // generate a unique and unchangable value from *available* id.
        derived = String.format("%1$016x", id);
    }

    @GeneratedValue(..., strategy = GenerationType.TABLE)
    @Id
    private Long id;

    @Basic(optional = false)
    @Colum(..., nullable = false, updatable = false)
    @NotNull
    private String derived;
}


Query.getSingleResultOrNull() の追加

#298 Add Query.getOptionalResult() or Query.getSingleResultOrNull()

Query.getSingleResult() は結果が見つからない場合に NoResultException を返すため、クライアントは、フロー制御のために例外処理の使用が強制されていたため、null を返すメソッドが追加された。

public interface Query {
  Object getSingleResultOrNull();
}

TypedQueryStoredProcedureQuery についても同様に追加。

なお、null ではなく Optional を返すべきかどうかは # Provide API returning Optional as an alternative to methods allowed to return null で議論が継続中。


@Table や @Column アノテーションでチェック制約とコメントが定義可能となった

#381 add comment and check to @Table and @Column #382 add check + comment to @Table + @Column

以下の属性が追加され、DDL用にチェック制約とコメントが定義可能となった。

@Target({METHOD, FIELD}) 
@Retention(RUNTIME)
public @interface Column {
    CheckConstraint[] check() default {};
    String comment() default "";
}

@Column @JoinColumn @JoinTable @SecondaryTable @Table に対して追加された。


Entity/Embeddable classes/primary key classes の制限改定

#380 revisit restrictions on entity, embeddable, and primary key classes

  • @Entityクラス
    • static inner クラスを許容
  • @Embeddableクラス
    • static inner クラスを許容
  • primary key クラス
    • public であり serializable である必要が無くなった
    • Java レコード可


エンティティローカルな@SequenceGenerator/@TableGenerator

#406 "entity local" @SequenceGenerator/@TableGenerator #511 fallback id generator

ID値を生成する際の @SequenceGenerator@TableGenerator アノテーションは name 属性が必須であり、この名前は永続化ユニット全体に対してグローバルであった。

@Entityクラスや @Id に対して直接個別に @SequenceGenerator@TableGenerator を指定することを考慮し、name 属性が任意化された。name 属性を省略した場合、IDジェネレータは対象のエンティティに対してローカルで定義されたものとして扱われるようになった。

これに関連し、@SequenceGenerator などはパッケージに対して付与可能()となり、その場合、パッケージローカルなジェネレータを定義したものとして扱われる。


@EnumeratedValueによるenum値のカスタムマッピング

#47 custom value mappings for @Enumerated

@EnumeratedValue アノテーションにてenumのマッピング値をカスタマイズ可能となった

enum Status {
    OPEN(0), CLOSED(1), CANCELLED(-1);
    @EnumeratedValue
    final int intValue;
    Status(int intValue) {
        this.intValue = intValue;
    }
}

@EnumeratedValue はマーカーアノテーションで、付与したフィールド値がマッピングに利用される。


@Version プロパティのサポート型の追加

#81 @Version Support for Temporal Types

@Version プロパティとして使用可能な型に LocalDateTimeInstant が追加された。

int, Integer, short, Short, long, Long, LocalDateTime, Instant, Timestamp


java.time.Instant と java.time.Year のマッピングサポート追加

#163 Support for java.time.Instant

java.time.Instantjava.time.Year を JDBC基本マッピングへ追加


equalTo() / notEqualTo() 式の追加

#438 add Expression.equalTo() and Expression.notEqualTo()

以下が追加された。

public interface Expression<T> extends Selection<T> {
    Predicate equalTo(Expression<?> value);
    Predicate equalTo(Object value);
    Predicate notEqualTo(Expression<?> value);
    Predicate notEqualTo(Object value);
}


PersistenceUnitUtil への初期化メソッド追加

#104 Enhance PersistenceUtil to allow initialization

以下のメソッド(initialize and unproxy)が追加された

public interface PersistenceUnitUtil extends PersistenceUtil {
  public <E> boolean isLoaded(E entity, Attribute<? super E,?> attribute);
  public void load(Object entity, String attributeName);
  public <E> void load(E entity, Attribute<? super E,?> attribute);
  public void load(Object entity);
  public boolean isInstance(Object entity, Class<?> entityClass);
  public <T> Class<? extends T> getClass(T entity);
}


エンティティ名から EntityType を取得可能に

#85 Add methods in Metamodel interface to get entity by jpa name

Metamodel に以下のメソッドが追加され、エンティティ名から EntityType を取得可能となった(を取得し、MetamodelEntityManager から取得可能)。

public interface Metamodel {
  EntityType<?> entity(String entityName);
}

以下の EntityType を、名前 car にて取得することができる。

@Entity(name = "car")
public class Car { ... }


エンティティのバージョン値取得

#74 Obtaining @Version value

JAX-RSやサーブレットは任意のエンティティに対してETagヘッダをクライアントに送信したいが、サーブレットはそのエンティティのクラスを知らない(クラス名と主キー値がHTTPリクエストによって提供される場合のように)。このようなケースで利用するバージョン取得メソッドが PersistenceUnitUtil に追加された。

public interface PersistenceUnitUtil extends PersistenceUtil {
  public Object getVersion(Object entity);
}


可変長の concat() 追加

#408 concat() is a variadic function, but declared binary by CriteriaBuilder

可変長引数を受ける文字列連結 concat() が追加された

public interface CriteriaBuilder {
   Expression<String> concat(List<Expression<String>> expressions);
}


@NamedNativeQuery の結果セット定義

#471 allow nested result set mappings in @NamedNativeQuery

@NamedNativeQuery でクエリを定義する場合、結果セットのマッピングは、@SqlResultSetMapping にて別途定義する必要があり、かなり煩わしいため、@NamedNatiiveQuery に直接マッピングを定義可能となった。

@NamedNativeQuery(name = “someQuery",
    query = "select TABLE_NAME as t_name, CURRENT_TIME as t_time from ALL_TABLES where TABLE_NAME = 'AUDIT_ACTIONS'“,
    entities = @EntityResult(
            entityClass = AllTables.class,
            fields = {
                @FieldResult(name = "tableName", column = "t_name"),
                @FieldResult(name = "daysOld", column = "t_time")
            }))

追加された属性は以下。

@Repeatable(NamedNativeQueries.class)
@Target({TYPE}) 
@Retention(RUNTIME)
public @interface NamedNativeQuery {
  Class<?> resultClass() default void.class;
  EntityResult[] entities() default {};
  ConstructorResult[] classes() default {};
  ColumnResult[] columns() default {};
}
@Target({}) 
@Retention(RUNTIME)
public @interface EntityResult { 
    Class<?> entityClass();
}


各種プロパティの定数化

#139 Provide constants for properties

各種のプロパティを PersistenceConfiguration に定数として定義。

public class PersistenceConfiguration {
    /** Fully qualified name of the JDBC driver class. */
    public static final String JDBC_DRIVER = "jakarta.persistence.jdbc.driver";
    /** JDBC URL. */
    public static final String JDBC_URL = "jakarta.persistence.jdbc.url";
    ...


述語のリストを引数に取るオーバーロードを追加

#137 API improvements - pass List to where

可変長引数を取るメソッドに加え、以下のメソッドが追加された。

CriteriaBuilder.and(List<Predicate> restrictions);
CriteriaBuilder.or(List<Predicate> restrictions);

CriteriaQuery.where(List<Predicate> restrictions);
CriteriaQuery.having(List<Predicate> restrictions);


数値リテラル型の明確化

#423 nail down semantics of arithmetic expressions and numeric literals

select 1.23 from Entity のような結果のリテラル型を明確化。 L(long)、D(double)、F(float) に加え、BI(BigInteger) 、BD(BigDecimal) を接尾辞に付けることでリテラルの型を指定可能となった。


JPQL演算/関数の追加


その他の細かな変更