以下Hibernate自定义ID生成器代码是否正确?

时间:2012-10-22 15:37:09

标签: java hibernate transactions

我刚刚创建了一个自定义的hibernate ID生成器,因为我不是一个hibernate专家,我想得到一些关于我的代码的反馈。生成的ID为select max(id) from table,+ 1。

public class MaxIdGenerator implements IdentifierGenerator, Configurable {

    private Type identifierType;
    private String tableName;
    private String columnName;

    @Override
    public void configure(Type type, Properties params, Dialect dialect) {
        identifierType = type;
        tableName = (String) params.getProperty("target_table");
        columnName = (String) params.getProperty("target_column");
    }

    @Override
    public synchronized Serializable generate(SessionImplementor session,
            Object object) {
        return generateHolder(session).makeValue();
    }

    protected IntegralDataTypeHolder generateHolder(SessionImplementor session) {
        Connection connection = session.connection();
        try {
            IntegralDataTypeHolder value = IdentifierGeneratorHelper
                .getIntegralDataTypeHolder(identifierType
                        .getReturnedClass());
            String sql = "select max(" + columnName + ") from " + tableName;
            PreparedStatement qps = connection.prepareStatement(sql);
            try {
                ResultSet rs = qps.executeQuery();
                if (rs.next())
                    value.initialize(rs, 1);
                else
                    value.initialize(1);
                rs.close();
            } finally {
                qps.close();
            }
            return value.copy().increment();
        } catch (SQLException e) {
            throw new IdentifierGenerationException(
                    "Can't select max id value", e);
        }
    }
    }

我想知道:

  1. 如何使此多事务安全? (即如果两个并发事务插入数据,我怎么能安全地假设我不会最终拥有两倍相同的ID?) - 我想这里唯一的解决方案是防止两个并发的hibernate事务同时运行如果他们使用相同的发电机,这可能吗?
  2. 如果代码可以改进:我觉得我不应该使用硬编码的"select""target_column"等...
  3. 为了保证第1点),如果有必要,我可以在我的java客户端代码上同步插入时回退。

    请不要评论我使用这种生成器的原因:遗留代码仍然将数据插入到同一个数据库中并使用此机制......并且无法修改。是的,我知道,这很糟糕。

2 个答案:

答案 0 :(得分:2)

我认为实现事务安全行为的最简单方法是在事务块中放置用于检索最大id的代码并执行insert语句。 类似的东西:

Transaction transaction = session.beginTransaction();
//some code...
transaction.commit();
session.close()

我还建议使用HQL(Hibernate Query Language)来创建查询,而不是在可能的情况下使用本机sql。此外,根据您的描述,我已经了解您希望从查询中获得唯一的结果,即最大ID。因此,您可以在查询中使用uniqueResult()方法而不是executeQuery。

答案 1 :(得分:0)

您可以使用AtomicInteger生成ID。这可以被许多线程同时使用。

如果您可以自由使用任何其他ID提供程序,那么我建议使用UUID类生成随机ID。

UUID.randomUUID();

您可以参考link,其中包含一些其他生成ID的方法。