简体   繁体   English

Hibernate import.sql与PostgreSQL重复主键

[英]Hibernate import.sql with PostgreSQL repeats primary keys

I am using Hibernate with PostgreSQL. 我在PostgreSQL中使用Hibernate。 I have an import.sql file that Hibernate executes after the schema is created/validated. 创建/验证架构后,我有一个Hibernate执行的import.sql文件。 The content is imported accordingly, but when I try to persist new entries through my application, Hibernate tries to use automatically generated primary keys starting from 1, which were already used by my import file. 内容将相应地导入,但是当我尝试通过应用程序保留新条目时,Hibernate尝试使用从1开始自动生成的主键,这些主键已经被导入文件使用。 Is there way to fix this? 有办法解决吗?

I have a single entity class that all other entities inherit from, which uses either GenerationType.AUTO or GenerationType.IDENTITY . 我有一个所有其他实体都继承自的实体类,它使用GenerationType.AUTOGenerationType.IDENTITY Both of them did not work: 他们两个都不起作用:

@MappedSuperclass
public class Entidade implements Serializable {
    private static final long serialVersionUID = -991512134416936719L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;

    public Entidade() {
    }

    public Entidade(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

}

And here is my persistence.xml file: 这是我的persistence.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
    xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
    <persistence-unit name="...">
        <jta-data-source>...</jta-data-source>
        <class>...</class>
        <properties>
            <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect" />
            <property name="hibernate.hbm2ddl.auto" value="create-drop" />
            <property name="hibernate.default_schema" value="jcat" />
            <property name="hibernate.hbm2ddl.import_files_sql_extractor"
                value="org.hibernate.tool.hbm2ddl.MultipleLinesSqlCommandExtractor" />
            <property name="hibernate.show_sql" value="true" />
            <property name="hibernate.hbm2ddl.import_files" value="import.sql" />
            <property name="hibernate.c3p0.min_size" value="5" />
            <property name="hibernate.c3p0.max_size" value="20" />
            <property name="hibernate.c3p0.max_statements" value="50" />
            <property name="hibernate.c3p0.timeout" value="3000" />
            <property name="hibernate.c3p0.idle_test_period" value="1800" />
        </properties>
    </persistence-unit>
</persistence>

Probably, Hibernate uses a global database-wide sequence called hibernate_sequence . Hibernate可能使用了一个称为hibernate_sequence的全局数据库范围的序列。 You can import it form an other database too. 您也可以将其导入其他数据库。 You can add to your script an insert statement for a large id value in hibernate_sequence . 您可以在hibernate_sequence中将具有较大id值的insert语句添加到脚本中。

Please, see in the SQL log does Hibernate use hibernate_sequence ? 请在SQL日志中查看Hibernate是否使用hibernate_sequence

Update 更新
Some additional notes by @DouglasDeRizzoMeneghetti @DouglasDeRizzoMeneghetti的一些附加说明

I did as you said and it worked. 我照你说的做了,奏效了。 The FKs became integers, which are nullable, and now I have only one sequence in the database, instead of one for each ID. FK变为整数,可以为空,现在我在数据库中只有一个序列,而不是每个ID都有一个序列。 I found out that even join tables had their own sequences created, so by using a single sequence, everything got cleaner and more organized. 我发现,即使联接表也创建了自己的序列,因此通过使用单个序列,所有内容都变得更加整洁和井井有条。

It turns out PostgreSQL has a "serial" data type for its columns, which is basically an integer data type associated with a sequence. 事实证明,PostgreSQL的列具有“串行”数据类型,基本上是与序列关联的整数数据类型。 the sequence is automatically created and handled by the DBMS. 该序列由DBMS自动创建和处理。 In order to force Hibernate to create such a column type, one uses the @Column(columnDefinition = "serial") annotation, like so: 为了强制Hibernate创建这样的列类型,可以使用@Column(columnDefinition = "serial")注释,如下所示:

@Id
@Column(columnDefinition = "serial")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;

Note that, while a sequence is being used to generate IDs, I didn't use strategy = GenerationType.SEQUENCE , because that would tell Hibernate to create is own hibernate_sequence and use it across all tables, a behavior I tried to avoid. 请注意,虽然序列用于生成ID,但我没有使用strategy = GenerationType.SEQUENCE ,因为这将告诉Hibernate创建自己的hibernate_sequence并在所有表中使用它,我试图避免这种行为。 The name of the generated sequence follows the pattern ${tablename}_${column_id_name}_seq , like 'client_id_seq'. 生成序列的名称遵循${tablename}_${column_id_name}_seq ,例如'client_id_seq'。 I could then omit in my import script the hard-coded insertion of IDs, so: 然后,我可以在导入脚本中省略ID的硬编码插入,因此:

insert into schema.table (id, name) values (1, 'John');

became: 成为:

insert into schema.table (name) values ('John');

and, when persisting entities in my webapp, Hibernate automatically uses the correct sequence for each table, so I did not have any more problems regarding repeated values. 而且,当将实体保留在我的Web应用程序中时,Hibernate会自动为每个表使用正确的顺序,因此我不再有关于重复值的问题。

As another contribution, if you have a @MappedSuperclass that uses the aforementioned annotation in its ID column, all subclasses automatically create their own sequences, so it's not necessary to do it for every single entity. 另一个贡献是,如果您有一个@MappedSuperclass在其ID列中使用上述注释,则所有子类都会自动创建自己的序列,因此不必为每个实体都执行此操作。 The only down side I saw is that, while I had an annotation that worked under MariaDB but not PostgreSQL, now I have an annotation that would not work on MariaDB, since I don't recall it having the serial data type that @Column(columnDefinition = "serial") forces. 我唯一看到的缺点是,虽然我有一个在MariaDB下可运行但在PostgreSQL下不起作用的注释,但现在我有一个在MariaDB上不起作用的注释,因为我不记得它具有@Column(columnDefinition = "serial")强制。 So much for abstraction and detachment from the database. 从数据库进行抽象和分离的工作量很大。

EDIT: This strategy raised another problem. 编辑:此策略提出了另一个问题。 The fact that my PKs were of serial type made Hibernate create their corresponding FKs as serial. 我的PK是串行类型的,这一事实使Hibernate将其对应的FK创建为串行。 Since the serial type can't be nullablein PostgreSQL, I couldn't have optional relations between my entities. 由于串行类型在PostgreSQL中不能为空,因此我的实体之间没有可选的关系。 I ended up using the solution proposed by @v.ladynev, using GenerationType.SEQUENCE in my entities IDs and setting a high starting value for hibernate_sequence , after inserting my test data 我最终使用了@ v.ladynev提出的解决方案,在我的实体ID中使用了GenerationType.SEQUENCE并为hibernate_sequence设置了一个较高的起始值,然后插入测试数据

This way, the FKs became integers, which are nullable, and now I have only one sequence in the database, instead of one for each ID. 这样,FK变成了可以为空的整数,现在我在数据库中只有一个序列,而不是每个ID都有一个序列。 I found out that even join tables had their own sequences created, so by using a single sequence, everything got cleaner and more organized. 我发现,即使联接表也创建了自己的序列,因此通过使用单个序列,所有内容都变得更加整洁和井井有条。 Thank you. 谢谢。

data type of your id column in postgres should be serial 您在postgres中的ID列的数据类型应该是串行的

than use this annotation 比使用此注释

@Id @GeneratedValue(strategy=GenerationType.IDENTITY)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM