如何使用 Hibernate 插入记录正确自动生成主键 ID

Posted

技术标签:

【中文标题】如何使用 Hibernate 插入记录正确自动生成主键 ID【英文标题】:How to auto generate primary key ID properly with Hibernate inserting records 【发布时间】:2016-12-23 15:31:09 【问题描述】:

我有一个成员实体类,其主键定义为:

    @Id
    @GeneratedValue
    private Long id;

我在启动应用程序时使用 Hibernate 将两条记录预加载到数据库中:

insert into Member (id, name, email, phone_number) values (0, 'John Smith', 'john.smith@mailinator.com', '2125551212')
insert into Member (id, name, email, phone_number) values (1, 'Mary Smith', 'mary.smith@mailinator.com', '2025551212') 

现在mysql数据库有两条记录:

select * from Member;
+----+---------------------------+------------+--------------+
| id | email                     | name       | phone_number |
+----+---------------------------+------------+--------------+
|  0 | john.smith@mailinator.com | John Smith | 2125551212   |
|  1 | mary.smith@mailinator.com | Mary Smith | 2025551212   |

+----+---------------+------------+ --------------+

现在,在我的会员注册页面中,当我提交注册新会员的请求时,我收到了以下错误消息:

Caused by: java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '1' for key 'PRIMARY'

我认为是因为自动生成的键总是以'1'开头,但是在数据库中,它已经有两条记录,所以主键'1'会重复。

我的问题是,给定表中现有的许多记录,如何正确创建主键?如果我不使用

@GeneratedValue

注解,那么我总是要在插入之前从数据库表中找出下一个键是什么。

有没有最好的方法来处理这种似乎很常见的情况?

编辑: 按照建议,我使用了 Stragey:

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

奇怪的是,如果表中有两条现有记录如下:

select * from English;
+----+------------+---------+--------+
| id | sentence   | source  | status |
+----+------------+---------+--------+
|  1 | i love you | unknown |      0 |
|  2 | i like you | unknown |      0 |
+----+------------+---------+--------+

我在表中注册一条新记录后,新的 ID 以 4 开头,而不是 3,如下所示。

select * from English;
+----+----------------+---------+--------+
| id | sentence       | source  | status |
+----+----------------+---------+--------+
|  1 | i love you     | unknown |      0 |
|  2 | i like you     | unknown |      0 |
|  4 | I have a book. | Unknown |      0 |
+----+----------------+---------+--------+
3 rows in set (0.00 sec)

什么可能导致这种情况?

【问题讨论】:

您需要提供表架构和用于保存的代码。通常,您只需什么都不做,并在保存之前将 ID 保留为空;这就是将其作为包装类型的意义所在。 insert语句中不要输入id 【参考方案1】:
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;  

你在坚持的时候留下null (0)

在某些情况下,AUTO 策略被解析为 SEQUENCE 而不是IDENTITYTABLE,因此您可能需要手动将其设置为IDENTITYTABLE(取决于底层数据库)。

似乎 SEQUENCE + 指定序列名称对你有用。

【讨论】:

【参考方案2】:

您应该使用IDENTITY 生成器。 IDENTITY 生成器允许根据需要自动递增整数和 bigint 列。增量过程非常有效,因为它使用数据库内部的轻量级锁定机制,而不是更重量级的事务性进程锁。

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

并且不要在插入语句中使用 id,因为它是自动生成的。 请注意,SEQUENCE 不适用于您,因为 SEQUENCE 不适用于 MySQL

【讨论】:

我会试试的。谢谢。 "auto-incremented on demand"意思是如果表已经有“1”和“2”两条记录,会生成一个新的主键“3”而不是1?【参考方案3】:

对于 MySQL,您将能够使用以下任何一种策略成功持久化:

@GeneratedValue(strategy=GenerationType.AUTO)  

=> 为主键指定 auto_increment 属性

@GeneratedValue(strategy=GenerationType.IDENTITY)

=>为主键指定 auto_increment 属性

在进一步的高级用法中,您可以使用TABLE 策略GenerationType.TABLE,您可以从单独的表中指定主键,并且可以将该表指定为@TableGenerator

Hibernate 还有一个生成策略:native。它根据底层数据库的能力适当地选择生成策略。

【讨论】:

【参考方案4】:
<id name="id" column="id" type="integer"> 
    <generator class="increment"></generator> 
</id> 

如果您使用映射类,则只需使用上述代码自动生成主键。

【讨论】:

【参考方案5】:

(postgresql) 在填充数据库的脚本中,在末尾添加:

ALTER SEQUENCE RESTART WITH 13;

查看 db 以找到 seq 名称,例如:

ALTER SEQUENCE role_role_id_seq RESTART WITH 13;

您应该能够命名生成的序列名称以及模型上的注释...

希望对你有帮助!

【讨论】:

【参考方案6】:

现在在 Hibernate 5.2 中,您只需在变量级别或方法级别检查 @GeneratedValue(strategy = IDENTITY)

@Id
@GeneratedValue(strategy = IDENTITY)
private Integer id;

@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "Id", unique = true, nullable = false)

public Integer getId() 
    return this.id;

【讨论】:

以上是关于如何使用 Hibernate 插入记录正确自动生成主键 ID的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Hibernate session.save() 插入/保存对象而不更新旧记录

如何使用hibernate避免遇到违反唯一约束的问题?

如何使用 Java / Hibernate 在 SQL 中自动插入当前日期

在 Hibernate 中,如何自动检索父 id 并在子插入中将其用作外键?

如何使用 JPA/Hibernate 在孩子的 id 中引用父母的 id?

如何在JPA / JAVA / Hibernate的两列中插入自动生成的ID