java - Spring Data JPA : query ManyToMany, 如何从映射的类获取数据?

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

我正在使用 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);

我的代码有错误吗? 或者其他方式?

感谢差异很大。

最佳答案

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

<小时/>

您正在使用双向ManyToMany 关系。因此,当您将作者添加到此图书时,您必须手动将作者图书“链接”:

@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}

关于java - Spring Data JPA : query ManyToMany, 如何从映射的类获取数据?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44988529/

相关文章:

java - 用于读入模型的 @XmlRootElement 注释

java - 发送短信时,应用程序在 getContentResolver().query(...) 上停止

java - 注释 ConcurrentHashMap 时处于 hibernate 状态的 "Illegal attempt to map a non collection as a @OneToMany, @ManyToMany or @CollectionOfElements"

java - 如何在 JPA 中选择和订购 EmbeddedID

java - Kadane 的算法中是否保留了足够的信息来返回实际的最大子数组或索引,而不仅仅是求和?

java - 使用 Math.pow 方法

java - 验证异常 : Unable to find a default provider

java - spring-mvc 3.0.5 : what's the preferred way to instantiate the JPA entity manager

java - Spring @Async 不允许使用 Autowiring 的 bean

hibernate - 使用 Grails GORM 从旧数据库中的 char 字段中去除尾随空格