使用 MySQL 和 Oracle 进行休眠自动密钥生成

Posted

技术标签:

【中文标题】使用 MySQL 和 Oracle 进行休眠自动密钥生成【英文标题】:Hibernate auto key generation with MySQL and Oracle 【发布时间】:2015-08-24 05:55:06 【问题描述】:

我正在开发一个 Java 应用程序,它应该在具有相同数据库架构的两个不同数据库上执行 CRUD 操作(使用 Hibernate 4.3.8)。 有一个 MySQL(版本 5.1.73)和一个 Oracle(11g Express Edition Release 11.2.0.2.0 - 64 位)数据库。

带有 JPA 注释 的 Java 类是使用 Hibernate Code Generation 从数据库表中生成的。

问题是我们现在需要使用自动主键生成,而 mysql 使用 GenerationType.IDENTITY 而 Oracle 使用 GenerationType.SEQUENCE。此外,在极少数情况下,我们还需要自己手动设置主键。

注释类中的后续代码适用于两个数据库的自动密钥生成,但如果主键是自设置的,则会失败。

@GeneratedValue(strategy=GenerationType.AUTO, generator="sequence_generator")
@SequenceGenerator(name="sequence_generator", sequenceName="SEQUENCE1")
@Column(name = "id", unique = true, nullable = false)
public Integer getId() 
    return this.id;

如果没有 @GeneratedValue@SequenceGenerator 注释,可以手动设置主键,但自动生成不起作用。

【问题讨论】:

试试这个 [manually-specify-the-value-of-a-primary-key-in-jpa-generatedvalue-column][1]。有很多解决方案。 [1]:***.com/questions/12002260/… 此线程中以数据库为中心的解决方案对我不起作用,因为不应更改数据库架构。除了我之外,还有其他人相信数据库架构不会改变。而且我不知道这是否可能以及如何编写自己的自定义 ID 生成器,它同时支持数据库 MySQL 和 Oracle(身份和序列)。在所有示例中,他们要么使用 IdentifierGenerator 要么使用 Sequencegenerator。 这个问题不清楚。您是说您希望 Hibernate 为某些类自动生成标识符并手动将标识符分配给其他类;或者您是说您希望 Hibernate 在大多数情况下自动为类的实例分配标识符,但在某些情况下您希望手动为同一类的某些实例分配标识符? 你应该使用触发器来拥有一个同构的跨数据库解决方案。 我的旧 Spring 4 项目过去在 MySQL 和 Oracle 11g 上都可以正常工作,只是带有 @SequenceGenerator(name = "id_generator", sequenceName = "sq_id_user") 注释,但现在我在 Spring Boot 2 和 Hibernate 5 上抱怨一个非10.1.24-MariaDB 中的现有序列。 【参考方案1】:

即使您在没有任何 SEQUENCE 特定参数的情况下使用 GenerationType.AUTO,您也无法保存分配的标识符。

如果您愿意做出一些妥协,有一些解决方法:

    一种方法是切换到分配的标识符。您可以使用 UUID 标识符,它适用于 MySQL 和 Oracle,您也可以手动分配值。

    另一种方法是使用自定义表格生成器。

首先定义一个 Identifiable 接口:

    public interface Identifiable<T extends Serializable> 
        T getId();
    

然后你扩展表生成器:

    public class AssignedTableGenerator extends TableGenerator 

        @Override
        public Serializable generate(SessionImplementor session, Object obj) 
            if(obj instanceof Identifiable) 
                Identifiable identifiable = (Identifiable) obj;
                Serializable id = identifiable.getId();
                if(id != null) 
                    return id;
                
            
            return super.generate(session, obj);
        
    

此生成器能够将分配的标识符与合成生成的标识符混合:

    doInTransaction(session -> 
        for (int i = 0; i < 5; i++) 
            session.persist(new AssignTableSequenceIdentifier());
        
        AssignTableSequenceIdentifier tableSequenceIdentifier = new AssignTableSequenceIdentifier();
        tableSequenceIdentifier.id = -1L;
        session.merge(tableSequenceIdentifier);
        session.flush();
    );

