Java Spring JPA-更新OneTONE关系的拥有端时出现Stackoverflow异常

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

我有两个对象之间的一对一映射

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)
    @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对象。