21.

Spring + JPA で EntityManager を取得する方法

編集
この記事の要点
  • Spring + JPA で EntityManager を取得する方法
  • 推奨: @PersistenceContext private EntityManager em; でフィールド注入
  • 代替: @PersistenceUnit EntityManagerFactory emf; から emf.createEntityManager()
  • Spring Data JPA のリポジトリだけで完結する場合は EntityManager を直接触る必要はない
  • 動的クエリ・JPQL の柔軟な実行・ストアドプロシージャ呼び出しのときに EntityManager を使う

EntityManager とは

JPA の EntityManager はエンティティの永続化・検索・更新・削除を司る中核 API です。Spring Data JPA はこの EntityManager を内部で利用してリポジトリメソッドを実装しています。普段はリポジトリ経由で済みますが、複雑なクエリやネイティブ SQL を使う際に直接触ることがあります。

方式1: @PersistenceContext (推奨)

Spring 管理のコンポーネントで使う場合、これがもっとも一般的:

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

@Repository
public class UserRepositoryImpl implements UserCustomRepository {

    @PersistenceContext
    private EntityManager em;

    public List findActiveUsersByDate(LocalDate date) {
        // JPQL の動的構築
        StringBuilder jpql = new StringBuilder("SELECT u FROM User u WHERE u.active = true");
        if (date != null) {
            jpql.append(" AND u.lastLoginAt >= :date");
        }

        TypedQuery query = em.createQuery(jpql.toString(), User.class);
        if (date != null) {
            query.setParameter("date", date);
        }
        return query.getResultList();
    }
}

方式2: EntityManagerFactory から個別取得

トランザクション境界を手動で制御したい / 非 Spring 管理のコンテキストで使う場合:

@Component
public class BatchService {
    @PersistenceUnit
    private EntityManagerFactory emf;

    public void processBatch() {
        EntityManager em = emf.createEntityManager();
        EntityTransaction tx = em.getTransaction();
        try {
            tx.begin();
            // ... 処理
            tx.commit();
        } catch (Exception e) {
            tx.rollback();
            throw e;
        } finally {
            em.close();  // ★ 必ず close
        }
    }
}

方式3: Spring Data JPA Custom Repository

Spring Data JPA で「規約メソッドだけでは無理な処理」を追加する標準パターン:

// ① Custom インターフェイス
public interface UserCustomRepository {
    List findByComplexCondition(UserSearchDto dto);
}

// ② 実装クラス(命名規則: 「リポジトリ名」+ "Impl")
@Repository
public class UserCustomRepositoryImpl implements UserCustomRepository {
    @PersistenceContext
    private EntityManager em;

    @Override
    public List findByComplexCondition(UserSearchDto dto) {
        // CriteriaBuilder で型安全なクエリ
        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery cq = cb.createQuery(User.class);
        Root root = cq.from(User.class);
        List predicates = new ArrayList<>();

        if (dto.getName() != null) {
            predicates.add(cb.like(root.get("name"), "%" + dto.getName() + "%"));
        }
        if (dto.getMinAge() != null) {
            predicates.add(cb.ge(root.get("age"), dto.getMinAge()));
        }

        cq.where(cb.and(predicates.toArray(new Predicate[0])));
        return em.createQuery(cq).getResultList();
    }
}

// ③ JpaRepository と一緒に extend する
public interface UserRepository extends JpaRepository, UserCustomRepository {
    // 標準のリポジトリメソッドと、カスタムメソッド両方が使える
}

EntityManager の主要メソッド

メソッド用途
persist(entity)新規エンティティを永続化(INSERT)
merge(entity)デタッチ状態のエンティティを更新(UPDATE)
remove(entity)削除(DELETE)
find(Class, id)主キーで検索
getReference(Class, id)遅延ロード proxy を取得(実 SQL は属性アクセス時)
flush()永続化コンテキストの変更を DB に反映
clear()永続化コンテキストを空に
refresh(entity)DB から再取得して上書き
createQuery(jpql)JPQL クエリ作成
createNativeQuery(sql)ネイティブ SQL クエリ作成
createStoredProcedureQuery(name)ストアドプロシージャ呼び出し

