spring-data-jpa 多对多 findByID

spring-data-jpa many to many findByID

我在书和作者之间有多对多关系,我有 3 个表:作者、书和 author_book。

@Entity()
@Table(name = "author")
public class Author implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String name;

    @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @JoinTable(
            name = "author_book",
            joinColumns = @JoinColumn(name = "author_id"),
            inverseJoinColumns = @JoinColumn(name = "book_id")
    )
    private List<Book> authorBooks = new ArrayList<Book>();

    public Author() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<Book> getAuthorBooks() {
        return authorBooks;
    }

    public void setAuthorBooks(List<Book> authorBooks) {
        this.authorBooks = authorBooks;
    }

    @Override
    public String toString() {
        return "Author{" + "name=" + name + ", authorBooks=" + authorBooks + '}';
    }

}


@Entity()
@Table(name = "book")
public class Book implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String name;

    @ManyToMany(mappedBy = "authorBooks", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private List<Author> bookAuthors = new ArrayList<Author>();

    public Book() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<Author> getBookAuthors() {
        return bookAuthors;
    }

    public void setBookAuthors(List<Author> bookAuthors) {
        this.bookAuthors = bookAuthors;
    }

    @Override
    public String toString() {
        return "Book{" + "name=" + name + ", bookAuthors=" + bookAuthors + '}';
    }

}

我可以毫无问题地将数据添加到数据库,但是当我想通过其 id 获取作者或书籍时

Optional<Author> optionalAuthor = authorReposi.findById(1L);
System.out.println("Author: " + optionalAuthor.get().toString());

我得到一个错误:LazyInitialization 延迟失败...

我想使用 FetchType.LAZY 并获取作者或书籍的实例。

感谢您的宝贵时间。

所以,阅读精美手册:6.3.10. Configuring Fetch- and LoadGraphs

您的问题只是您的 toString() 方法是递归的。 Authors 表示打印 BooksBooks 表示打印 Authors。专业提示:成功在于细节。

对于 loadfetch,您需要使用 JPA 中的 EntityGraph 来指定连接的属性。所以:

@Entity()
@Table(name = "author")
@NamedEntityGraph(name = "Book.detail", attributeNodes = @NamedAttributeNode("books"))
@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Author {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String name;

    @ManyToMany
    private List<Book> books;
}

@Entity()
@Table(name = "book")
@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Book {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String name;

    @ManyToMany(mappedBy = "books")
    private List<Author> authors;

}

有存储库:

public interface AuthorRepository extends JpaRepository<Author, Long>{
    @EntityGraph(value = "Book.detail", type = EntityGraphType.LOAD)
    Author getById(Long id);
}

public interface BookRepository extends JpaRepository<Book, Long>{

}

然后你必须自己打印你想要的。基本上,把 toStrings 放在 Entities 中通常会导致问题,但你也应该 RTFM.

private void read(Long id) {
    Author a = authorRepository.getById(id);
    System.out.println("Author: " + a.getName());
    for( Book b: a.getBooks()) {
        System.out.println("\tBook: " + b.getName());
    }
}

最后,我避免像瘟疫一样使用 Cascade 注释。它们是复杂的注释。此外,ManyToMany 默认为 FetchType.LAZY。重要的注释是 mappedBy 注释。这会告诉您哪个实体拥有该关系。拥有实体是负责保持关系的实体。双向注释的另一面实际上只用于查询。不需要在实体中创建新的 ArrayList,因为它们在查询期间无论如何都会被丢弃。当您需要保留一个新的 Author 具有关系的实体时,只需创建一个列表,否则使用查询返回的列表。

private Author save() {
    Book b = bookRepository.save(Book.builder().name("b1").build());
    return authorRepository.save(Author.builder().name("a1").books(Collections.singletonList(b)).build());
}