生成以下语句:

    select tbl.next_val from sequence_table tbl where tbl.sequence_name=default for update
    insert into sequence_table (sequence_name, next_val)  values (default,1)
    update sequence_table set next_val=2  where next_val=1 and sequence_name=default
    select tbl.next_val from sequence_table tbl where tbl.sequence_name=default for update
    update sequence_table set next_val=3  where next_val=2 and sequence_name=default
    select tbl.next_val from sequence_table tbl where tbl.sequence_name=default for update
    update sequence_table set next_val=4  where next_val=3 and sequence_name=default
    select tbl.next_val from sequence_table tbl where tbl.sequence_name=default for update
    update sequence_table set next_val=5  where next_val=4 and sequence_name=default
    select tbl.next_val from sequence_table tbl where tbl.sequence_name=default for update
    update sequence_table set next_val=6  where next_val=5 and sequence_name=default
    select identityvs0_.id as id1_0_0_ from assigneTableIdentifier identityvs0_ where identityvs0_.id=-1
    insert into assigneTableIdentifier (id) values (1, 2)
    insert into assigneTableIdentifier (id) values (2, 4)
    insert into assigneTableIdentifier (id) values (5, -1)

对于 Oracle,您可以组合 SEQUENCE 和分配的生成器。简而言之,考虑以下生成器:

public class AssignedSequenceStyleGenerator 
    extends SequenceStyleGenerator 
 
    @Override
    public Serializable generate(SessionImplementor session, 
        Object obj) 
        if(obj instanceof Identifiable) 
            Identifiable identifiable = (Identifiable) obj;
            Serializable id = identifiable.getId();
            if(id != null) 
                return id;
            
        
        return super.generate(session, obj);
    

您可以将其映射到您的实体,如下所示:

@Id
@GenericGenerator(
    name = "assigned-sequence",
    strategy = "com.vladmihalcea.book.hpjp.hibernate.identifier.AssignedSequenceStyleGenerator",
    parameters = @org.hibernate.annotations.Parameter(
        name = "sequence_name", 
        value = "post_sequence"
    )
)
@GeneratedValue(
    generator = "assigned-sequence", 
    strategy = GenerationType.SEQUENCE
)
private Long id;

所有的代码都可以在GitHub 上找到,就像一个魅力。

【讨论】:

这种方法是否适用于 mysql 数据库?似乎我低于异常原因:org.hibernate.MappingException:org.hibernate.dialect.MySQL5Dialect 不支持序列 嗨弗拉德,你是说使用 TableGenerator 而不是序列生成器?我已经有一个实体类,并将其用于带有序列生成器的 oracle db。但我的目的是对 mysql db 也使用相同的类。据我了解,mysql 数据库中没有等效的序列,所以我应该更改我的 ID 生成策略(即使用另一个表而不是序列)? 嗨弗拉德。感谢您的建议。我点击了这个链接:vladmihalcea.com/…,并且可以将相同的实体与 oracle db 的序列和 mfsql db 的自动增量一起使用。谢谢 如果我将代码(名称和导入的所有更改)插入应用程序,服务器拒绝使用它。恕我直言,您忘记了一些重要的细节。 专业提示 1:从 Stack Overflow 复制代码并将其随机粘贴到您的应用程序中并非明智之举。专业提示 2:当您可以 fork GitHub repository 找到 package 并运行代码以查看它的工作原理时,为什么要从 Stack Overflow 复制代码。【参考方案2】:

试试这样的:

@Id
@Column( name = "ID" )
@TableGenerator( 
        name = "AppSeqStore", 
        table = "APP_SEQ_STORE", 
        pkColumnName = "APP_SEQ_NAME", 
        pkColumnValue = "LISTENER_PK", 
        valueColumnName = "APP_SEQ_VALUE", 
        initialValue = 1, 
        allocationSize = 1 )
@GeneratedValue( strategy = GenerationType.TABLE, generator = "AppSeqStore" )

以及数据库中的这张表:

CREATE TABLE APP_SEQ_STORE (
    APP_SEQ_NAME VARCHAR(255) NOT NULL,
    APP_SEQ_VALUE NUMBER(10) NOT NULL,
    PRIMARY KEY(APP_SEQ_NAME)
)

INSERT INTO APP_SEQ_STORE VALUES ('LISTENER_PK', 0)

这一切都可以在 Oracle、MS Sql Server 和 MySql 中使用 JBoss 作为 App Server。

更多信息在这里: http://www.developerscrappad.com/408/java/java-ee/ejb3-jpa-3-ways-of-generating-primary-key-through-generatedvalue/

【讨论】:

以上是关于使用 MySQL 和 Oracle 进行休眠自动密钥生成的主要内容,如果未能解决你的问题,请参考以下文章

休眠自动增量 MySQL

休眠/Oracle 序列不工作

oracle12c 上的休眠空间 - sdo_geometry 对象在 em.merge 上变为空

休眠(mysql)刷新与提交中的自动增量

使用 mysql 进行休眠配置会出现错误 - SAXParseException

使用休眠和 Oracle 将 Clob 转换为字符串