java - 无主键和一般用途的 JPA 数字生成器

标签 java jpa tomcat7 jpa-2.0 openjpa

环境:OpenJPA2.x、Tomcat webapp、Mariadb,但可能会更改。这不是 Hibernate 或 Spring Web 应用程序。

我已经阅读了一些主题,例如: Hibernate JPA Sequence (non-Id)

我有一些带有 someNumber 非主键字段的实体类,有些具有 someNumbersomeNumberB 双列。字段有约束 UNIQUE(someNumber) 和 UNIQUE(someNumberB),主键组合是 PRIMARY(server_id, code)。在提交行插入之前我需要一个数值。

如果我理解其他主题,我就无法使用 JPA @generator 标签。我被迫实现一种老式的实用方法。这是我所做的方法,它需要一个新的数据库连接,因此它始终在单独的事务中运行。

public synchronized static long getGeneratorValue(String genName, int incStep) {
    Connection conn = null;
    Statement stmt = null;
    ResultSet rs = null;
    try {
        if (genName==null) genName="app";
        // try few times before give up, another node may have updated a value in-between. Create a new transaction from db connection pool.
        for(int idx=0; idx<3; idx++) {
            conn = createConnection(false); // autocommit=false
            stmt = conn.createStatement();              
            rs = stmt.executeQuery(String.format("Select value From generators Where name='%s'", genName));
            if (!rs.next()) throw new IllegalArgumentException("Invalid generator name " + genName);

            if (incStep==0)
                return rs.getLong("value"); // return an existing value

            long oldValue = rs.getLong("value");
            long newValue = oldValue+incStep;
            int rowCount = stmt.executeUpdate(String.format("Update generators Set value=%d Where name='%s' and value=%d", newValue, genName, oldValue));
            if (rowCount>0) {
                conn.commit();
                return newValue;                    
            }
            close(rs, stmt, conn);
            conn=null;
        }
        throw new IllegalArgumentException("Obtaining a generator value failed " + genName);
    } catch(Exception ex) {
        try { conn.rollback(); } catch(Exception e){}
        if (ex instanceof IllegalArgumentException) throw (IllegalArgumentException)ex;
        else throw new IllegalArgumentException(ex.getMessage(), ex);
    } finally {
        if (conn!=null) close(rs, stmt, conn);
    }
}

我对此并不完全满意,尤其是针对另一个 Tomcat 节点同时更新生成器值的故障安全 foreach_loop。此循环可能会在繁忙的工作负载上失败。

我可以使用数据库自​​动增量列作为通用数字生成器吗,我想它可以容忍更好的并发性?如果这将数据库锁定到 MariaDB、MySQL 或类似的数据库,我现在可以接受。

值必须是用于遗留目的的数字字段,我无法使用 GUID 字符串值。

最佳答案

我正在考虑使用 DB auto_increment 列并想出了这个实用函数。我可能会采用这种实现方式,或者 StackOverflow 社区有更好的技巧吗?

CREATE TABLE generatorsB (
  value bigint UNSIGNED NOT NULL auto_increment,
  name varchar(255) NOT NULL,
  PRIMARY KEY(value)
) ENGINE=InnoDB DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_swedish_ci;

// name=any comment such as an entityClass name, no real application use
public static long getGeneratorBValue(String name) {
    Connection conn = null;
    Statement stmt = null;
    ResultSet rs = null;
    try {
        String sql = String.format("Insert Into generatorsB (name) Values('%s')", name);
        conn = createConnection(false); // autocommit=false
        stmt = conn.createStatement();
        int rowCount = stmt.executeUpdate(sql, Statement.RETURN_GENERATED_KEYS);
        if (rowCount>0) {
            rs = stmt.getGeneratedKeys();
            if (rs.next()) {
                long newValue = rs.getLong(1);
                if (newValue % 5 == 0) {
                    // delete old rows every 5th call, we don't need generator table rows
                    sql = "Delete From generatorsB Where value < "+ (newValue-5);
                    stmt.close();
                    stmt = conn.createStatement();
                    stmt.executeUpdate(sql, Statement.NO_GENERATED_KEYS);
                }
                conn.commit();
                return newValue;
            }
        }
        throw new IllegalArgumentException("Obtaining a generator value failed");
    } catch(Exception ex) {
        try { conn.rollback(); } catch(Exception e){}
        if (ex instanceof IllegalArgumentException) throw (IllegalArgumentException)ex;
        else throw new IllegalArgumentException(ex.getMessage(), ex);
    } finally {
        if (conn!=null) close(rs, stmt, conn);
    }
}

关于java - 无主键和一般用途的 JPA 数字生成器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30981764/

相关文章:

java - Web 应用程序和 Tomcat 7 内存泄漏

java - 在 Tomcat 7 中部署 JAX-WS Web 应用程序

java - 如何在java应用程序运行时更新css文件

java - 我的启动中的某些内容导致应用程序崩溃。如果有人能提供帮助,我将不胜感激

java - 计算文件中的所有字符,包括\n 等

java - ibm mq + ssl + 自签名证书

java - 获取延迟加载的@ManyToOne 实体的@Id 始终返回 null

MySQL 性能和 Memcache

java - Junit 在循环中处理预期异常

java - 使用 Java JPA (EclipseLink) MySQL 不时删除表的所有内容