使用PostgreSQL的Hibernate import.sql重复主键

时间:2016-04-05 06:33:04

标签: java hibernate postgresql jpa

我正在使用Hibernate和PostgreSQL。我有一个在创建/验证模式后Hibernate执行的import.sql文件。相应地导入内容,但是当我尝试通过我的应用程序保留新条目时,Hibernate会尝试使用从1开始自动生成的主键,这些主键已经被我的导入文件使用。有办法解决这个问题吗?

我有一个所有其他实体都继承自的实体类,它使用GenerationType.AUTOGenerationType.IDENTITY。它们都不起作用:

@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;
    }

}

这是我的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>

3 个答案:

答案 0 :(得分:2)

可能Hibernate使用名为hibernate_sequence的全局数据库范围的序列。您也可以从其他数据库导入它。您可以在insert中为您的脚本添加hibernate_sequence语句,以获取较大的ID值。

请在SQL日志中看到Hibernate使用hibernate_sequence

<强>更新
@DouglasDeRizzoMeneghetti的一些补充说明

  

我按照你说的做了,但它确实奏效了。 FK成为可以为空的整数,现在我在数据库中只有一个序列,而不是每个ID一个。我发现即使连接表也创建了自己的序列,因此通过使用单个序列,一切都变得更清晰,更有条理。

答案 1 :(得分:2)

事实证明PostgreSQL的列有一个“串行”数据类型,它基本上是一个与序列相关的整数数据类型。序列由DBMS自动创建和处理。为了强制Hibernate创建这样的列类型,可以使用@Column(columnDefinition = "serial")注释,如下所示:

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

请注意,虽然序列正在用于生成ID,但我没有使用strategy = GenerationType.SEQUENCE,因为这会告诉Hibernate创建自己的hibernate_sequence并在所有表中使用它,行为我试图避免。生成的序列的名称遵循模式${tablename}_${column_id_name}_seq,如'client_id_seq'。然后我可以在导入脚本中省略ID的硬编码插入,所以:

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

成为:

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

并且,当我的webapp中持久化实体时,Hibernate会自动为每个表使用正确的序列,因此我没有任何关于重复值的问题。

作为另一个贡献,如果您的@MappedSuperclass在其ID列中使用上述注释,则所有子类都会自动创建自己的序列,因此不必为每个实体执行此操作。我看到的唯一缺点是,虽然我有一个注释在MariaDB下工作而不是PostgreSQL,但现在我有一个注释不适用于MariaDB,因为我不记得它具有{{1的串行数据类型}} 军队。非常适合抽象和脱离数据库。

编辑:此策略引发了另一个问题。我的PK是串行类型的事实使得Hibernate创建了相应的FK作为序列。由于串行类型不能在PostgreSQL中为nullable,我的实体之间不能有可选的关系。在插入我的测试数据之后,我最终使用@ v.ladynev提出的解决方案,在我的实体ID中使用@Column(columnDefinition = "serial")并为GenerationType.SEQUENCE设置高起始值

这样,FK成为可以为空的整数,现在我在数据库中只有一个序列,而不是每个ID一个。我发现即使连接表也创建了自己的序列,因此通过使用单个序列,一切都变得更清晰,更有条理。谢谢。

答案 2 :(得分:0)

postgres中id列的数据类型应为serial

使用此注释

@Id @GeneratedValue(strategy=GenerationType.IDENTITY)