
ステートレス・エンティティ・マネージャー
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 との共通部分を抜き出したもので、EntityManager も EntityHandler を継承するように変更された。
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() して永続化する必要がある。