Java Spring JPA-更新OneTONE关系的拥有端时出现Stackoverflow异常
我有两个对象之间的一对一映射 UserDO.java:Java Spring JPA-更新OneTONE关系的拥有端时出现Stackoverflow异常,java,spring,hibernate,jpa,Java,Spring,Hibernate,Jpa,我有两个对象之间的一对一映射 UserDO.java: public class UserDO { @Id @GeneratedValue private Long id; @Column(nullable = false) @NotNull(message = "Username can not be null!") private String username; @Column(nullable = false) @No
public class UserDO {
@Id
@GeneratedValue
private Long id;
@Column(nullable = false)
@NotNull(message = "Username can not be null!")
private String username;
@Column(nullable = false)
@NotNull(message = "Password can not be null!")
private String password;
@Column(nullable = false)
private Boolean deleted = false;
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private OnlineStatus onlineStatus;
@Column(nullable = false)
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
private ZonedDateTime dateCreated = ZonedDateTime.now();
@OneToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private CardDO card;
}
public class CardDO {
@Id
@GeneratedValue
@Column(name="id")
private Long id;
@Column(nullable = false)
private Long pan;
@Column(nullable = false)
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
private LocalDate expirationDate;
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private CardType cardType;
@Column(nullable = false)
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
private ZonedDateTime dateCreated = ZonedDateTime.now();
@OneToOne(mappedBy="card")
private UserDO user;
}
CardDO.java:
public class UserDO {
@Id
@GeneratedValue
private Long id;
@Column(nullable = false)
@NotNull(message = "Username can not be null!")
private String username;
@Column(nullable = false)
@NotNull(message = "Password can not be null!")
private String password;
@Column(nullable = false)
private Boolean deleted = false;
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private OnlineStatus onlineStatus;
@Column(nullable = false)
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
private ZonedDateTime dateCreated = ZonedDateTime.now();
@OneToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private CardDO card;
}
public class CardDO {
@Id
@GeneratedValue
@Column(name="id")
private Long id;
@Column(nullable = false)
private Long pan;
@Column(nullable = false)
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
private LocalDate expirationDate;
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private CardType cardType;
@Column(nullable = false)
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
private ZonedDateTime dateCreated = ZonedDateTime.now();
@OneToOne(mappedBy="card")
private UserDO user;
}
现在,当我第一次链接这两个对象并将用户对象保存在repo中时,它可以正常工作,但每次我尝试更新用户对象时,它都会抛出StackOverFlow异常
用于将两个对象链接起来的代码:
userDO.setCard(cardDO);
userRepository.save(userDO);
更新代码:
UserDO userDO = findUser(userId);
userDO.setOnlineStatus(onlineStatus);
userRepository.save(userDO);
有什么不对劲吗 您需要设置关系的所有者站点:
public class CardDO {
//skipped
@OneToOne(mappedBy="card", insertable = false, updateable = false)
private UserDO user;
}
及
Hibernate脏检查机制会看到
CardDO
已更新,因此希望更新User
。然后是卡
,然后是用户
等等。我可以运行您的代码,只需在注释中做一些小的更改,如下所示
{
"username":"vishal",
"password":"123456",
"onlineStatus":"OFFLINE",
"cardDO" :{
"cardType":"NEW",
"pan":"2"
}
}
UserDO.java
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.validation.constraints.NotNull;
import com.fasterxml.jackson.annotation.JsonManagedReference;
@Entity
public class UserDO {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column(nullable = false)
@NotNull(message = "Username can not be null!")
private String username;
@Column(nullable = false)
@NotNull(message = "Password can not be null!")
private String password;
@Column(nullable = false)
private Boolean deleted = false;
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private OnlineStatus onlineStatus;
@Column(nullable = false)
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
private ZonedDateTime dateCreated = ZonedDateTime.now();
@JsonManagedReference
@OneToOne(cascade = CascadeType.ALL)
private CardDO card;
}
import java.time.LocalDate;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import org.springframework.format.annotation.DateTimeFormat;
import com.fasterxml.jackson.annotation.JsonBackReference;
@Entity
public class CardDO {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column(nullable = false)
private Long pan;
@Column(nullable = false)
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
private LocalDate expirationDate;
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private CardType cardType;
@Column(nullable = false)
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
private ZonedDateTime dateCreated = ZonedDateTime.now();
@JsonBackReference
@OneToOne
private UserDO user;
}
CardDO.java
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.validation.constraints.NotNull;
import com.fasterxml.jackson.annotation.JsonManagedReference;
@Entity
public class UserDO {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column(nullable = false)
@NotNull(message = "Username can not be null!")
private String username;
@Column(nullable = false)
@NotNull(message = "Password can not be null!")
private String password;
@Column(nullable = false)
private Boolean deleted = false;
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private OnlineStatus onlineStatus;
@Column(nullable = false)
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
private ZonedDateTime dateCreated = ZonedDateTime.now();
@JsonManagedReference
@OneToOne(cascade = CascadeType.ALL)
private CardDO card;
}
import java.time.LocalDate;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import org.springframework.format.annotation.DateTimeFormat;
import com.fasterxml.jackson.annotation.JsonBackReference;
@Entity
public class CardDO {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column(nullable = false)
private Long pan;
@Column(nullable = false)
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
private LocalDate expirationDate;
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private CardType cardType;
@Column(nullable = false)
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
private ZonedDateTime dateCreated = ZonedDateTime.now();
@JsonBackReference
@OneToOne
private UserDO user;
}
如果使用下面的代码保存数据,则无法将用户ID保存在您的cartdo
实体中
userDO.setCard(cardDO);
userRepository.save(userDO);
UserDO userDO = repository.findOne(userId);
CardDO card = userDO.getCard();
card.setCardType(CardType.OLD);
userDO.setOnlineStatus(OnlineStatus.ONLINE);
userDO.setCard(card);
repository.save(userDO);
如果您想将userId
也保存在cartdo
实体中,您必须通过postman
点击rest api,并在api中发送数据,如下所示
{
"username":"vishal",
"password":"123456",
"onlineStatus":"OFFLINE",
"cardDO" :{
"cardType":"NEW",
"pan":"2"
}
}
我没有发送datecreated
和expirationdate
的值,因为我从实体中删除了这些字段以便于观察问题。
通过邮递员存储数据后,您可以检查数据库中的carddo
表
中有一个用户id
,并且该id不为空
当您要使用crudepository
的findOne
方法更新第一个find时,请按下面的代码保存。在这段代码中,我还能够更新属于CardDO.java
实体的cardType
userDO.setCard(cardDO);
userRepository.save(userDO);
UserDO userDO = repository.findOne(userId);
CardDO card = userDO.getCard();
card.setCardType(CardType.OLD);
userDO.setOnlineStatus(OnlineStatus.ONLINE);
userDO.setCard(card);
repository.save(userDO);
您正在获取stackoverflow异常,因为您已在
UserDO
和CardDO
中为彼此声明了@OneToOne
映射。那么什么时候
您可以使用userRepository的findOne()
方法,它将为您提供UserDO
对象,但此对象具有@oneToOne
到CardDo
的映射,因此CardDo
的对象将同时从数据库中获取。由于您的CardDo
对象具有@OneToOne
与UserDO
的映射,因此同样的情况也会发生,即UserDO
对象从数据库中获取。这将形成一个无限循环,并导致堆栈溢出异常
我建议在您的CardDo
实体中使用以下代码:
private Long userId;
不要进行@oneToOne
映射:
@OneToOne(mappedBy="card")
private UserDO user;
您可以显示异常的stacktrace吗?当我这样做时,IDE抱怨:无法解析方法“insertable”/“Updateable”。所以我尝试了这个:@OneToOne(mappedBy=“card”)@JoinColumn(insertable=false,updateable=false)private-UserDO-user;仍在引发异常:java.lang.StackOverflower错误:在ee.neocard.domainobject.UserDO.hashCode(UserDO.java:12)处为null ~[classes/:na]在ee.neocard.domainobject.CardDO.hashCode(CardDO.java:16)~[classes/:na]我发布了更新的答案,请检查它,让我知道它是否工作。现在你可以使用此代码,因为现在我可以保存和更新这两个实体。有了此功能,CardDO甚至似乎没有保留对UserDO的引用。每次我向用户添加一张卡时,我都会检查它是否分配给了任何其他用户,现在它总是返回null。你的意思是卡内实体userId总是null。如果你想将userId也保存在你的cartdo实体中,你必须通过postman点击你的restapi。我已经创建了一个用于存储数据的控制器和RESTAPI。我只在更新UserDO对象时得到StackOverflow。我可以毫无错误地获取包含CardDO的UserDO对象。