Spring boot @可嵌入复合密钥未在spring数据JPA中引发完整性异常

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 下面是代码- 可嵌入

我使用了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
  • 从Spring实现接口并实现您自己的
    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"
        }
    }