【问题标题】:n+1 query not workingn+1 查询不工作
【发布时间】:2014-11-07 09:06:16
【问题描述】:

在下面的代码中,我预计会发生n+1 查询问题,但它没有发生。 User.java

import java.util.*;

public class User {
    private long userId;
    private String firstName;
    private Set phones;

    public User() {
        System.out.println("0-arg constructor :User");
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public long getUserId() {
        return userId;
    }

    public void setUserId(long userId) {
        this.userId = userId;
    }

    public Set getPhones() {
        return phones;
    }

    public void setPhones(Set phones) {
        this.phones = phones;
    }
}

PhoneNumber.java

public class PhoneNumber {
    private String numberType;
    private long phone;
    private long id;
    User parent;

    public PhoneNumber() {
        System.out.println("0-arg constructor :PhoneNumber");
    }

    // write getXxx(),setXxx() methods (4 sets)

    public void setId(long id) {
        this.id = id;
    }

    public long getId() {
        return id;
    }

    public String getNumberType() {
        return numberType;
    }

    public void setNumberType(String numberType) {
        this.numberType = numberType;
    }

    public long getPhone() {
        return phone;
    }

    public void setPhone(long phone) {
        this.phone = phone;
    }

    public void setParent(User parent) {
        this.parent = parent;
    }

    public User getParent() {
        return parent;
    }

}

User.hbm.xml

 <hibernate-mapping>
  <class name="User" 

      table="USER_TABLE" >

      <id name="userId" 

      column="USER_ID"/>
      <property name="firstName"  

       column="FIRST_NAME"/>

     <set name="phones"  

     table="PHONE_NUMBERS" cascade="all"  

       lazy="true">
    <key column="UNID"/>
    <one-to-many 

    class="PhoneNumber"/>
   </set>

  </class>
   </hibernate-mapping>

phoneNumber.hbm

   <hibernate-mapping>

    <class name="PhoneNumber" table="PHONE_NUMBERS" >

   <id name="phone"  column="PHONE"/>
   <property name="numberType" column="NUMBER_TYPE"/>
   <property name="id" column="UNID" insert="false" update="false"/>

   <many-to-one name="parent" class="User"  column="UNID2" cascade="all"/>
    </class>
    </hibernate-mapping>

hibernate.cfg

    <session-factory>
    <property 

    name="hibernate.connection.driver_class">oracle.jdbc.driver.OracleDriver</

     property>
    <property 

   name="hibernate.connection.url">jdbc:oracle:thin:@localhost:1521:xe</prope

    rty>
    <property 

     name="hibernate.connection.username">system</property>
    <property 

       name="hibernate.connection.password">oracle123</property>

    <property 

    name="hibernate.dialect">org.hibernate.dialect.OracleDialect</property>
    <property name="hibernate.hbm2ddl.auto">update</property>
    <property name="show_sql">true</property>

    <mapping resource="user.hbm.xml"/>          

      <mapping resource="phoneNumber.hbm.xml"/>

</session-factory>

HQLClient.java

import org.hibernate.*;
import org.hibernate.cfg.*;
import java.util.*;

public class HQLJoinsClient {

