0

场景:我遇到了一些在事务中混合 JPA 和 JDBC 的代码。JDBC 正在插入一个基本上是空白行的表,将主键设置(SELECT MAX(PK) + 1)middleName临时时间戳。然后该方法从同一个表中选择max(PK)+那个临时时间戳来检查是否存在冲突。如果成功,它会取消middleName和更新。该方法返回新创建的主键。

问题: 有没有更好的方法将实体插入数据库,将 PK 设置为max(pk) + 1并获得对新创建的 PK 的访问权限(最好使用 JPA)?

环境: 使用 EclipseLink,需要同时支持多个版本的 Oracle 和 MS SqlServer 数据库。

奖励背景:我问这个问题的原因是因为我java.sql.BatchUpdateException在运行集成测试时将此方法作为链的一部分调用时遇到了。链的上半部分使用 JPAEntityManager来持久化一些对象。

有问题的方法

@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;
}
4

2 回答 2

3

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

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

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

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

如果你坚持你的解决方案:关闭ResultSet,StatementConnection. 抱歉,最初没有抓住 try-with-resources。

于 2013-04-06T20:02:12.710 回答
0

JDBC 代码是病态的,没有任何意义,并且无法在多用户环境中工作。

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

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

见, http ://en.wikibooks.org/wiki/Java_Persistence/Identity_and_Sequencing#Sequencing

如果您真的想进行自己的排序,您可以自己分配 Id,使用 PrePersist 分配您自己的 id,或者在 EclipseLink 中实现您自己的 Sequence 子类来满足您的任何需求。您将需要使用 SessionCustomizer 注册此序列对象。

见, http://wiki.eclipse.org/EclipseLink/Examples/JPA/CustomSequencing

于 2013-05-14T13:06:50.383 回答