单向One To Many关联添加方法触发Proxy调用异常求助
错误信息
at jdk.proxy2/jdk.proxy2.$Proxy106.save(Unknown Source) ~[na:na] at
com.example.OneToManyUniDirectional.service.concretes.AuthorManager.addAuthor(AuthorManager.java:50)
~[classes/:na] at
com.example.OneToManyUniDirectional.controller.AuthorController.addAuthors(AuthorController.java:21)
~[classes/:an]
相关代码
Author实体类
public class Author { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(name="name") private String firstName; @Column(name="surname") private String lastName; @OneToMany(cascade=CascadeType.MERGE,fetch = FetchType.EAGER) private Set<Book> books=new HashSet<>(); }
Book实体类
public class Book { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String isbn; @Column(name="book_name") private String name; }
addAuthor业务方法
@Override public void addAuthor(CreateAuthor createAuthor){ Author author=new Author(); author.setFirstName(createAuthor.getFirstName()); author.setLastName(createAuthor.getLastName()); if(this.bookRepository.findById(createAuthor.getBook().getId()).isPresent()){ UpdateBook updateBook=new UpdateBook(); updateBook.setName(createAuthor.getBook().getName()); updateBook.setIsbn(createAuthor.getBook().getIsbn()); this.bookService.updateBook(updateBook,createAuthor.getBook().getId()); } else{ CreateBook createBook=new CreateBook(); createBook.setName(createAuthor.getBook().getName()); createBook.setIsbn(createAuthor.getBook().getIsbn()); this.bookService.addBook(createBook); author.getBooks().add(createAuthor.getBook()); } this.authorRepository.save(author); }
CreateAuthor DTO
@Data @NoArgsConstructor @AllArgsConstructor public class CreateAuthor { private String firstName; private String lastName; private Book book; }
Controller代码
@PostMapping("/authors") public void addAuthors(@RequestBody CreateAuthor createAuthor){ this.authorService.addAuthor(createAuthor); }
注:CreateBook和UpdateBook类不含id属性
报错原因分析
- 单向OneToMany关联配置缺失:
@OneToMany未指定@JoinColumn,Hibernate会自动创建中间表维护关联,但此时关联的Book实体状态不符合要求,导致保存Author时关联处理失败。 - 游离态实体操作问题:CreateAuthor中传递的Book是从请求体反序列化的游离态对象,
cascade=CascadeType.MERGE仅支持合并持久态或托管态实体,无法处理游离态,触发代理类的save方法报错。 - 新增Book后关联对象错误:新增Book时,调用
bookService.addBook(createBook)后,直接将传入的未持久化Book对象添加到Author的集合,该对象未被Hibernate托管,保存Author时无法正确关联。
解决办法
1. 修正OneToMany关联配置
给Author的@OneToMany添加@JoinColumn,明确外键列,避免自动生成中间表,同时调整cascade类型覆盖新增场景:
@OneToMany(cascade = {CascadeType.MERGE, CascadeType.PERSIST}, fetch = FetchType.EAGER) @JoinColumn(name = "author_id") // 指定Book表中的外键列 private Set<Book> books = new HashSet<>();
2. 重构CreateAuthor的字段类型
不要直接传递Book实体,改用CreateBook DTO,避免传递游离态实体:
@Data @NoArgsConstructor @AllArgsConstructor public class CreateAuthor { private String firstName; private String lastName; private CreateBook book; // 替换为CreateBook DTO }
3. 修正addAuthor方法逻辑
确保添加到Author集合的是持久态Book对象,同时简化逻辑:
@Override public void addAuthor(CreateAuthor createAuthor){ Author author = new Author(); author.setFirstName(createAuthor.getFirstName()); author.setLastName(createAuthor.getLastName()); CreateBook createBook = createAuthor.getBook(); Book book; // 检查Book是否存在(需给CreateBook添加id字段支持更新场景) if (createBook.getId() != null && bookRepository.findById(createBook.getId()).isPresent()) { // 更新现有Book book = bookRepository.findById(createBook.getId()).get(); book.setName(createBook.getName()); book.setIsbn(createBook.getIsbn()); bookRepository.save(book); } else { // 新增Book并获取持久态对象 book = new Book(); book.setName(createBook.getName()); book.setIsbn(createBook.getIsbn()); book = bookRepository.save(book); } // 将持久态Book添加到Author集合 author.getBooks().add(book); authorRepository.save(author); }
4. 调整BookService的addBook方法
确保addBook返回持久化后的Book对象:
public Book addBook(CreateBook createBook) { Book book = new Book(); book.setName(createBook.getName()); book.setIsbn(createBook.getIsbn()); return bookRepository.save(book); }
内容的提问来源于stack exchange,提问作者samaritan
相关产品推荐
相关产品推荐