    public static void main(String[] args) {
        try {
            Configuration conf = new Configuration().configure();
            SessionFactory factory = conf.buildSessionFactory();
            Session ses = factory.openSession();

            String hql = "from User ";
            Query q = ses.createQuery(hql);
            List l = q.list();
            System.out.println("++++++++++++++++++" + l.size()
                    + "+++++++++++++");

            for (int i = 0; i < l.size(); ++i) {
                User u1 = (User) l.get(i);
                System.out
                        .println("\n\n\nParent----------------------------------------------------------------->");

                System.out.print("user id: " + u1.getUserId());
                System.out.println("FirstName " + u1.getFirstName());

                Set s = u1.getPhones();
                if (s != null) {
                    Iterator it = s.iterator();
                    while (it.hasNext()) {
                        PhoneNumber p1 = (PhoneNumber) it.next();
                        System.out.println("\nchild---->");
                        System.out.print("Number Type=" + p1.getNumberType());
                        System.out.print("Phone Number=" + p1.getPhone());
                        System.out.println("User id=" + p1.getId());

                    }// inner while
                }// if
            }

            ses.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

输出为:

INFO: schema update complete                                                    Hibernate: select user0_.USER_ID as USER1_0_, user0_.FIRST_NAME as FIRST2_0_ from USER_TABLE user0_                                                             


0-arg constructor :User 0-arg constructor :User                                                         0-arg constructor :User                                                         ++++++++++++++++++3+++++++++++++++++++++++++                                                                                                                                                                                                                                                                                    Parent----------------------------------------------------------------->        user id: 102FirstName ravi                                                      Hibernate: select phones0_.UNID as UNID1_, phones0_.PHONE as PHONE1_, phones0_.PHONE as PHONE1_0_, phones0_.NUMBER_TYPE as NUMBER2_1_0_, phones0_.UNID as UNID1_0_, phones0_.UNID2 as UNID4_1_0_ from PHONE_NUMBERS phones0_ where phones0_.UNID=?                                                                              0-arg constructor :PhoneNumber                                                  0-arg constructor :PhoneNumber                                                  0-arg constructor :PhoneNumber                                                                                                                                  child---->                                                                      Number Type=resPhone Number=81818181User id=102                                                                                                                 child---->                                                                      Number Type=officePhone Number=71717171User id=102                                                                                                              child---->                                                                      Number Type=homePhone Number=91919191User id=102                                                                                                                                                                                                                                                                                Parent----------------------------------------------------------------->        user id: 103FirstName jayendra                                                  Hibernate: select phones0_.UNID as UNID1_, phones0_.PHONE as PHONE1_, phones0_.PHONE as PHONE1_0_, phones0_.NUMBER_TYPE as NUMBER2_1_0_, phones0_.UNID as UNID1_0_, phones0_.UNID2 as UNID4_1_0_ from PHONE_NUMBERS phones0_ where phones0_.UNID=?                                                                              0-arg constructor :PhoneNumber                                                  0-arg constructor :PhoneNumber                                                                                                                                  child---->                                                                      Number Type=resPhone Number=3748329382User id=103                                                                                                               child---->                                                                      Number Type=homePhone Number=538432342User id=103                                                                                                                                                                                                                                                                               Parent----------------------------------------------------------------->        user id: 104FirstName mike                                                      Hibernate: select phones0_.UNID as UNID1_, phones0_.PHONE as PHONE1_, phones0_.PHONE as PHONE1_0_, phones0_.NUMBER_TYPE as NUMBER2_1_0_, phones0_.UNID as UNID1_0_, phones0_.UNID2 as UNID4_1_0_ from PHONE_NUMBERS phones0_ where phones0_.UNID=?                                                                              0-arg constructor :PhoneNumber                                                  0-arg constructor :PhoneNumber                                                                                                                                  child---->                                                                      Number Type=homePhone Number=238349384User id=104                                                                                                               child---->                                                                      Number Type=mobilePhone Number=9455682832User id=104                            

我希望每个电话号码记录到一个用户 ID 会有单独的选择语句,但对于 3 个电话号码 - 1 个用户 ID 有一个选择语句 [而不是 (3+1)]。为什么会这样?

谢谢!

【问题讨论】:

    标签: hibernate hibernate-onetomany select-n-plus-1


    【解决方案1】:

    您的映射中存在一些问题。

    首先,one-to-manymany-to-one 是用一列表示的关系数据库。两端相同的列,所以这是错误的:

    // class name="User"
    <set name="phones" table="PHONE_NUMBERS"
         cascade="all" lazy="true">
    
        <key column="UNID"/>                   // this column must be same
        <one-to-many class="PhoneNumber" />
    </set>
    
    // class name="PhoneNumber"
    ...   
    <many-to-one name="parent" class="User" 
           column="UNID2"                      // as this column
           cascade="all"/>
    

    两个列值必须针对同一列

    其次,您遇到了 1 + N 问题。用户有 一个 SELECT,但他们的电话有 3 个选择。这是标准的 1 + N 问题。

    解决方法是使用:

    20.1.5. Using batch fetching

    小引用:

    使用批量获取,如果访问一个代理,Hibernate 可以加载多个未初始化的代理。批量抓取是对惰性选择抓取策略的优化。您可以通过两种方式配置批量获取:在类级别和集合级别。

    类/实体的批量获取更容易理解。考虑以下示例:在运行时,您在 Session 中加载了 25 个 Cat 实例,每个 Cat 都有对其所有者的引用,即一个 Person。 Person 类使用代理lazy="true" 进行映射。如果您现在遍历所有猫并在每个猫上调用 getOwner(),默认情况下,Hibernate 将执行 25 个 SELECT 语句来检索代理的所有者。您可以通过在 Person 的映射中指定批量大小来调整此行为:

    <class name="Person" batch-size="10">...</class>
    

    所以,我们应该在集合上使用这个设置:

    <set name="phones" table="PHONE_NUMBERS" batch-size="25"
         cascade="all" lazy="true">
    

    我建议在每个类映射上也使用它:

    <class name="PhoneNumber" table="PHONE_NUMBERS" batch-size="25">
    

    【讨论】:

    • 请仔细阅读问题。我没有遇到 1+n 问题,但它应该给出 1+n 没有发生的问题。对于用户表中的用户 id 102,我在电话号码表中有 3 条记录,这些记录在 1 个选择查询中获取(理想情况下,它应该需要 3 个电话号码查询和 1 个用户表查询),有多种技术可以解决这个问题(但不幸的是没有遇到这个问题:()
    • 请仔细阅读我的回答。 您遇到 1+N 问题。对于每个用户,他的电话列表都有一个选择。这是 1 + N 的问题。不仅您拥有它,而且我还向您解释如何修复它。现在有用吗?
    • 是的,它成功了!在我在集合中指定批量大小后,1+1 = 2 现在选择查询。我想知道如何使用 fetch 关键字,以防万一我想用 HQL 语句进行获取。
    • 很高兴看到这一点。 fetch ala batch-size="25" 是一个映射功能。很棒的功能,因为它不需要在 HQL 中考虑它。一旦触及集合或多对一,它将为我们完成......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-07
    • 2011-08-09
    • 2011-06-23
    • 1970-01-01
    相关资源
    最近更新 更多