タイトル: JPAにおけるEntityManagerの取得方法
SEOタイトル: Spring + JPA で 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
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 込みのテストが可能