我正在使用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){
...
}
}
它有效,但内循环中的迭代非常慢,比分别查询表book
,book_author
和author
要慢得多。我想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现在生成单个查询。
错误还比分别查询表book
,book_author
和author
慢20倍。也许mysql查询优化器不起作用?
答案 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";