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

9

我有一个Member实体类,其主键定义如下:

    @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

如果使用注释,则我总是需要在插入之前从数据库表中找出下一个键。

有没有最佳方式来处理这种看似很普遍的情况?

编辑:

如建议的那样,我使用了策略:

@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保留为空即可;这就是将其作为包装类型的目的。 - chrylis -cautiouslyoptimistic-
不要在插入语句中输入ID。 - Jens
6个回答

13

你应该使用 IDENTITY 生成器。 IDENTITY 生成器允许整数和大整数列在需要时自动递增。由于它使用数据库内部轻量级锁定机制而不是更重的事务粗粒度锁定,所以递增过程非常高效。

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

不要在插入语句中使用id,因为它会自动生成。
请注意,SEQUENCE对MySQL不起作用。


我会尝试。谢谢。 - user697911
1
“按需自动递增”意味着如果表已经有两条记录“1”和“2”,则会生成一个新的主键“3”,而不是“1”。 - user697911

1
<id name="id" column="id" type="integer"> 
    <generator class="increment"></generator> 
</id> 

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

1
对于MySQL,您可以成功使用以下任何一种策略进行持久化:
@GeneratedValue(strategy=GenerationType.AUTO)  

=> 指定主键的auto_increment属性

@GeneratedValue(strategy=GenerationType.IDENTITY)

=>为主键指定auto_increment属性

在更高级的用法中,您可以利用TABLE策略GenerationType.TABLE,在其中可以从单独的表中指定主键,并将此表指定为@TableGenerator

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


0

(postgresql) 在填充数据库的脚本中,在结尾添加以下内容:

ALTER SEQUENCE RESTART WITH 13;

查看数据库以找到序列名称,例如:

ALTER SEQUENCE role_role_id_seq RESTART WITH 13;

您还可以使用模型上的注释来命名生成的序列名称...

希望能帮到您!


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

当持久化时,您可以将其设置为null(0)

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

看起来,对于您来说,SEQUENCE + 指定序列名称是有效的。


0

现在在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;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接