java - JPA:如何插入将 PK 设置为 MAX(PK) + 1

标签 java jpa jdbc insert

场景:我遇到了一些在事务中将 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;
}

最佳答案

首先:回答这个问题很痛苦。但我知道,有时你必须与魔鬼打交道:(

所以从技术上讲,它不是 JPA,但如果您使用 Hibernate 作为 JPA-Provider,则可以使用

@org.hibernate.annotations.GenericGenerator(
    name = “incrementGenerator”,
    strategy = “org.hibernate.id.IncrementGenerator”)
@GeneratedValue(generator="incrementGenerator")
private Long primaryKey;

Hibernate 解决方案是“线程安全”的,但不是“集群安全”的,即如果您在多个主机上运行应用程序,则可能会失败。您可以捕获适当的异常并重试。

如果您坚持您的解决方案:关闭 ResultSetStatementConnection 抱歉,最初没有捕获 try-with-resources。

关于java - JPA:如何插入将 PK 设置为 MAX(PK) + 1,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15852714/

相关文章:

java utillogging在属性文件中配置过滤器

spring - 在 Spring JPA 中实现查询

java - JPA 2.0/Hibernate InheritanceType TABLE_PER_CLASS 和 OneToMany/ManyToOne 双向关系

java - 如何通过不对数据库进行 N 次调用来获取实体集合中的惰性属性

java - 使用 JOptionPane 更新 mysql

java - Spring TransactionManager - 提交不起作用

java - 更新查询的 SQL 语法错误

java - 复制构造函数和多态性

java - 当我在 Linux 中启动 Spring Boot 应用程序作为系统服务时,日志文件没有生成?

java - 如何使用 JPA 和 Hibernate 更新前 N 行