繁体   English   中英

Spring JPA-更新OnetoOne关系拥有权结束时的Stackoverflow异常

[英]Spring JPA - Stackoverflow exception on updating Owning end of OnetoOne relation

我有两个对象之间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;

}

现在,当我第一次将两者链接起来并将用户对象保存在回购中时,它可以正常工作,但是每次当我尝试更新用户对象时,它都会引发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;
}

@OneToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@JoinColumn(name = card_id)
private CardDO card;

休眠检查机制发现CardDO已更新,因此想更新User 然后是Card ,然后是User等。

我可以在注释中进行一些小的更改来运行您的代码,如下所示

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因为我从实体中删除了这些字段以便于观察问题。 通过邮递员存储数据后,您可以检查database中的carddo表是否存在user_id ,但不为null。

当您要进行更新时,请先使用CrudRepository 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);

之所以会出现stackoverflow异常,是因为在UserDOCardDO中都声明了@OneToOne映射。 所以当你exceute方法类似findOne() userRepository的,它会给你UserDO对象,但这个对象@oneToOne映射CardDo这样的对象CardDo将从数据库在同一时间被读取。 由于您的CardDo对象具有与UserDO @OneToOne映射,因此会发生同样的事情,即从数据库中获取UserDo对象。 这将造成无限循环并导致stackoverflow exception

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

private Long userId;

而不是执行@oneToOne映射:

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM