Search code examples
springpostgresqlspring-boothibernateone-to-many

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


I have AppUser that can have many BookCards, which can have many Books. I need to delete my AppUser with his BookCards entities, but no change for the book itself. But when I tried do it by this method

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

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

        userRepository.deleteById(id);
    }

I got an error

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

This are my AppUser, Book And 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

As you can see, my Book class has field of Set bookCards, its just for understating that I tried to add many annotations, like @OnDelete(action = OnDeleteAction.CASCADE), believe that them can solve my problem

As I understand, its no problem with deleteting AppUser and his BookCard, but because BookCard has a relation to the Book, hibernate doesn't understand what to do with the Book. So how can I make it clear to my program that I just need to delete BookCards enetities, but do not touch the book itself? Thank you very much


Solution

  • For the future readers, who can face a similar error. I solve my problem changing entities like this

    @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
    

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