Not-null 属性 在删除具有许多关系的实体时引用空值或瞬态值

Not-null property references a null or transient value when delete entity with many relations

我的 AppUser 可以有很多 BookCards,它可以有很多书。我需要删除我的 AppUser 和他的 BookCards 实体,但书本身没有变化。但是当我尝试用这种方法做的时候

@Transactional
    public void deleteUser(Long id) {
        List<BookCard> bookCards = bookCardService.findAllUserBookCards(id);

        if (!bookCards.isEmpty()) {
            bookCardService.deleteBookCardByUserId(id);
        }

        userRepository.deleteById(id);
    }

我收到一个错误

org.hibernate.PropertyValueException: not-null property references a null or transient value : kpi.diploma.ovcharenko.entity.card.BookCard.book

这是我的 AppUser、Book 和 BookCard classes

@Getter
@Setter
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Builder(toBuilder = true)
@Table(uniqueConstraints = @UniqueConstraint(columnNames = "email"), name = "library_user")
public class AppUser {
    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    // other fields 

    @EqualsAndHashCode.Exclude
    @OnDelete(action = OnDeleteAction.CASCADE)
    @OneToMany(mappedBy = "book", fetch = FetchType.EAGER)
    private Set<BookCard> bookCards = new HashSet<>();

    // getters and setters
@Getter
@Setter
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Builder(toBuilder = true)
@Table(name = "booking_card")
@TypeDef(
        name = "pgsql_enum",
        typeClass = PostgreSQLEnumType.class
)
public class BookCard {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;

    @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @OnDelete(action = OnDeleteAction.CASCADE)
    @JoinColumn(name = "user_id", nullable = false)
    private AppUser user;

    @OnDelete(action = OnDeleteAction.NO_ACTION)
    @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER, optional = false)
    @JoinColumn(name = "book_id")
    private Book book;

    // getters and setters
@Getter
@Setter
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Builder(toBuilder = true)
@Table(name = "books")
public class Book {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "idbooks", insertable = false, updatable = false)
    private Long id;

    // other fields 

    @EqualsAndHashCode.Exclude
    @OnDelete(action = OnDeleteAction.NO_ACTION)
    @OneToMany(mappedBy = "book", fetch = FetchType.EAGER)
    private Set<BookCard> bookCards = new HashSet<>();
    
    // getters and setters

如你所见,我的 Book class 有 Set bookCards 字段,只是为了低估我尝试添加许多注释,例如 @OnDelete(action = OnDeleteAction.CASCADE),相信他们能解决我的问题

据我了解,删除 AppUser 和他的 BookCard 没有问题,但是因为 BookCard 与 Book 有关系,hibernate 不明白如何处理 Book。那么我怎样才能让我的程序清楚我只需要删除 BookCards 实体,而不是触摸书本身呢?非常感谢

对于未来的读者,谁会遇到类似的错误。我解决了我这样改变实体的问题

@Getter
@Setter
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Builder(toBuilder = true)
@Table(uniqueConstraints = @UniqueConstraint(columnNames = "email"), name = "library_user")
public class AppUser {
    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    // other fields 

    @EqualsAndHashCode.Exclude
    @OnDelete(action = OnDeleteAction.CASCADE)
    @OneToMany(mappedBy = "user", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    private Set<BookCard> bookCards = new HashSet<>();

    // getters and setters
@Getter
@Setter
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Builder(toBuilder = true)
@Table(name = "booking_card")
@TypeDef(
        name = "pgsql_enum",
        typeClass = PostgreSQLEnumType.class
)
public class BookCard {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;

    @ManyToOne(cascade = CascadeType.REFRESH, fetch = FetchType.EAGER)
    @JoinColumn(name = "user_id", nullable = false)
    private AppUser user;

    @ManyToOne(cascade = CascadeType.REFRESH, fetch = FetchType.EAGER)
    @JoinColumn(name = "book_id")
    private Book book;

    // getters and setters
@Getter
@Setter
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Builder(toBuilder = true)
@Table(name = "books")
public class Book {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "idbooks", insertable = false, updatable = false)
    private Long id;

    // other fields 

    @EqualsAndHashCode.Exclude
    @OnDelete(action = OnDeleteAction.CASCADE)
    @OneToMany(mappedBy = "user", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    private Set<BookCard> bookCards = new HashSet<>();
    
    // getters and setters

这个注解解决了这个问题 org.hibernate.PropertyValueException: not-null property references a null or transient value : kpi.diploma.ovcharenko.entity.card.BookCard.book