使用 PostgreSQL 的 Hibernate import.sql 重复主键
Posted
技术标签:
【中文标题】使用 PostgreSQL 的 Hibernate import.sql 重复主键【英文标题】:Hibernate import.sql with PostgreSQL repeats primary keys 【发布时间】:2016-04-05 06:33:04 【问题描述】:我将 Hibernate 与 PostgreSQL 一起使用。我有一个 import.sql 文件,Hibernate 在创建/验证架构后执行该文件。相应地导入了内容,但是当我尝试通过我的应用程序保留新条目时,Hibernate 尝试使用从 1 开始的自动生成的主键,我的导入文件已经使用了这些主键。有没有办法解决这个问题?
我有一个所有其他实体都继承自的单一实体类,它使用GenerationType.AUTO
或GenerationType.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>
【问题讨论】:
为什么不查看为 INSERT 调用的 SQL? 您需要更改 SQL 脚本,以便在脚本内调整序列。但是如果没有看到那个脚本,就不可能提供更多细节(基本上你不应该在那里使用硬编码的 ID,而是从序列中获取 ID) 如果我有需要填写的外键怎么办?使用硬编码的 ID,我可以确切地知道要放置哪些 ID,但是通过使用序列,我无法确定序列正在使用哪些值,因此我可以将它们放入外键中。 你不需要。只需使用例如引用生成的 IDlastval()
或使用数据修改 CTE。向我们展示您的 SQL 脚本。 *** 上有很多例子。
JPA“IDENTITY”的定义是在数据存储区中设置的生成值。这适用于 RDBMS 中的 AUTO_INCREMENT/SERIAL 类型功能。由于您使用了 INT,因此您颠覆了 JPA 机制。又名用户错误
【参考方案1】:
很可能,Hibernate 使用称为hibernate_sequence
的全局数据库范围序列。您也可以从其他数据库导入它。您可以在您的脚本中添加一个 insert
语句以获取 hibernate_sequence
中的大 id 值。
请在 SQL 日志中查看 Hibernate 是否使用 hibernate_sequence
?
更新 @DouglasDeRizzoMeneghetti 的一些补充说明
我按照你说的做了,效果很好。 FK 变成了整数,可以为空,现在我在数据库中只有一个序列,而不是每个 ID 一个序列。我发现即使是连接表也创建了自己的序列,因此通过使用单个序列,一切都变得更干净、更有条理。
【讨论】:
如果您使用 GenerationType.SEQUENCE,Hibernate 会创建自己的序列,但这些值会在所有表中使用,这是我想避免的行为。 @DouglasDeRizzoMeneghetti 不知道你这样做的原因。多年来,我们的团队一直在将此类序列用于大型系统(使用 Oracle DB 和 PostgreSQL)。但如你所愿:)【参考方案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');
而且,当在我的 web 应用程序中持久化实体时,Hibernate 会自动为每个表使用正确的顺序,因此我不会再遇到任何关于重复值的问题。
作为另一个贡献,如果您有一个@MappedSuperclass
在其 ID 列中使用上述注释,则所有子类都会自动创建自己的序列,因此不必为每个实体都这样做。我看到的唯一缺点是,虽然我有一个在 MariaDB 但不能在 PostgreSQL 下工作的注释,但现在我有一个在 MariaDB 上不起作用的注释,因为我不记得它具有@Column(columnDefinition = "serial")
的串行数据类型军队。对数据库的抽象和分离来说就这么多了。
编辑:这种策略引发了另一个问题。我的 PK 是串行类型的事实使 Hibernate 将其相应的 FK 创建为串行。由于在 PostgreSQL 中序列类型不能为空,我的实体之间不能有可选的关系。在插入我的测试数据后,我最终使用了@v.ladynev 提出的解决方案,在我的实体 ID 中使用了GenerationType.SEQUENCE
,并为hibernate_sequence
设置了一个较高的起始值
这样,FK 变成了整数,可以为空,现在我在数据库中只有一个序列,而不是每个 ID 一个序列。我发现即使是连接表也创建了自己的序列,因此通过使用单个序列,一切都变得更清晰、更有条理。谢谢你。
【讨论】:
如何处理外键? 在导入脚本中,我可以只使用之前使用的序号。但是,PostgreSQL 将 FK 列创建为serial
列并创建其对应的序列,但 serial
列还允许您在数据插入期间选择一个值,在这种情况下它不使用序列值。到目前为止,我还没有发现任何问题。
@v.ladynev 现在我想我知道你在说什么了。由于我的外键是串行类型的,它们不能为空,所以我的实体之间不能有可选的关系。现在这是一个我需要帮助的问题。
那么为什么不回到顺序方法呢?
@v.ladynev 你的意思是使用hibernate_sequence
?那么我的列可以是普通的旧整数类型吗?【参考方案3】:
你在 postgres 中的 id 列的数据类型应该是串行的
比使用这个注解
@Id @GeneratedValue(strategy=GenerationType.IDENTITY)
【讨论】:
目前我的 PK 是 int 类型。如果是这种情况,那么如何通过注释我的类来设置序列列类型? 你需要在PostgreSQL数据库中改变你的列数据类型,你不能通过注解类来改变列的类型。@Id @Column(columnDefinition = "serial") @GeneratedValue(strategy = GenerationType.IDENTITY)
可以解决问题。以上是关于使用 PostgreSQL 的 Hibernate import.sql 重复主键的主要内容,如果未能解决你的问题,请参考以下文章
使用 PostgreSQL 的 Hibernate import.sql 重复主键
使用 Hibernate 从 PostgreSQL 中的 JSONArray 获取对象的问题
不能用Hibernate调用PostgreSQL的11个存储过程