hibernate - 如何在使用spring数据和jpa hibernate保存具有一对多关系的实体时避免唯一键违规?
问题描述
我在保存两个具有“一对多”关系的实体时遇到问题。我得到了 ConstraintViolationException:
违反 UNIQUE KEY 约束“...”。无法在对象“dbo.classification”中插入重复键。重复键值为 (...)。
这是我的实体:
@Data
@Table(uniqueConstraints = {
@UniqueConstraint(columnNames = {"x", "y"})
})
@Entity
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@EqualsAndHashCode
public class Account {
@Id
@Nullable
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(unique = true, nullable = false)
private Integer id;
@Nonnull
@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "accountId")
private Set<Classification> classifications = new HashSet<>();
... rest fields
}
@Data
@Table(uniqueConstraints = {
@UniqueConstraint(columnNames = {"accountId", "anotherClassification"})
})
@Entity
@RequiredArgsConstructor
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@EqualsAndHashCode
public class Classification {
@Id
@Nullable
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(nullable = false)
private Integer id;
@Nonnull
@Column(nullable = false)
private Integer accountId;
@Nonnull
@Column(nullable = false)
@Enumerated(EnumType.STRING)
private AnotherClassification anotherClassification;
}
现在在前端我编辑分类,例如删除一个条目,所以在请求中我有一个包含一组分类的帐户,这些分类(当然)已经存储在数据库中。发生异常是因为无法再次添加。这是 AccountController 中的 @PutMapping 调用的服务方法:
@Transactional
@Override
public Account updateAccount(Integer id, AccountRequest accountRequest) {
return accountRepository.findById(id).map(account -> {
...
Set<Classification> classifications = new HashSet<>();
List<AnotherClassification> requestClassifications = accountRequest.getClassifications();
requestClassifications.forEach(ac -> {
Classification classification = new Classification(account.getId(), ac);
classifications.add(classification);
});
account.setClassifications(classifications);
return accountRepository.save(account);
}).orElseThrow(() -> idAccountNotFoundException(id));
}
我还尝试构建分类的手动清理(如下所示)并在更新方法的开头调用它,但它没有帮助。条目没有被删除,仍然是同样的错误。
@Override
public void clearClassificationsForAccount(Integer accountId) {
classificationRepo.deleteAllByAccountId(accountId);
classificationRepo.flush();
}
进行此类更新的正确方法是什么?
解决方案
为什么将分类映射为实体?您可以像这样使用元素集合映射:
@Nonnull
@ElementCollection
@Column(nullable = false)
@Enumerated(EnumType.STRING)
private Set<AnotherClassification> classifications = new HashSet<>();
这将创建一个只有两列的表,即您用于唯一约束的列。如果你真的必须使用代理 id,你可以尝试根据你的自然 id 来实现 equals-hashCode,即字段accountId
和anotherClassification
.
推荐阅读
- reactjs - 在 AWS 上运行的reachJS 应用程序存在延迟问题
- docker - Docker,MySQL - 错误 1045,拒绝使用 root 访问
- node.js - 通过 SASS 支持搜索带有 Typescript、SASS 和 CSS 模块的 React 组件库的 Webpack 配置
- javascript - 如何将包含对象数组的字符串转换回数组?
- vue.js - 页面重新加载后,在创建/挂载的钩子中未定义导入的 vue 实例
- javascript - 根据扩展名确定文件是图片还是视频不起作用
- django - Django基于下拉选择显示内容
- javascript - 如何构建一个包含项目 src 目录中数据的 json 文件?
- amazon-cloudformation - cdk:导入不相关的不同堆栈的输出
- swift - nil-coalescing 运算符“??”的作用是什么 在可选链中?