Spring Data JPA:查询ManyToMany,如何从映射类中获取数据?
Posted
技术标签:
【中文标题】Spring Data JPA:查询ManyToMany,如何从映射类中获取数据?【英文标题】:Spring Data JPA: query ManyToMany, How Can I get the data from mapped Class? 【发布时间】:2017-12-12 19:11:46 【问题描述】:我正在使用 Spring Data JPA 和 H2 数据库开发 Spring Boot 应用程序。我正在使用 spring-data-jpa。 当我使用 ManyToMany 映射器类来获取另一个类的数据时。但我发现它是NULL。
The code is on github
书籍类
@Entity
public class Book implements Serializable
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue
private Integer id;
private String name;
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name = "BOOK_AUTHOR", joinColumns =
@JoinColumn(name = "BOOK_ID", referencedColumnName = "ID"), inverseJoinColumns =
@JoinColumn(name = "AUTHOR_ID", referencedColumnName = "ID"))
private Set<Author> authors;
public Book()
super();
public Book(String name)
super();
this.name = name;
this.authors = new HashSet<>();
public Book(String name, Set<Author> authors)
super();
this.name = name;
this.authors = authors;
public Integer getId()
return id;
public void setId(Integer id)
this.id = id;
public String getName()
return name;
public void setName(String name)
this.name = name;
public Set<Author> getAuthors()
return authors;
public void setAuthors(Set<Author> authors)
this.authors = authors;
@Override
public String toString()
return String.format("Book [id=%s, name=%s, authors=%s]", id, name, authors);
作者.class
@Entity
public class Author implements Serializable
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue
private Integer id;
private String name;
@ManyToMany(mappedBy = "authors")
private Set<Book> books;
//why CAN NOT GET the data when using these code else ?
// @ManyToMany(cascade = CascadeType.ALL)
// @JoinTable(name = "BOOK_AUTHOR", joinColumns =
// @JoinColumn(name = "AUTHOR_ID", referencedColumnName = "ID"),
//inverseJoinColumns =
// @JoinColumn(name = "BOOK_ID", referencedColumnName = "ID"))
// private Set<Book> books;
public Author()
super();
public Author(String name)
super();
this.name = name;
public Integer getId()
return id;
public void setId(Integer id)
this.id = id;
public String getName()
return name;
public void setName(String name)
this.name = name;
public Set<Book> getBooks()
return books;
public void setBooks(Set<Book> books)
this.books = books;
@Override
public String toString()
return String.format("Author [id=%s, name=%s, books=%s]", id, name, books);
在 test.class 中测试代码快照器
List<Book> books = bookRepository.findAll();
for (Book it : books)
Set<Author> authors = it.getAuthors();
//CAN get authors data.
System.out.println(authors.size());
assertThat(bookRepository.findAll()).hasSize(2);
List<Author> authors = authorRepository.findAll();
for (Author it : authors)
//CAN NOT get books data ? Why and HOW ?
//HOW can I get the books data ? Or other ways ?
// thanks
Set<Book> books1 = it.getBooks();
assertThat(books1 == null);
assertThat(authorRepository.findAll()).hasSize(3);
我的代码有错误吗? 还是其他方式?
感谢有很大不同。
【问题讨论】:
Author
对象在访问其getBooks
方法时处于什么生命周期状态?在此之前是否加载了数据? JPA 提供者有一个日志告诉你这些事情。数据库中的数据是否正确?
你的代码应该可以工作,除非你刚刚在同一个事务中创建了书籍和作者,而忘记在作者中设置书籍。 JPA 从不将集合设置为 null,您也不应该这样做。将您的集合初始化为空集合。
【参考方案1】:
将 FetchType 设置为 EAGER 不好,因为它效率不高。
当然,您必须先初始化authors
和books
。
您正在使用 双向 多对多关系。因此,当您将 Authors 添加到此 Book 时,您必须手动将其“链接”到 Book:
@ManyToMany(cascade = CascadeType.ALL)
private final Set<Author> authors = new HashSet<>();
// ...
public void setAuthors(Set<Author> authors)
for (Author author : authors)
author.getBooks().add(this);
this.authors.add(author);
(关注author.getBooks().add(this);
)
你需要分别改变你的构造函数:
public Book(String name, Set<Author> authors)
this.name = name;
setAuthors(authors);
另外我建议更正 toString
方法 - 从中删除 authors
,以避免发生 *** 异常(您也必须在 Author 类中执行此操作):
@Override
public String toString()
return String.format("Book [id=%s, name=%s]", id, name);
你的测试可以正常工作:
@Before
public void init()
Author lewis = new Author("Lewis");
Author mark = new Author("Mark");
Author peter = new Author("Peter");
Book spring = new Book("Spring in Action", new HashSet<>(asList(lewis, mark)));
Book springboot = new Book("Spring Boot in Action", new HashSet<>(asList(lewis, peter)));
bookRepository.save(Arrays.asList(spring, springboot));
@Test
public void findAll()
List<Book> books = bookRepository.findAll();
assertThat(books).hasSize(2);
for (Book book : books)
Set<Author> bookAuthors = book.getAuthors();
assertThat(bookAuthors).isNotNull();
assertThat(bookAuthors.size()).isGreaterThan(0);
System.out.println(book);
bookAuthors.forEach(System.out::println);
List<Author> authors = authorRepository.findAll();
assertThat(authors).hasSize(3);
for (Author author : authors)
Set<Book> authorBooks = author.getBooks();
assertThat(authorBooks).isNotNull();
assertThat(authorBooks.size()).isGreaterThan(0);
System.out.println(author);
authorBooks.forEach(System.out::println);
请参阅Hibernate User Guide 了解更多信息。
一般不要将cascade = CascadeType.ALL
用于ManyToMany,因为在这种情况下实体是独立的。请改用cascade = CascadeType.PERSIST, CascadeType.MERGE
。
【讨论】:
我将它提交到我的 github 和 commet。 project code【参考方案2】:您可以在实体中使用mappedBy = "", cascade = CascadeType.ALL, fetch = FetchType.LAZY
。在方法之前使用@Transactional(propagation = Propagation.REQUIRED)
注释并在类上使用@Transactional
。当您获取数据时,在多对多关系实体 lilnk 上使用 .size()
方法:当您获取 Author
时使用 Author.getBooks().size();
,您可以获得作者的启动详细信息。
例子
FOrd fOrd = orderRepo.findOne(id);
fOrd.getFOrdItems().size();
fOrd.getFOrdDetails().size();
【讨论】:
【参考方案3】:当我使用这个代码迭代 Set 时,控制台崩溃:
org.hibernate.LazyInitializationException: 无法初始化代理 - 没有 Session,encore un fois
所以,我在 ***.com 上搜索了这个提示。
This answer help me
只需添加
FISRT - 初始书籍
@ManyToMany(mappedBy = "authors")
private Set<Book> books = new HashSet<Book>();
使用@JB Nizet 建议,谢谢!
第二个 - 在结束链接中添加 FetchType.EAGER
@ManyToMany(mappedBy = "posts", fetch = FetchType.EAGER)
@JsonBackReference
谢谢大家。
【讨论】:
以上是关于Spring Data JPA:查询ManyToMany,如何从映射类中获取数据?的主要内容,如果未能解决你的问题,请参考以下文章
Spring Data 系列学习Spring Data JPA 基础查询
Spring Data 系列学习Spring Data JPA 自定义查询,分页,排序,条件查询
spring-data-jpa 存储库模式与 Querydsl 查询模式有啥区别?