Spring JPA - 更新OnetoOne关系的Owning结束时的Stackoverflow异常

时间:2017-08-11 10:18:34

标签: java spring hibernate jpa

我在两个对象之间有一个one-to-one映射。

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;

}

CardDO.java:

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异常。

用于链接2个对象的代码:

userDO.setCard(cardDO);
userRepository.save(userDO);

更新代码:

UserDO userDO = findUser(userId);
userDO.setOnlineStatus(onlineStatus);
userRepository.save(userDO);

可能有什么不对?

2 个答案:

答案 0 :(得分:0)

我可以通过下面的注释中的一些细微更改来运行您的代码

<强> 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;
}

<强> CardDO.java

    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;
}

如果使用以下代码保存数据userId无法保存在cartdo实体中

userDO.setCard(cardDO);
userRepository.save(userDO);

如果您想在userId实体中保存cartdo,则必须通过postman点击您的休息API并在您的api中发送数据

{
    "username":"vishal",
    "password":"123456",
    "onlineStatus":"OFFLINE",
    "cardDO" :{
        "cardType":"NEW",
        "pan":"2"
    }
}

我没有发送datecreatedexpirationdate的值,因为我从我的实体中删除了这些字段,以便于查看问题。 通过邮递员存储数据后,您可以查看carddo中的database表,其中有一个user_id,且该值不为空。

当您要使用findOne CrudRepository方法进行更新时,请先保存,然后保存如下代码。在此代码中,我还可以更新属于cardType实体

CardDO.java
UserDO userDO = repository.findOne(userId);
    CardDO card = userDO.getCard();
    card.setCardType(CardType.OLD);
    userDO.setOnlineStatus(OnlineStatus.ONLINE);
    userDO.setCard(card);
    repository.save(userDO);

答案 1 :(得分:0)

您正在获得stackoverflow异常,因为您已在@OneToOneUserDO中相互声明了CardDO映射。所以当 除了userRepository的findOne()方法之外,它会为您提供UserDO个对象,但此对象的@oneToOne映射到CardDo,因此CardDo的对象将被提取同时从数据库。由于您的CardDo对象与@OneToOne进行UserDO映射,因此同样的事情发生,即从数据库中获取UserDo对象。这将产生无限循环并导致stackoverflow exception

我建议您在CardDo实体中使用以下代码:

private Long userId;

而不是@oneToOne映射:

@OneToOne(mappedBy="card")
private UserDO user;