我正在尝试使用Hibernate来提高应用程序的性能,因为Hibernate对数据库执行了太多SQL调用。我认为数据提取可以组合在一起,以减少调用并提高性能,但我在这里有点不知所措。我已经查看了Hibernate文档中关于子选择和批量获取的内容,这确实有帮助,但我认为它并没有完全消除这个问题。
在下面的例子中,我需要获得部队士兵列表的详细信息,并将其显示在网页上。
@Entity
public class Troop {
@OneToMany(mappedBy="troop")
public List<Soldier> getSoldiers() {
...
}
在没有太多SQL语句的情况下,很容易将获取策略设置为子选择、批处理或急于检索该部队的所有士兵。
@Entity
public class Soldier {
@Id
String soldierId
String firstName;
String lastName;
@OneToMany(mappedBy="address")
public List<Soldier> getAddress() {
...
@OneToMany(mappedBy="combatHistory")
public List<Soldier> getCombatHistory() {
...
@OneToMany(mappedBy="medicalHistory")
public List<Soldier> getMedicalHistory() {
...
}
每个士兵实体与其他延迟加载的实体具有多个一对多关系。我需要初始化这些集合并从中检索值。如果一个士兵有3个一对多关系,而一个部队有1000个士兵,则会产生3 x 1000个SQL调用!
有没有办法通过减少通话次数来优化这一点?既然我已经知道我需要检索的士兵ID,我可以检索实体并在一级缓存中提供它们吗?
例如,我可以查询
from Address as a where a.soldierId in (...)
from CombatHistory as a where a.soldierId in (...)
from MedicalHistory as a where a.soldierId in (...)
如果Address、CombatHistory等实体可以缓存,那么当在每个士兵中访问集合时,将不会执行SQL select。这将把每个集合的呼叫次数减少到一次(在这种情况下为3次),而不是每个士兵每个集合一次(3 x 1000)
我在文档中没有看到太多关于解决这个问题的内容,所以我们非常感谢任何提示和想法。请记住,由于这些集合是Lists而不是Sets,因此不能在多个集合上执行左联接提取,否则Hibernate将返回异常。
HibernateException: cannot simultaneously fetch multiple bags
您也可以在集合上使用注释@Fetch(FetchMode.SUBSELECT)如果"触摸"该类型的一个集合,那么所有集合都将在一个SQL请求中获取。
@Entity
public class Country implements java.io.Serializable {
private long id;
private int version;
private String country;
private Set<City> cities = new HashSet<City>(0);
@Fetch(FetchMode.SUBSELECT)
@OneToMany(mappedBy = "country", cascade = CascadeType.ALL)
public Set<City> getCities() {
return cities;
}
...
}
以下是如何使用它的示例:
public List<Country> selectCountrySubSelect() {
List<Country> list = getSession().createQuery("select c from Country c").list();
// You don't have to initialize every collections
// for (Country country : list) {
// Hibernate.initialize(country.getCities());
// }
// but just "touch" one, and all will be initialized
Hibernate.initialize(((Country) list.get(0)).getCities());
return list;
}
日志:
DEBUG org.hibernate.engine.loading.internal.CollectionLoadContext.endLoadingCollections(): - 2 collections were found in result set for role: business.hb.Country.cities
DEBUG org.hibernate.engine.loading.internal.CollectionLoadContext.endLoadingCollection(): - Collection fully initialized: [business.hb.Country.cities#1]
DEBUG org.hibernate.engine.loading.internal.CollectionLoadContext.endLoadingCollection(): - Collection fully initialized: [business.hb.Country.cities#2]
DEBUG org.hibernate.engine.loading.internal.CollectionLoadContext.endLoadingCollections(): - 2 collections initialized for role: business.hb.Country.cities
DEBUG org.hibernate.engine.internal.StatefulPersistenceContext.initializeNonLazyCollections(): - Initializing non-lazy collections
即使获取集合的所有项,hibernate也不会确保集合的所有项目都已加载,并且不会填充集合。然而,由于士兵在会话缓存中,您可以一次加载一个集合。
from Soldier s left join fetch s.Address where s.soldierId in (...)
from Soldier s left join fetch s.CombatHistory where s.soldierId in (...)
from Soldier s left join fetch s.MedicalHistory where s.soldierId in (...)
这将把往返次数减少到3次。