22.

【Spring】JPAにおけるjava.sql.Connectionの取得方法

編集
この記事の要点
  • JPA / Spring Data JPA で低レベルの java.sql.Connection を取得する 4 つの方法
  • EntityManager.unwrap(Connection.class): JPA 経由(推奨)
  • DataSource.getConnection(): DataSource から直接
  • JdbcTemplate.execute(ConnectionCallback): Spring 標準
  • トランザクション中なら同じ Connectionが返る

 

取得が必要なケース

通常は JPA / Spring Data でクエリは十分。低レベル Connection が欲しいのは:

  • JPA で対応できないベンダ固有 SQLを実行
  • 大量 INSERTでバッチ処理
  • CallableStatementでストアドプロシージャ
  • BLOB / CLOB のストリーム書き込み
  • カーソル制御を細かく行う

方法 1: EntityManager から取得(JPA 経由、推奨)

@Service
public class MyService {

    @PersistenceContext
    private EntityManager em;

    @Transactional
    public void doSomething() throws SQLException {
        // JPA トランザクション内の Connection を取得
        Connection conn = em.unwrap(Connection.class);

        try (PreparedStatement ps = conn.prepareStatement("SELECT 1")) {
            ResultSet rs = ps.executeQuery();
            // ...
        }
        // conn.close() しない! JPA が管理する
    }
}

# 利点:
# - 同じトランザクション内で動作 (commit/rollback 連携)
# - JPA キャッシュとも整合性が保たれる

方法 2: DataSource から取得

@Service
public class MyService {

    @Autowired
    private DataSource dataSource;

    public void doSomething() throws SQLException {
        try (Connection conn = dataSource.getConnection()) {
            // 独立した接続 (JPA トランザクションとは別)
            try (PreparedStatement ps = conn.prepareStatement("SELECT 1")) {
                ResultSet rs = ps.executeQuery();
                // ...
            }
        }
    }
}

# 注意:
# - JPA トランザクションと別の接続なので、JPA 側の未 commit データは見えない
# - 自分で close 必須
# - try-with-resources 推奨

方法 3: トランザクション内で同じ Connection を取得

@Service
public class MyService {

    @Autowired
    private DataSource dataSource;

    @Transactional
    public void doSomething() throws SQLException {
        // トランザクション中なら JPA と同じ Connection が返る
        Connection conn = DataSourceUtils.getConnection(dataSource);

        try (PreparedStatement ps = conn.prepareStatement("SELECT 1")) {
            ResultSet rs = ps.executeQuery();
            // ...
        }

        // 必ず DataSourceUtils.releaseConnection() で解放
        DataSourceUtils.releaseConnection(conn, dataSource);
    }
}

# DataSourceUtils 経由なら:
# - トランザクション参加 (commit/rollback 共有)
# - close は releaseConnection で行う

方法 4: JdbcTemplate.execute(ConnectionCallback)

@Service
public class MyService {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public void doSomething() {
        jdbcTemplate.execute((ConnectionCallback) conn -> {
            // この conn は JdbcTemplate が管理
            try (PreparedStatement ps = conn.prepareStatement("SELECT 1")) {
                ResultSet rs = ps.executeQuery();
                // ...
            }
            return null;
        });
    }
}

# 利点:
# - リソース管理が自動 (close 不要)
# - JdbcTemplate の例外変換も効く
# - トランザクション統合

典型的な利用例

例 1: 大量 INSERT バッチ

@Service
public class BatchService {

    @PersistenceContext
    private EntityManager em;

    @Transactional
    public void bulkInsert(List users) throws SQLException {
        Connection conn = em.unwrap(Connection.class);

        String sql = "INSERT INTO users (name, email) VALUES (?, ?)";
        try (PreparedStatement ps = conn.prepareStatement(sql)) {
            int count = 0;
            for (UserData user : users) {
                ps.setString(1, user.getName());
                ps.setString(2, user.getEmail());
                ps.addBatch();

                if (++count % 1000 == 0) {
                    ps.executeBatch();
                    ps.clearBatch();
                }
            }
            ps.executeBatch();
        }
    }
}

# JPA の persist() より圧倒的に高速 (10-50x)

例 2: ストアドプロシージャ呼び出し

@Transactional
public ProcedureResult callProcedure() throws SQLException {
    Connection conn = em.unwrap(Connection.class);

    try (CallableStatement cs = conn.prepareCall("{call my_procedure(?, ?)}")) {
        cs.setLong(1, 123L);
        cs.registerOutParameter(2, Types.VARCHAR);
        cs.execute();
        return new ProcedureResult(cs.getString(2));
    }
}

// または JPA の StoredProcedureQuery
StoredProcedureQuery query = em.createStoredProcedureQuery("my_procedure")
    .registerStoredProcedureParameter(1, Long.class, ParameterMode.IN)
    .registerStoredProcedureParameter(2, String.class, ParameterMode.OUT)
    .setParameter(1, 123L);
query.execute();
String result = (String) query.getOutputParameterValue(2);

例 3: 大きな BLOB のストリーミング書き込み

@Transactional
public void uploadFile(MultipartFile file) throws SQLException, IOException {
    Connection conn = em.unwrap(Connection.class);

    try (PreparedStatement ps = conn.prepareStatement(
            "INSERT INTO files (name, content) VALUES (?, ?)")) {
        ps.setString(1, file.getOriginalFilename());
        ps.setBinaryStream(2, file.getInputStream(), file.getSize());
        ps.executeUpdate();
    }
}

トランザクション管理の理解

@Transactional
public void txAware() throws SQLException {
    Connection conn1 = em.unwrap(Connection.class);
    Connection conn2 = DataSourceUtils.getConnection(dataSource);

    // → conn1 と conn2 は同じインスタンス (トランザクション中)

    // PROPAGATION_REQUIRES_NEW で別トランザクション
    anotherService.doInNewTransaction();
    // → 別の Connection
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void doInNewTransaction() {
    Connection conn = em.unwrap(Connection.class);
    // 親と別の接続・トランザクション
}

注意点

  • close() しない: JPA / Spring 管理の Connection は自動 close
  • DataSource.getConnection() は close 必須: try-with-resources で確実に
  • auto-commit に注意: トランザクション中は false(手動 commit 必要なし、Spring が制御)
  • JPA の 1 次キャッシュとの整合性: SQL 直接実行はキャッシュをバイパス → flush() / refresh() で同期

関連記事

編集
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実行時の引数を取得する方法