Ebean ManyToMany:如何优化ManyToMany关系的获取

时间:2014-02-03 12:05:03

标签: database performance persistence ebean

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

更确切地说,我正在获取一系列书籍:

@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查询优化器不起作用?

1 个答案:

答案 0 :(得分: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;

}

接下来将SQL日志记录添加到application.conf

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";