【问题标题】:TransientPropertyValueException in Spring BootSpring Boot 中的 TransientPropertyValueException
【发布时间】:2019-10-04 03:57:19
【问题描述】:

我有LoginCredential 类:

@Data
@Entity
public class LoginCredential implements Serializable {
   @Id
   @GeneratedValue(strategy = GenerationType.AUTO)
   Long userID;
   String eMail;
   String passwordHash;
   @GeneratedValue(strategy = GenerationType.AUTO)
   @OneToOne(mappedBy = "loginCredential", fetch = FetchType.LAZY)
   User user;
}

这是我的User 课程:

@Data
@Entity
public class User {
   @Id
   @GeneratedValue(strategy = GenerationType.AUTO)
   Long userID;
   @OneToOne(fetch = FetchType.LAZY,targetEntity = LoginCredential.class)
   @JoinColumn(name = "userID",referencedColumnName = "userID")
   private LoginCredential loginCredential;
}

而我的LoginCredentialControllerPOST 方法很简单:

@PostMapping("/login")
LoginCredential newLoginCredential(@RequestBody LoginCredential newLoginCredential) {
    logger.debug(newLoginCredential);
    LoginCredential a=repository.save(newLoginCredential);
    logger.debug(a);
    return a;
}

现在当我尝试这个命令时:curl -X POST -H "Content-Type: application/json" -d "{ \"email\": \"1\"}" http://localhost:8080/login

我得到一个LoginCredential 没有任何错误,要提到的user 字段是null

这就是我尝试这个命令的原因curl -X POST -H "Content-Type: application/json" -d "{ \"email\": \"1\",\"user\":{} }" http://localhost:8080/login

这给了我错误的说法:

{
  "status" : 500,
  "error" : "Internal Server Error",
  "message" : "org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : com.mua.cse616.Model.LoginCredential.user -> com.mua.cse616.Model.User; org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : com.mua.cse616.Model.LoginCredential.user -> com.mua.cse616.Model.User",
  "trace":....
}

Application.properties

pom.xml

【问题讨论】:

    标签: spring hibernate spring-boot jpa


    【解决方案1】:

    你有三个问题

    1. 缺少cascade 选项来触发User 实体创建从LoginCredential 保存。
    2. User 上缺少@MapsId 注释以便它们共享相同的ID,否则LoginCredential 及其创建的User 将具有不同的ID 值,因为它们的@GeneratedValue(strategy = GenerationType.AUTO) 列上有@GeneratedValue(strategy = GenerationType.AUTO)
    3. 不设置关系的双方...

    要解决所有问题,您需要将实体更改为以下(我还删除了一些无用的注释和值);

    @Data
    @Entity
    public class User {
    
        @Id
        Long userID;
    
        @JsonBackReference
        @MapsId
        @OneToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "userID", referencedColumnName = "userID")
        @ToString.Exclude
        private LoginCredential loginCredential;
    }
    

    @Data
    @Entity
    public class LoginCredential {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        Long userID;
        String eMail;
        String passwordHash;
    
        @JsonManagedReference
        @OneToOne(mappedBy = "loginCredential", fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
        private User user;
    }
    

    还需要在最终确定端点之前设置关系的双方;

    Optional.ofNullable(loginCredential.getUser())
            .ifPresent(user -> user.setLoginCredential(loginCredential));
    loginCredentialRepo.save(loginCredential);
    

    【讨论】:

    • 不工作 - 当我尝试 curl -X POST -H "Content-Type: application/json" -d "{ \"email\": \"1\"}" http://localhost:8080/login 它给了我 "尝试从空的一对一属性 [com.mua.cse616.Model.LoginCredential.user] 分配 id;嵌套异常是 org.hibernate.id.IdentifierGenerationException: attempted to assign id from null 一对一属性 [com.mua.cse616.Model.LoginCredential.user]"
    • @MaifeeUlAsad 啊当然,那是由于无限递归循环。更新答案,检查@JsonBackReference & @JsonManagedReference。当您尝试打印LoginCredential 时,它会尝试打印User,然后User 尝试再次打印LoginCredential,然后尝试再次打印User,创建这个无穷无尽的递归调用堆栈,直到它杀死所有东西.
    • @MaifeeUlAsad 在我的本地没有发生,您确定您从我的答案中复制了所有内容吗?当我通过{"email": "hey"} 时,我得到了{"userID":1,"passwordHash":null,"user":null,"email":"hey"}。您似乎在LoginCredential 中插入了一个空的User,要么将其保留为空,要么执行user.setLoginCredential()
    • @MaifeeUlAsad 不不,那是因为我告诉过你的堆栈溢出情况,你为什么要打印一个实体?只需在您的控制器方法中返回它,就可以了。否则尝试使用@Autowired ObjectMapper objectMapper 打印它,使用System.out.println(objectMapper.writeValueAsString(loginCredential))。通过 @JsonBackReference@JsonManagedReference 注释,杰克逊修复了递归问题。但是toString() 仍然会抛出该错误。
    • @MaifeeUlAsad 你已经解决了所有问题,只是 toString() 问题,只需将来自 lombok 的 @ToString(exclude = "loginCredential") 放在 User 类上
    【解决方案2】:

    用户对象不需要@GeneratedValue

    为 OneToOne 添加CascadeType

    通过这样做,您告诉 hibernate 在保存 LoginCredential 时将用户保存到数据库中。

    @Data
    @Entity
    public class LoginCredential implements Serializable {
    
       @Id
       @GeneratedValue(strategy = GenerationType.AUTO)
       Long userID;
    
       String eMail;
    
       String passwordHash;
    
       @OneToOne(cascade = {CascadeType.ALL}, mappedBy = "loginCredential", fetch = FetchType.LAZY)
       User user;
    }
    

    请参考more detail

    【讨论】:

    • 蓝色是我收到的,红色保存,黄色获取..用户为空..请参阅here
    【解决方案3】:

    使用户类也可序列化。

    【讨论】:

    • 用户在登录凭证对象中不应为空。当您在 newLoginCredential(@RequestBody LoginCredential newLoginCredential) 方法中接收时
    猜你喜欢
    • 1970-01-01
    • 2022-08-11
    • 2017-09-27
    • 2016-10-10
    • 2022-12-14
    • 1970-01-01
    • 2018-05-17
    • 2017-04-05
    • 2017-09-28
    相关资源
    最近更新 更多