我正在开发一个多语言应用程序。出于这个原因,许多对象在其名称和描述字段中都有我称之为LocalizedStrings而不是普通字符串的集合。每个LocalizedString基本上都是一对语言环境和一个本地化为该语言环境的字符串。
让我们以一个实体为例,让我们说一本书 - 对象。
public class Book{
@OneToMany
private List<LocalizedString> names;
@OneToMany
private List<LocalizedString> description;
//and so on...
}
当用户要求提供书籍列表时,它会进行查询以获取所有书籍,在用户选择运行应用程序的区域设置中提取每本书籍的名称和描述,并将其显示回用户。
这有效,但这是一个主要的性能问题。目前,hibernate使一个查询获取所有书籍,之后它遍历每个单独的对象并向hibernate请求该特定对象的本地化字符串,从而导致“n + 1选择问题”。获取50个实体的列表会在我的服务器日志中生成大约6000行sql命令。
我试着让收藏品变得热切,但这导致我“无法同时取出多个包包” - 发送。
然后我尝试将集合上的获取策略设置为subselect,希望它会对所有书籍执行一次查询,然后执行一个查询以获取所有书籍的所有LocalizedStrings。在这种情况下,子选择不起作用,我希望它基本上与我的第一个案例完全相同。
我开始没有关于如何优化它的想法。
简而言之,当你提取一个集合并且该集合中的每个元素本身都有一个或多个集合时,有哪些获取策略替代品,这些集合必须同时获取。
答案 0 :(得分:3)
你说
我尝试将集合上的抓取策略设置为subselect,希望它能为所有图书执行一个查询
你可以,但你需要访问一些属性来抛出子选择
@Entity
public class Book{
private List<LocalizedString> nameList = new ArrayList<LocalizedString>();
@OneToMany(cascade=javax.persistence.CascadeType.ALL)
@org.hibernate.annotations.Fetch(org.hibernate.annotations.FetchMode.SUBSELECT)
public List<LocalizedString> getNameList() {
return this.nameList;
}
private List<LocalizedString> descriptionList = new ArrayList<LocalizedString>();
@OneToMany(cascade=javax.persistence.CascadeType.ALL)
@org.hibernate.annotations.Fetch(org.hibernate.annotations.FetchMode.SUBSELECT)
private List<LocalizedString> getDescriptionList() {
return this.descriptionList;
}
}
执行以下操作
public class BookRepository implements Repository {
public List<Book> getAll(BookFetchingStrategy fetchingStrategy) {
switch(fetchingStrategy) {
case BOOK_WITH_NAMES_AND_DESCRIPTIONS:
List<Book> bookList = session.createQuery("from Book").list();
// Notice empty statement in order to start each subselect
for (Book book : bookList) {
for (Name address: book.getNameList());
for (Description description: book.getDescriptionList());
}
return bookList;
}
}
public static enum BookFetchingStrategy {
BOOK_WITH_NAMES_AND_DESCRIPTIONS;
}
}
我已完成以下操作来填充数据库
SessionFactory sessionFactory = configuration.buildSessionFactory();
Session session = sessionFactory.openSession();
session.beginTransaction();
// Ten books
for (int i = 0; i < 10; i++) {
Book book = new Book();
book.setName(RandomStringUtils.random(13, true, false));
// For each book, Ten names and descriptions
for (int j = 0; j < 10; j++) {
Name name = new Name();
name.setSomething(RandomStringUtils.random(13, true, false));
Description description = new Description();
description.setSomething(RandomStringUtils.random(13, true, false));
book.getNameList().add(name);
book.getDescriptionList().add(description);
}
session.save(book);
}
session.getTransaction().commit();
session.close();
并检索
session = sessionFactory.openSession();
session.beginTransaction();
List<Book> bookList = session.createQuery("from Book").list();
for (Book book : bookList) {
for (Name address: book.getNameList());
for (Description description: book.getDescriptionList());
}
session.getTransaction().commit();
session.close();
我看到了
休眠:
select
book0_.id as id0_,
book0_.name as name0_
from
BOOK book0_
Hibernate:返回100行(如预期的那样)
select
namelist0_.BOOK_ID as BOOK3_1_,
namelist0_.id as id1_,
namelist0_.id as id1_0_,
namelist0_.something as something1_0_
from
NAME namelist0_
where
namelist0_.BOOK_ID in (
select
book0_.id
from
BOOK book0_
)
Hibernate:返回100行(如预期的那样)
select
descriptio0_.BOOK_ID as BOOK3_1_,
descriptio0_.id as id1_,
descriptio0_.id as id2_0_,
descriptio0_.something as something2_0_
from
DESCRIPTION descriptio0_
where
descriptio0_.BOOK_ID in (
select
book0_.id
from
BOOK book0_
)
三个选择陈述。没有“n + 1”选择问题。请注意,我使用属性访问策略而不是字段。记住这一点。
答案 1 :(得分:1)
您可以在行李上设置batch-size
,当初始化一个未初始化的集合时,Hibernate将使用单个查询初始化其他一些集合