Сценарий: Я столкнулся с некоторым кодом, который смешивает JPA с JDBC в транзакции. JDBC делает INSERT в таблицу с в основном пустой строкой, устанавливая основной ключ на (SELECT MAX(PK) + 1)
и middleName
на временную метку темпа. Затем метод выбирает из той же таблицы для max(PK)
+ эту временную метку времени, чтобы проверить, произошло ли столкновение. В случае успеха он отменяет middleName
и обновляет. Метод возвращает вновь созданный первичный ключ.JPA: Как установить INSERT PK в MAX (PK) + 1
Вопрос: Есть ли лучший способ вставить объект в базу данных, установив ПК в max(pk) + 1
и получить доступ к этому вновь созданным ПК (желательно с использованием JPA)?
Окружающая среда: Использование EclipseLink и поддержка нескольких версий баз данных Oracle и MS SqlServer.
Bonus фон: Причина, почему я задаю этот вопрос, потому что я бег в 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;
}
Не делайте этого. Это ужасная идея.Идентификатор не работает должным образом в многопользовательской среде и не масштабируется. Вместо этого используйте последовательность (или любой другой «генератор», поддерживаемый вашей СУБД). JPA может быть настроен на использование средств базы данных для генерации уникальных идентификаторов. –
Я * полностью * согласен - но в некоторых случаях наши руки связаны. – Snekse
Не могли бы вы рассказать нам, что вы не будете/не можете принять в качестве ответа? Я имею в виду, почему вы не можете использовать последовательность? – skirsch