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
我的 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