【发布时间】:2020-10-22 21:39:56
【问题描述】:
我正在使用 Spring MVC 和 EclipseLink 2.5.2 在 mysql 数据库上开发大型代码库。数据库及其结构是直接创建的,而不是通过任何代码优先的方法。我的问题涉及一对多关系中的 2 个表。
CREATE TABLE ROLE (
ID BIGINT(20) PRIMARY KEY,
-- OTHER FIELDS --
);
CREATE TABLE ROLE_DOMAIN (
ID BIGINT(20) PRIMARY KEY,
ROLE_ID BIGINT(20) NOT NULL,
DOMAIN VARCHAR(255) NOT NULL
-- OTHER FIELDS --
);
ALTER TABLE ROLE_DOMAIN ADD CONSTRAINT FK_ROLE_DOMAIN_ROLE_ID FOREIGN KEY (ROLE_ID) REFERENCES ROLE_BASE (ID) ON DELETE CASCADE;
ALTER TABLE ROLE_DOMAIN ADD CONSTRAINT UQ_ROLE_DOMAIN_ROLE_ID_DOMAIN UNIQUE (ROLE_ID, DOMAIN);
在 java 中,这就是我配置两个实体的方式。
@Entity
public class Role {
private Long id;
private Set<RoleDomain> roleDomains = new HashSet<>();
@Id
@TableGenerator(name = "ROLE.ID", allocationSize = 1, initialValue = 1)
@GeneratedValue(strategy = GenerationType.TABLE, generator = "ROLE.ID")
public Long getID() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "ROLE_ID", referencedColumnName = "ID", insertable = false, updatable = false)
public Set<RoleDomain> getRoleDomains() {
return roleDomains;
}
public void setRoleDomains(Set<RoleDomain> roleDomains) {
this.roleDomains = roleDomains;
}
}
@Entity
@Table(name = "ROLE_DOMAIN")
public class RoleDomain {
private Long id;
private Long roleId;
private String domain;
@Id
@TableGenerator(name = "ROLE_DOMAIN.ID", allocationSize = 1, initialValue = 1)
@GeneratedValue(strategy = GenerationType.TABLE, generator = "ROLE_DOMAIN.ID")
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@Column(name = "ROLE_ID", nullable = false)
public Long getRoleId() {
return roleId;
}
public void setRoleId(Long roleId) {
this.roleId = roleId;
}
@Column(name = "DOMAIN", length = 255)
public String getDomain() {
return domain;
}
public void setDomain(String domain) {
this.domain = domain;
}
}
假设在这个表结构中,我已经在 ROLE 中有一条记录,并且在 ROLE_DOMAIN 中有一条引用它的记录,转换为一个名为 myRole 的 Role 对象,其中包含 roleDomains 中的 RoleDomain。
现在,当我添加一个新的RoleDomain 并使用这样的 spring 数据存储库进行保存时:
myRole.add(new RoleDomain("some string"));
roleRepository.save(myRole);
由于重复插入违反了我对数据库中 ROLE_ID 和 DOMAIN 的唯一约束,我得到了一个例外。
[EL Warning]: 2020-10-22 14:53:22.405--UnitOfWork(994047815)--Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.6.8.v20190620-d6443d8be7): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '198732-some string' for key 'UQ_ROLE_DOMAIN_ROLE_ID_DOMAIN'
Error Code: 1062
Call: INSERT INTO ROLE_DOMAIN (ID, DOMAIN, ROLE_ID) VALUES (?, ?, ?)
bind => [27, some other string, 198732]
关于这个问题最奇怪的是,如果我从数据库中删除唯一约束(注意:保持 java 注释配置完全相同。字面上只是数据库中的“DROP CONSTRAINT ...”)然后save通话效果很好。它不会在 ROLE_DOMAIN 中创建重复项。它完全按照它应该做的,只是将新记录添加到 ROLE_DOMAIN。
我不明白 db 中的唯一约束如何导致 eclipselink 不一致地执行此操作。我有什么配置错误吗?谢谢。
编辑:
我刚刚尝试用这个替换RoleDomain 类上的@Table 注释:
@Table(name = "ROLE_DOMAIN", uniqueConstraints =
@UniqueConstraint(columnNames = {"ROLE_ID", "DOMAIN"}))
它没有改变任何东西。
【问题讨论】:
-
打开 SQL 日志记录,因为 EclipseLink 不知道该约束,无论有无它在数据库中的操作都相同。你怎么确定没有这个约束没有重复?如果您在类上错误地实现了自己的 equals 方法,您的集合将不会显示。
-
我知道它不会在没有约束的情况下插入重复项,因为我可以 SELECT * FROM ROLE_DOMAIN 并且没有重复项。
-
@Chris 在查看了在没有唯一约束的情况下会发生什么之后,它实际上是从 Set 中插入所有记录,无论它们是否重复,但随后它会从之后的桌子,这很奇怪。
-
我认为您可能需要仔细查看时间。我怀疑您在数据库中有条目,添加到重复记录的集合中-但是将其定义为重复记录的是什么? role_id 和 domain 不是标识符,因此您引用了一个全新的行,并且正在删除现有的行。所以这不是更新情况 - 为什么要在新实体已经存在的情况下放入相同的数据?
-
> 为什么要在新实体已经存在的情况下放入相同的数据? -----我不明白你在这里的意思。我从 Role 对象中的数据库中提取了一组 RoleDomain。如果我向其中添加新记录并保存,我希望 eclipselink 知道不必重新插入已经存在的其他记录。
标签: java mysql jpa eclipselink