JPA:如何将PK设置为MAX(PK)+ 1

时间:2013-04-06 15:31:40

标签: 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;
}

2 个答案:

答案 0 :(得分:2)

首先:回答这个很痛苦。但我知道,有时你必须对付魔鬼:(

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

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

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

如果你坚持你的解决方案:关闭ResultSetStatementConnection 抱歉,没有抓住尝试 - 最初使用资源。

答案 1 :(得分:0)

JDBC代码是病态的,毫无意义,并且在多用户环境中不起作用。

我强烈建议修复代码以使用序列对象或序列表。

在JPA中,您可以使用排序。

请参阅, http://en.wikibooks.org/wiki/Java_Persistence/Identity_and_Sequencing#Sequencing

如果你真的想做自己的排序,你可以自己分配Id,使用PrePersist分配你自己的id,或者在EclipseLink中实现你自己的Sequence子类,它可以做你想做的任何事情。您需要使用SessionCustomizer注册此Sequence对象。

请参阅, http://wiki.eclipse.org/EclipseLink/Examples/JPA/CustomSequencing