0

我假设我遇到了 N+1 选择问题。

我得到了这个实体:

@Entity
@Table(name = "Devices")
public class Device implements Serializable {

 @OneToOne(mappedBy="holdingDevice", fetch=FetchType.LAZY)
    @Cascade(CascadeType.ALL)
    @PrimaryKeyJoinColumn
    private WarrantyEntry warranty;
}

这是另一个实体:

@Entity
@Table(name = "Warranty")
public class WarrantyEntry implements Serializable{
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Long id;

    @OneToOne
    @JoinColumn(name = "serial")
    @PrimaryKeyJoinColumn
    private Device holdingDevice;

现在当我开始迭代这个循环时:

 Set<Device> customerDevices = user.getCustomer().getDevices();
            for (Device device : customerDevices) {
               ...
            }

我被卡住了,我在 Hibernate 选择的日志中看到:

Hibernate: */ 选择保修en0_.id as... Hibernate: /* load myclass.Device */

Hibernate: */ 选择保修en0_.id as .. Hibernate: /* load myclass.Device */...

一遍又一遍,我想我遇到了 n+1 选择问题。

有什么建议我该如何解决并只用一个替换所有这些选择?

这样做之后

 String query="from Customer c left join fetch c.devices d  \n" +
                    "left join fetch d.tradeInOldDevice "  +
                    "left join fetch d.tradeInNewDevice  "+
                    "left join fetch d.warranty";



            deviceDao.getSessionFactory().openSession().createQuery(query);


