【问题标题】:Spring Data JPA: query ManyToMany, How Can I get the data from mapped Class?Spring Data JPA:查询ManyToMany,如何从映射类中获取数据?
【发布时间】: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,您也不应该这样做。将您的集合初始化为空集合。

标签: java spring hibernate spring-mvc spring-data-jpa


【解决方案1】:

将 FetchType 设置为 EAGER 不好,因为它效率不高。 当然,您必须先初始化authorsbooks


您正在使用 双向 多对多关系。因此,当您将 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,以避免发生 Stackoverflow 异常(您也必须在 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

    所以,我在 Stackoverflow.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
    

    谢谢大家。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-18
      • 1970-01-01
      • 1970-01-01
      • 2017-04-25
      • 1970-01-01
      • 1970-01-01
      • 2020-10-24
      相关资源
      最近更新 更多