保存子实体时重复键值
Duplicate key value on saving child entities
当我尝试保存 ManyToMany 关系的实体(角色)时,子实体的 ID 生成不正确,并且我收到 DataIntegrityViolationException。
保存父实体和子实体的一部分:
public Organization create(Organization organization) {
Organization created = save(organization);
Role role = new Role();
role.setCode("test");
created.getRoles().add(role);
return save(created);
}
Hibernate 调试和异常:
Hibernate: insert into organization (company_code, full_legal_name, id) values (?, ?, ?)
Hibernate: insert into role (code, description, read_only, reserved) values (?, ?, ?, ?)
2021-12-23 10:34:50.459 WARN 13464 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 0, SQLState: 23505
2021-12-23 10:34:50.459 ERROR 13464 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : ERROR: duplicate key value violates unique constraint "role_pkey"
Detail: Key (id)=(15) already exists.
父实体:
@Entity
public class Organization {
@Id
Long id;
@ManyToMany(cascade = {CascadeType.ALL}, fetch= FetchType.EAGER)
@JoinTable(name = "organization_role",
joinColumns = @JoinColumn(name = "organization_id"),
inverseJoinColumns = @JoinColumn(name = "role_id"))
Set<Role> roles = new HashSet<>();
}
角色实体:
@Entity
public class Role implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
}
液基表:
角色:
CREATE TABLE role
(
id SERIAL NOT NULL,
code VARCHAR(32) NOT NULL,
CONSTRAINT role_pkey PRIMARY KEY (id)
);
组织:
CREATE TABLE organization (
id bigint NOT NULL,
CONSTRAINT organization_pkey PRIMARY KEY (id)
);
组织角色:
CREATE TABLE organization_role (
role_id INTEGER NOT NULL,
organization_id BIGINT NOT NULL,
CONSTRAINT fk_organization_role_role FOREIGN KEY (role_id) REFERENCES role(id),
CONSTRAINT fk_organization_role_organization FOREIGN KEY (organization_id) REFERENCES organization(id),
CONSTRAINT user_organization_pkey PRIMARY KEY (role_id, organization_id)
);
尝试先保存角色,然后将其添加到 Org,然后保存 Org:
Organization created = save(organization);
Role role = new Role();
role.setCode("test");
save(role); // this assign a persistant primary key to the role
created.getRoles().add(role);
return save(created);
这与 MTM 无关。这种情况只有当你的 PK 序列(串行)落后于某种方式时才有可能,现在它生成的密钥已经存在于 table 中。为什么会发生这种情况的几个想法:
- 您重新创建了与连续剧相关的序列,所以它又从 1 开始了。但是直到 15 之前都没有 ID 的记录,所以前 14 次插入是成功的。
- 您插入的记录带有明确指定的 ID,而不是使用
serial
功能。以下是重现问题的方法(第二次插入会导致问题):
drop table if exists org;
create table org (
id serial,
name text,
constraint org_pk primary key (id)
);
insert into org(name) values('name1'); -- works fine
insert into org(id, name) values(2, 'name2'); -- we don't use the sequence
insert into org(name) values('name3');-- sequence generates 2 again and insert fails
您需要找出是谁搞砸了 sequence/who 插入具有指定 ID 的行。并更新那个地方:
- 要么根本不指定 ID - serial 将为您完成此操作
- 或者在插入语句中显式访问序列:
insert into org(id, name) values(nextval('org_id_seq'), 'name2');
- 或者 update the sequence 在插入行后使用 setval()。
当我尝试保存 ManyToMany 关系的实体(角色)时,子实体的 ID 生成不正确,并且我收到 DataIntegrityViolationException。
保存父实体和子实体的一部分:
public Organization create(Organization organization) {
Organization created = save(organization);
Role role = new Role();
role.setCode("test");
created.getRoles().add(role);
return save(created);
}
Hibernate 调试和异常:
Hibernate: insert into organization (company_code, full_legal_name, id) values (?, ?, ?)
Hibernate: insert into role (code, description, read_only, reserved) values (?, ?, ?, ?)
2021-12-23 10:34:50.459 WARN 13464 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 0, SQLState: 23505
2021-12-23 10:34:50.459 ERROR 13464 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : ERROR: duplicate key value violates unique constraint "role_pkey"
Detail: Key (id)=(15) already exists.
父实体:
@Entity
public class Organization {
@Id
Long id;
@ManyToMany(cascade = {CascadeType.ALL}, fetch= FetchType.EAGER)
@JoinTable(name = "organization_role",
joinColumns = @JoinColumn(name = "organization_id"),
inverseJoinColumns = @JoinColumn(name = "role_id"))
Set<Role> roles = new HashSet<>();
}
角色实体:
@Entity
public class Role implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
}
液基表: 角色:
CREATE TABLE role
(
id SERIAL NOT NULL,
code VARCHAR(32) NOT NULL,
CONSTRAINT role_pkey PRIMARY KEY (id)
);
组织:
CREATE TABLE organization (
id bigint NOT NULL,
CONSTRAINT organization_pkey PRIMARY KEY (id)
);
组织角色:
CREATE TABLE organization_role (
role_id INTEGER NOT NULL,
organization_id BIGINT NOT NULL,
CONSTRAINT fk_organization_role_role FOREIGN KEY (role_id) REFERENCES role(id),
CONSTRAINT fk_organization_role_organization FOREIGN KEY (organization_id) REFERENCES organization(id),
CONSTRAINT user_organization_pkey PRIMARY KEY (role_id, organization_id)
);
尝试先保存角色,然后将其添加到 Org,然后保存 Org:
Organization created = save(organization);
Role role = new Role();
role.setCode("test");
save(role); // this assign a persistant primary key to the role
created.getRoles().add(role);
return save(created);
这与 MTM 无关。这种情况只有当你的 PK 序列(串行)落后于某种方式时才有可能,现在它生成的密钥已经存在于 table 中。为什么会发生这种情况的几个想法:
- 您重新创建了与连续剧相关的序列,所以它又从 1 开始了。但是直到 15 之前都没有 ID 的记录,所以前 14 次插入是成功的。
- 您插入的记录带有明确指定的 ID,而不是使用
serial
功能。以下是重现问题的方法(第二次插入会导致问题):
drop table if exists org;
create table org (
id serial,
name text,
constraint org_pk primary key (id)
);
insert into org(name) values('name1'); -- works fine
insert into org(id, name) values(2, 'name2'); -- we don't use the sequence
insert into org(name) values('name3');-- sequence generates 2 again and insert fails
您需要找出是谁搞砸了 sequence/who 插入具有指定 ID 的行。并更新那个地方:
- 要么根本不指定 ID - serial 将为您完成此操作
- 或者在插入语句中显式访问序列:
insert into org(id, name) values(nextval('org_id_seq'), 'name2');
- 或者 update the sequence 在插入行后使用 setval()。