            for (Device device : customerDevices) {
               ..
            }

我仍然明白:

Hibernate: 
        devices0_.owningCompany_customerRefId as owningC15_0_1_,
        devices0_.serial as serial1_,
        devices0_.serial as serial9_0_,
        devices0_.blackListed as blackLis2_9_0_,
        devices0_.Creation_id as Creation12_9_0_,
        devices0_.deactivated as deactiva3_9_0_,
        devices0_.deviceComment as deviceCo4_9_0_,
        devices0_.deviceName as deviceName9_0_,
        devices0_.deviceType as deviceType9_0_,
        devices0_.distributor_customerRefId as distrib13_9_0_,
        devices0_.endCustomer_customerRefId as endCust14_9_0_,
        devices0_.owningCompany_customerRefId as owningC15_9_0_,
        devices0_.paChallenge as paChalle7_9_0_,
        devices0_.parent_serial as parent16_9_0_,
        devices0_.pendingDeactivation as pendingD8_9_0_,
        devices0_.safetyStock as safetySt9_9_0_,
        devices0_.serialSalt as serialSalt9_0_,
        devices0_.signedBlackBerry as signedB11_9_0_,
        devices0_.tradeInOldDevice as tradeIn17_9_0_ 
    from
        Devices devices0_ 
    where
        devices0_.owningCompany_customerRefId=?
Hibernate: 
        device0_.serial as serial9_0_,
        device0_.blackListed as blackLis2_9_0_,
        device0_.Creation_id as Creation12_9_0_,
        device0_.deactivated as deactiva3_9_0_,
        device0_.deviceComment as deviceCo4_9_0_,
        device0_.deviceName as deviceName9_0_,
        device0_.deviceType as deviceType9_0_,
        device0_.distributor_customerRefId as distrib13_9_0_,
        device0_.endCustomer_customerRefId as endCust14_9_0_,
        device0_.owningCompany_customerRefId as owningC15_9_0_,
        device0_.paChallenge as paChalle7_9_0_,
        device0_.parent_serial as parent16_9_0_,
        device0_.pendingDeactivation as pendingD8_9_0_,
        device0_.safetyStock as safetySt9_9_0_,
        device0_.serialSalt as serialSalt9_0_,
        device0_.signedBlackBerry as signedB11_9_0_,
        device0_.tradeInOldDevice as tradeIn17_9_0_ 
    from
        Devices device0_ 
    where
        device0_.tradeInOldDevice=?
Hibernate: 
        warrantyen0_.id as id34_2_,
        warrantyen0_.createdTime as createdT2_34_2_,
        warrantyen0_.deleted as deleted34_2_,
        warrantyen0_.expiryDate as expiryDate34_2_,
        warrantyen0_.serial as serial34_2_,
        warrantyen0_.updateTime as updateTime34_2_,
        warrantyen0_.updateUser as updateUser34_2_,
        device1_.serial as serial9_0_,
        device1_.blackListed as blackLis2_9_0_,
        device1_.Creation_id as Creation12_9_0_,
        device1_.deactivated as deactiva3_9_0_,
        device1_.deviceComment as deviceCo4_9_0_,
        device1_.deviceName as deviceName9_0_,
        device1_.deviceType as deviceType9_0_,
        device1_.distributor_customerRefId as distrib13_9_0_,
        device1_.endCustomer_customerRefId as endCust14_9_0_,
        device1_.owningCompany_customerRefId as owningC15_9_0_,
        device1_.paChallenge as paChalle7_9_0_,
        device1_.parent_serial as parent16_9_0_,
        device1_.pendingDeactivation as pendingD8_9_0_,
        device1_.safetyStock as safetySt9_9_0_,
        device1_.serialSalt as serialSalt9_0_,
        device1_.signedBlackBerry as signedB11_9_0_,
        device1_.tradeInOldDevice as tradeIn17_9_0_,
        management2_.id as id22_1_,
        management2_1_.deleted as deleted22_1_,
        management2_1_.firstName as firstName22_1_,
        management2_1_.lastLogin as lastLogin22_1_,
        management2_1_.lastName as lastName22_1_,
        management2_1_.password as password22_1_,
        management2_1_.primaryEmail as primaryE7_22_1_,
        management2_1_.userName as userName22_1_,
        management2_.authority as authority23_1_,
        management2_.isViewer as isViewer23_1_,
        management2_3_.distributor as distribu1_25_1_,
        management2_4_.umeKeysQuota as umeKeysQ1_27_1_,
        case 
            when management2_2_.id is not null then 2 
            when management2_3_.id is not null then 3 
            when management2_4_.id is not null then 5 
            when management2_5_.id is not null then 6 
            when management2_6_.id is not null then 7 
            when management2_.id is not null then 1 
        end as clazz_1_,
        cids3_.Users_id as Users1_22_4_,
        cids3_.element as element4_,
        emails4_.Users_id as Users1_22_5_,
        emails4_.element as element5_,
        roles5_.Users_Management_id as Users1_22_6_,
        roles5_.element as element6_ 
    from
        Warranty warrantyen0_ 
    left outer join
        Devices device1_ 
            on warrantyen0_.serial=device1_.serial 
    left outer join
        Users_Management management2_ 
            on warrantyen0_.updateUser=management2_.id 
    left outer join
        Users management2_1_ 
            on management2_.id=management2_1_.id 
    left outer join
        Users_Management_Administrators management2_2_ 
            on management2_.id=management2_2_.id 
    left outer join
        Users_Management_Distributors management2_3_ 
            on management2_.id=management2_3_.id 
    left outer join
        Users_Management_Limited management2_4_ 
            on management2_.id=management2_4_.id 
    left outer join
        Users_Management_Managers management2_5_ 
            on management2_.id=management2_5_.id 
    left outer join
        Users_Management_Workers management2_6_ 
            on management2_.id=management2_6_.id 
    left outer join
        Users_CID cids3_ 
            on management2_.id=cids3_.Users_id 
    left outer join
        Users_Emails emails4_ 
            on management2_.id=emails4_.Users_id 
    left outer join
        Users_Management_roles roles5_ 
            on management2_.id=roles5_.Users_Management_id 
    where
        warrantyen0_.serial=?

谢谢,雷。

4

2 回答 2

2

假设你已经在你的类中定义了这样的customerDevices关联:Customer

@OneToMany(fetch = FetchType.LAZY, mappedBy = "device")
private Set<Device> customerDevices;

LAZY映射关联使您可以n+1选择问题。让我们考虑一个customer为给定检索 a 的简单查询customerId

session().createQuery("from Customer c where c.name=:name").setParameter("name", name);

这将返回一个未初始化的集合包装器集合customercustomerDevices现在,当您遍历循环时:

Set<Device> customerDevices = user.getCustomer().getDevices();
            for (Device device : customerDevices) {
               ...
            }

并且在访问 的集合时,Hibernate 必须从执行额外语句devices的数据库中获取这个惰性集合。select

这个问题的推荐解决方案是在代码中覆盖运行时的默认获取策略,您可以通过使用如下查询来实现:

session().createQuery(from Customer c left join fetch c.devices d  
left join fetch d.warrantyEntry)

这将返回 aCustomer以及相关的集合。因此,不是只检索初始查询中的顶级对象,而是通过准确指定在正在进行的工作单元中将访问哪些关联来获取初始查询中所有需要的数据。

编辑:

看起来有时一对一的映射会导致 HQL 急切获取失败。您可以在以下讨论中找到有关此问题的更多信息:HQL eager fetch failureN+1 selects on left join

提到的一种解决方案是将OneToOne映射更改为ManyToOneand OneToMany,我尝试过(使用类似类型的模型类),它对我来说非常有效。我用一个 sql 查询选择了所有结果。

例如,在您的Device班级中,您可以将OneToOne映射更改为ManyToOne

@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "warrantyFK", unique = true)
private WarrantyEntry warranty;

unique=true属性将使该关联成为一对一关联,因为没有两个Device可以具有相同的WarrantyEntry. Device您可以在类中定义一个集合WarrantyEntry

@OneToMany(mappedBy = "warranty")
private Set<Device> holdingDevices;

主要思想是使用OneToManyandManyToOne而不是OneToOne. 您只需要确保在映射set的一侧最多添加一个项目。OneToMany

于 2013-09-17T14:47:20.160 回答
0

它可以是“n+1 选择问题”,您可以确保将这些属性放在您的 persistence.xml 中:

    <property name="hibernate.show_sql">true</property>
    <property name="hibernate.format_sql">true</property>
    <property name="hibernate.use_sql_comments">true</property>

我认为您也可以在 JoinColumn 中使用 unique=true, nullable=false。

您可以尝试使用 JPQL/HQL 进行急切获取并解决此问题(也许使用 Criteria 也可以解决,不确定)。

于 2013-09-17T12:14:27.667 回答