Hibernate JPA中的自定义主键生成器

Hibernate JPA中的自定义主键生成器,hibernate,jpa,id-generation,shared-primary-key,Hibernate,Jpa,Id Generation,Shared Primary Key,我使用org.hibernate.id.IdentifierGenerator生成一个主键列,如下所示。在下面给出的示例中,当前它只是按顺序递增INT(11)(MySQL)类型的键,即它确实像MySQL中的auto_increment,但它可以用于生成任何自定义模式的值,如E0001、E0002、E0003。。。E0010。。。E0100。。。E1000。。。E12345 import java.io.Serializable; import java.sql.Connection; impor

我使用
org.hibernate.id.IdentifierGenerator
生成一个主键列,如下所示。在下面给出的示例中,当前它只是按顺序递增
INT(11)
(MySQL)类型的键,即它确实像MySQL中的
auto_increment
,但它可以用于生成任何自定义模式的值,如E0001、E0002、E0003。。。E0010。。。E0100。。。E1000。。。E12345

import java.io.Serializable;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.id.IdentifierGenerator;

public final class TestIdGenerator implements IdentifierGenerator {

    @Override
    public Serializable generate(SessionImplementor session, Object object) throws HibernateException {

        try {
            Connection connection = session.connection();
            PreparedStatement ps = connection.prepareStatement("SELECT MAX(id) AS id FROM test");
            ResultSet rs = ps.executeQuery();

            if (rs.next()) {
                int id = rs.getInt("id");
                return id <= 0 ? 1 : id + 1;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

        return null;
    }
}
然而,这是一个特定于Hibernate的特性。有没有一种方法可以使it提供商不可知,即JPA(2.1)中是否有这样的功能

我使用的是Hibernate 5.1.0,最终版本是JPA2.1



这种方法在持久化、合并和删除实体时是否会导致一些并发问题,并且需要某种类型的锁定?

您不能像希望的那样使用注释。您可以使用
@PrePersist
钩子

@PrePersist
public void ensureId() {
    id = ...
}
也就是说,您可能无法从
@PrePersist
方法访问数据库。。。JPA 2规范(JSR 317)规定如下:

通常,可移植应用程序的生命周期方法不应调用EntityManager或查询操作、访问其他实体实例或修改同一持久性上下文中的关系

就实现而言,Hibernate明确禁止:

回调方法不能调用EntityManager或查询方法

我的建议是:如果需要指定类型,请使用序列并有一个单独的列。此外,您始终可以修改
选择
,以撤回您选择的模式:

select 'E' || id as id from schema.table;
或者,您可以使用@PostLoad/@pospersist回调临时字段“formattedId”:


您可以使用事件监听器,并在preStore回调中将PK字段设置为max(id)(并删除GeneratedValue注释)。这适用于所有JPA提供者。不幸的是,序列生成的主键或自动生成的主键是不允许的。有一些独立的数据库维护人员对自动生成或序列生成的主键不感兴趣。。。那效率太低了。你是唯一一个将插入到表中的人吗?您也许可以在启动时缓存最大值,然后在内存中递增(但我不喜欢这样)。您是否可以放弃基于序列的键的概念,转而使用复合业务键,例如“事务类型、用户、时间戳”之类的内容?
select 'E' || id as id from schema.table;
@PostLoad
@PostPersist
private void setFormattedId() {
    this.formattedId = "E" + id;
}