【问题标题】:Ebean ManyToMany: How to optimize the fetching of a ManyToMany relationshipEbean ManyToMany:如何优化多对多关系的获取
【发布时间】:2014-02-03 12:05:03
【问题描述】:

我正在使用 PLAY 2.2.1、Ebean 构建一个 WebApp,并希望优化通过多对多关系关联到另一个实体的实体列表的获取。

更准确地说,我正在获取书籍列表:

@Entity
public class Book extends Model {

    @Id
    @Column(name="book_id")
    public int bookId;

    @ManyToMany
    @JoinTable(name="book_author", 
        joinColumns={@JoinColumn(name="book_id", referencedColumnName = "book_id")},
        inverseJoinColumns={@JoinColumn(name="author_id", referencedColumnName = "author_id")})
    public List<Author> authors;
}

...每本书都与作者列表相关联:

@Entity
public class Author extends Model {

    @Id
    @Column(name="author_id")
    public int authorId;
}

创建查询并获取其结果的代码是

Query<Book> queryBooks = Ebean.createQuery(Book.class);
queryBooks.where("...");
FutureList<Book> fLBooks = queryBooks.findFutureList();

if (fLBooks.isDone()){
    List<Book> books = fLBooks.get();
}

FutureList 因为我想并行声明不同的查询。我需要访问所有书籍和所有作者,因此我必须遍历所有书籍及其所有作者:

for(Book b : books){
    List<Author> authors = b.authors;
    for (Author a : authors){
        ...
    }
}

它可以工作,但是内部循环中的迭代非常慢,比分别查询表bookbook_authorauthor 慢得多。我想 Ebean 一次只取作者一个。 有更快的方法吗?

使用@ManyToMany(fetch=FetchType.EAGER) 不会改变检索速度。

更新 05.02.2014

我以这种方式创建查询:

Query<Book> queryBooks = Ebean.createQuery(Book.class);
FutureList<Book> fLBooks = queryBooks.fetch("authors", "*").where("...").findFutureList();

生成的 Ebean 查询是

select 
t0.book_id c0, 
t1.author_id c0
from book t0 
left outer join book_author t1z_ on t1z_.book_id = t0.book_id  
left outer join author t1 on t1.author_id = t1z_.author_id  
where MATCH (t0.text) AGAINST ('...' IN BOOLEAN MODE) order by t0.book_id

好: Ebean 现在生成单个查询。 糟糕: 仍然比分别查询表 bookbook_authorauthor 慢 20 倍。也许mysql查询优化器不起作用?

【问题讨论】:

  • 那么你最初是如何得到书单的呢?
  • 查看我的更新(创建查询并获取结果的代码)。

标签: database performance persistence ebean


【解决方案1】:

嗯,为什么不用普通的 Ebean 的Finder 自己和@JoinColumn 之类的东西战斗呢? Ebean 的优势是让事情变得非常简单,以正确方式编写的相同模型是:

图书

@Entity
public class Book extends Model {

    @Id
    public Integer id;
    public static Finder<Integer, Book> find = new Finder<>(Integer.class, Book.class);

    public String title;

    @ManyToMany
    public List<Author> authors;

}

作者

@Entity
public class Author extends Model {

    @Id
    public Integer id;
    public static Finder<Integer, Author> find = new Finder<>(Integer.class, Author.class);

    public String name;

    @ManyToMany(mappedBy = "authors") // take care about this mappedBy annotation, otherwise Ebean will want to use second JoinColumn
    public List<Book> books;

}

接下来通过将其添加到您的 application.conf 来打开 SQL 日志记录

db.default.logStatements = true
logger.com.jolbox = DEBUG

并比较执行:

String out = "";
List<Book> books = Book.find.findList();
for (Book book : books)
    for (Author author : book.authors)  
        out += "Book " + book.title + " by " + author.name + "\n";

对比:

String out = "";
List<Book> books = Book.find.fetch("authors", "*").findList();
for (Book book : books)
    for (Author author : book.authors)  
        out += "Book " + book.title + " by " + author.name + "\n";

一般来说你是对的,第一种方式它对每本书执行额外的查询,第二种方式只执行一个查询。

反方向:

String out = "";
List<Author> authors = Author.find.findList();
for (Author author : authors)
    for (Book book : author.books)
        out += author.name + " wrote this book: " + book.title + "\n";

对比:

String out = "";
List<Author> authors = Author.find.fetch("books", "*").findList();
for (Author author : authors)
    for (Book book : author.books)
        out += author.name + " wrote this book: " + book.title + "\n";

【讨论】:

  • 感谢您的详细回复,但单次查询并没有带来预期的速度提升。它仍然比分别查询表 book、book_author 和 author 慢 20 倍(2 秒到 100 毫秒)。见更新。顺便说一句,我不能使用 Ebean 的命名,因为这些表已经存在并且我正在它之上构建一个 WebApp。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-11-13
  • 2021-06-16
  • 2021-05-26
  • 1970-01-01
  • 1970-01-01
  • 2015-12-12
  • 1970-01-01
相关资源
最近更新 更多