Jakarta Persistence 4.0 - ステートレス・エンティティ・マネージャー -


ステートレス・エンティティ・マネージャー

Jakarta Persistence 4.0 では、いわゆる StatelessSession を提供する API が追加された。

StatelessSession は、永続コンテキストを持たず、エンティティは常にデタッチされた状態として扱われる。 永続コンテキストを持たないため、dirty checking や lazy loading、cascading、first-level cache などの機能は利用できないが、直接的なデータ操作が可能となる。

Issue は以下

プルリクエストは以下


EntityAgent

従来の EntityManager に代わり、ステートレスセッションを利用する EntityAgent が追加された(EntityAgent という名前には不満が多い)。

EntityAgent は以下のインターフェースとして定義される。

public interface EntityAgent extends EntityHandler {
    void insert(Object entity);
    void insertMultiple(List<?> entities);
    void update(Object entity);
    void updateMultiple(List<?> entities);
    void delete(Object entity);
    void deleteMultiple(List<?> entities);
    void upsert(Object entity);
    void upsertMultiple(List<?> entities);
    void refresh(Object entity);
    void refreshMultiple(List<?> entities);
    void refresh(Object entity, LockModeType lockMode);
    <T> T fetch(T association);
}

EntityHandler は、EntityManager との共通部分を抜き出したもので、EntityManagerEntityHandler を継承するように変更された。

public interface EntityManager extends EntityHandler {
    // ...
}

EntityHandler は、以下のようなメソッドを定義する。

public interface EntityHandler extends AutoCloseable {
    <T> T get(Class<T> entityClass, Object id);
    <T> T get(Class<T> entityClass, Object id, FindOption... options);
    <T> T get(EntityGraph<T> graph, Object id, FindOption... options);
    <T> List<T> getMultiple(Class<T> entityClass, List<?> ids, FindOption... options);
    <T> List<T> getMultiple(EntityGraph<T> graph, List<?> ids, FindOption... options);
    <T> T find(Class<T> entityClass, Object id);
    <T> T find(Class<T> entityClass, Object id, FindOption... options);
    <T> T find(EntityGraph<T> graph, Object id, FindOption... options);
    <T> List<T> findMultiple(Class<T> entityClass, List<?> ids, FindOption... options);
    <T> List<T> findMultiple(EntityGraph<T> graph, List<?> ids, FindOption... options);
    // ...
}

findMultiple()add EntityManager.findMultiple() で追加された、複数のエンティティを ID でマルチロードロードする機能。


ステートレス・エンティティ・マネージャーのコールバック用に以下のアノテーション・インターフェースが追加された。

  • @PostDelete
  • @PostInsert
  • @PostUpsert
  • @PreDelete
  • @PreInsert
  • @PreUpsert


EntityAgent の利用

EE 環境下では、以下のように EntityAgent をインジェクトして利用できるハズ。

@PersistenceContext(unitName = "puName")
private EntityAgent ea;

SE環境では、EntityManagerFactory に追加された以下のメソッドを利用することになる。

EntityAgent createEntityAgent();
<H extends EntityHandler> void runInTransaction(Class<H> handlerClass, Consumer<H> work);
<R, H extends EntityHandler> R callInTransaction(Class<H> handlerClass, Function<EntityAgent,R> work);

例えば以下のように。

var book = factory.callInTransaction(EntityAgent.class, agent -> {
    return agent.get(Book.class, isbn);
});

EntityAgent では、エンティティは即座にデタッチされた状態となるため、lazy loading で関連を取得することはできない。

よって以下のような明示的なフェッチが必要となる。

Book book = agent.get(Book.class, isbn);
agent.fetch(book.getAuthors());
book.getAuthors().forEach(author -> ... );

dirty checking なども機能しないため、変更の反映は、明示的に upsert() して永続化する必要がある。