如何在Hibernate中优化获取多个集合?

时间:2012-07-23 03:04:34

标签: hibernate optimization

我正在尝试使用Hibernate来提高应用程序的性能,Hibernate正在对数据库执行过多的SQL调用。我认为数据获取可以组合在一起以减少调用并提高性能,但我在这里有点不知所措。我查看了关于子选择和批量提取的Hibernate文档,这确实有帮助,但我不认为它完全消除了这个问题。

在下面的示例中,我需要获取属于部队的士兵列表的详细信息并将其显示在网页上。

@Entity
public class Troop {
    @OneToMany(mappedBy="troop")
    public List<Soldier> getSoldiers() {
   ...
}

很容易将获取策略设置为subselect,batch或eager,以便在没有太多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 1,000次SQL呼叫!

有没有办法通过减少通话次数来优化这一点?既然我已经知道我需要检索的士兵,我可以检索实体并让它们在第一级缓存中可用吗?

例如我可以查询

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选择。这样可以减少每个集合一次调用的次数(本例中为3次),而不是每个士兵每次调用一次(3 x 1000)

我在文档中没有真正看到有关解决此问题的内容,因此非常感谢任何提示和想法。请记住,由于这些集合是列表而不是集合,因此无法在多个集合上执行左连接提取,或者Hibernate将返回异常。

HibernateException: cannot simultaneously fetch multiple bags 

2 个答案:

答案 0 :(得分:3)

您还可以在集合中使用注释@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

答案 1 :(得分:1)

即使您获取集合的所有项目,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。