Spring boot @可嵌入复合密钥未在spring数据JPA中引发完整性异常
我使用了Spring数据JPA和@EmbeddeAbble来创建复合键。 一个基类BaseDate将由所有实体扩展 sysCreationDate将在插入期间生成(非空且不可更新) 保存用户第一次工作正常,但这里有3个问题-Spring boot @可嵌入复合密钥未在spring数据JPA中引发完整性异常,spring-boot,hibernate,spring-data-jpa,composite-key,embeddable,Spring Boot,Hibernate,Spring Data Jpa,Composite Key,Embeddable,我使用了Spring数据JPA和@EmbeddeAbble来创建复合键。 一个基类BaseDate将由所有实体扩展 sysCreationDate将在插入期间生成(非空且不可更新) 保存用户第一次工作正常,但这里有3个问题- 在第二次调用期间,它不是抛出异常,而是更新sysUpdateDate和userType 在第一次调用期间,sysUpdateDate不为null(@UpdateTimestamp) 在第二次响应调用期间,它将sysCreationDate返回为null 下面是代码- 可嵌入
@Embeddable
public class CompKey implements Serializable {
@Column(name ="USER_ID")
private String userId;
@Column(name ="USER_NAME")
private String userName;
public CompKey(String userId, String userName) {
super();
this.userId = userId;
this.userName = userName;
}
public CompKey() {
super();
}
//Getters /Setters /Equual and Hashcode
}
日期的基类
@MappedSuperclass
public abstract class BaseDate {
@CreationTimestamp
@Column(name = "SYS_CREATION_DATE", updatable=false, nullable=false)
private Calendar sysCreationDate;
@Column(name = "SYS_UPDATE_DATE")
@UpdateTimestamp
private Calendar sysUpdateDate;
public BaseDate(Calendar sysCreationDate, Calendar sysUpdateDate) {
this.sysCreationDate = sysCreationDate;
this.sysUpdateDate = sysUpdateDate;
}
public BaseDate() {
}
//Getters and Setters
}
实体类
@Entity
public class User extends BaseDate{
@Column(name = "USER_TYPE")
private String userType;
@EmbeddedId
private CompKey compkey;
public User() {
super();
}
public User(Calendar sysCreationDate, Calendar sysUpdateDate, String userType, CompKey compkey) {
super(sysCreationDate, sysUpdateDate);
this.userType = userType;
this.compkey = compkey;
}
//Getters and setters
}
回购-
@Repository
public interface UserRepo extends CrudRepository<User, CompKey> {
}
产出1)-
提前感谢不会引发完整性约束冲突异常,因为您的Spring存储库只是更新对象 Spring存储库不区分插入和更新。只有一种通用方法--
保存
。默认情况下,仅当主键为null
或0
时,此方法才会保留(插入)新对象;否则,它将合并(更新)到现有对象中。您总是有一个主键集,因此它总是调用merge
,这会在第二次更新
它在SimpleParepository
中的基本实现如下所示:
@Transactional
公共存储(S实体){
Assert.notNull(实体,“实体不能为null”);
if(this.entityInformation.isNew(entity)){
这个.em.persist(实体);
返回实体;
}否则{
返回此.em.merge(实体);
}
}
关键部分是isNew
方法,其默认实现如下:
public boolean isNew(T实体){
ID=getId(实体);
类idType=getIdType();
如果(!idType.isPrimitive()){
返回id==null;
}
if(id instanceof Number){
返回((数字)id).longValue()==0L;
}
抛出新的IllegalArgumentException(String.format(“不支持的基元id类型%s!”,idType));
}
可用的解决方案有:
EntityManager
isNew
,以通知Spring存储库您的对象是新的还是已经被持久化long
,@GeneratedValue
)和唯一约束find
,检查数据库中是否存在对象。但是,此解决方案容易出现竞争问题(创建新对象的两个并发REST请求,都调用find
,都接收null,因此都保存,一个数据丢失/覆盖)
对于@UpdateTimestamp,您已经得到了一条评论,对于@CreationTimestamp null,请发布您的控制器。您可以发布您的控制器保存/更新用户的方法吗?第2点:根据这一点,它旨在在持久化实体期间设置
@UpdateTimestamp
。通过阅读此答案:我可以想象,问题3属于问题1:实体得到更新,在请求数据中,sysCreationDate
字段是null
,因此该字段被覆盖。。。通过null
。可能的解决方案:将该列标记为“不可更新”(@column(/*…*/updateable=false)
)谢谢您的快速回答,我已经用服务和控制器更新了代码
@Service
public class UserService {
@Autowired
UserRepo userRepo;
public User saveUser(User user) {
return userRepo.save(user);
}
public Optional<User> getUser(CompKey key) {
return userRepo.findById(key);
}
}
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
UserService userService;
@PostMapping("/save")
public User saveUser(@RequestBody User user) {
return userService.saveUser(user);
}
@GetMapping("/get")
public Optional<User> getUser(@RequestBody CompKey key) {
return userService.getUser(key);
}
{
"userType": "K",
"compkey": {
"userId": "1002",
"userName": "ASDF"
}
}
{
"sysCreationDate": "2021-01-08T18:09:28.802+00:00",
"sysUpdateDate": "2021-01-08T18:09:28.802+00:00",
"userType": "K",
"compkey": {
"userId": "1002",
"userName": "ASDF"
}
{
"sysCreationDate": null,
"sysUpdateDate": "2021-01-08T18:10:43.206+00:00",
"userType": "K",
"compkey": {
"userId": "1002",
"userName": "ASDF"
}
}