ネイティブ SQL の実行

// 結果を Map で取得
List> rows = em
    .createNativeQuery("SELECT user_id, name, age FROM users WHERE age > ?")
    .setParameter(1, 18)
    .unwrap(NativeQueryImpl.class)
    .setResultTransformer(AliasToEntityMapResultTransformer.INSTANCE)
    .getResultList();

// 結果を Entity にマップ
List users = em
    .createNativeQuery("SELECT * FROM users WHERE age > ?", User.class)
    .setParameter(1, 18)
    .getResultList();

// スカラー値(1 列だけ)
Long count = ((Number) em
    .createNativeQuery("SELECT COUNT(*) FROM users")
    .getSingleResult())
    .longValue();

flush と commit の違い

  • flush(): 永続化コンテキストの変更を DB に送信(INSERT/UPDATE 文の実行)。まだコミットされていない
  • commit(): トランザクションを確定(DB レベルで永続化)
  • Spring の @Transactional はメソッド終了時に自動的に flush + commit
  • 長い処理の途中で DB へ反映確認したいときに em.flush() を明示

パフォーマンス Tip: バッチ INSERT

@Transactional
public void bulkInsert(List users) {
    for (int i = 0; i < users.size(); i++) {
        em.persist(users.get(i));
        if (i % 50 == 0) {  // 50 件ごとに flush + clear
            em.flush();
            em.clear();  // ★ 永続化コンテキストをクリアしてメモリ節約
        }
    }
}

// application.properties で JDBC バッチサイズも設定
// spring.jpa.properties.hibernate.jdbc.batch_size=50
// spring.jpa.properties.hibernate.order_inserts=true
// spring.jpa.properties.hibernate.order_updates=true

関連

  • @Transactional の必要性: EntityManager で更新系を実行する場合、Service 層または同メソッドに @Transactional 必須
  • QueryDSL / Criteria API: 型安全な動的クエリ構築には QueryDSL を導入する選択肢も
  • JPA Specifications: Spring Data JPA 標準の動的クエリ構築機構(CriteriaBuilder ベース)
  • テスト: @DataJpaTest で EntityManager 込みのテストが可能
編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. インストール(eclipseプラグイン)
  2. クイックスタート
  3. プロジェクトの作成
  4. Spring Bootプロジェクトの作成
  5. Spring Bootプロジェクトの実行
  6. Spring BootでHello World!
  7. アノテーション一覧
  8. DB接続設定からエンティティおよびリポジトリの作成、値の取得まで(JPA編)
  9. DB接続設定や値の取得(JdbcTemplate編)
  10. ビューから値をモデルに格納しコントローラーで受け取る方法
  11. コントローラーにてモデルに値を格納してビューに渡す方法
  12. テンプレートエンジン
  13. ModelとModelAndViewの違い
  14. AOPの使用方法
  15. classpath: 内部ファイルの読み込み
  16. file: 外部ファイルの読み込み
  17. CSVファイルアップロード方法(Ajax)
  18. CSVファイルダウンロード方法(Ajax)
  19. Spring Bootプロジェクトのビルドと本番環境へのデプロイ方法(内部tomcat使用)
  20. Application.propertiesの環境依存設定の分割方法
  21. JPAにおけるEntityManagerの取得方法
  22. JPAにおけるjava.sql.Connectionの取得方法
  23. エラー一覧
  24. jarの引数を受け取る方法
  25. Spring BootでGmailからメール送信
  26. 複数のDBに接続する設定(Spring Boot & JPA編)
  27. ポート番号の変更
  28. Basic認証の実装と特定のURLに限定する方法
  29. Spring SecurityのBasic認証の無効化
  30. 独自のエラーページを定義する方法
  31. プロパティファイルの値やjar実行時の引数を取得する方法