シナリオ:トランザクション内で JPA と JDBC を混合しているコードを見つけました。JDBC は、基本的に空白行を含むテーブルに INSERT を実行し、主キー(SELECT MAX(PK) + 1)
とmiddleName
一時タイムスタンプを設定します。メソッドは、同じテーブルからmax(PK)
+その一時タイムスタンプを選択して、衝突があったかどうかを確認します。成功した場合はmiddleName
、更新を無効にします。このメソッドは、新しく作成された主キーを返します。
質問:
データベースにエンティティを挿入し、PK を設定max(pk) + 1
して新しく作成された PK にアクセスする (できれば JPA を使用する) より良い方法はありますか?
環境: EclipseLink を使用しており、Oracle データベースと MS SqlServer データベースの両方のいくつかのバージョンをサポートする必要があります。
おまけの背景: 私がこの質問をしている理由はjava.sql.BatchUpdateException
、統合テストを実行しているときにチェーンの一部としてこのメソッドを呼び出すときに、に遭遇したためです。チェーンの上部では、JPA を使用EntityManager
して一部のオブジェクトを永続化します。
問題のメソッド
@Override
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public int generateStudentIdKey() {
final long now = System.currentTimeMillis();
int id = 0;
try {
try (final Connection connection = dataSource.getConnection()) {
if (connection.getAutoCommit()) {
connection.setAutoCommit(false);
}
try (final Statement statement = connection.createStatement()) {
// insert a row into the generator table
statement.executeUpdate(
"insert into student_demo (student_id, middle_name) " +
"select (max(student_id) + 1) as student_id, '" + now +
"' as middle_name from student_demo");
try (final ResultSet rs = statement.executeQuery(
"select max(student_id) as student_id " +
"from student_demo where middle_name = '" + now + "'")) {
if (rs.next()) {
id = rs.getInt(1);
}
}
if (id == 0) {
connection.rollback();
throw new RuntimeException("Key was not generated");
}
statement.execute("update student_demo set middle_name = null " +
"where student_id = " + id);
} catch (SQLException statementException) {
connection.rollback();
throw statementException;
}
}
} catch (SQLException exception) {
throw new RuntimeException(
"Exception thrown while trying to generate new student_ID", exception);
}
return id;
}