Hibernate 查询1+N问题详解


1、1+N简单来说就是,Person和Phone是一对多关系,现在我看看所有手机的信息,对于其属于哪个人不感兴趣,但把lazy设为false(lazy=false),这样就会发出1(查询手机的sql)+N(和所有查询的这些手机相关的Person的查询sql),这样会造成很大的性能开销。

首先列一下会产生1+N问题的代码

Person:

[java]
  1. private int id;  
  2. private String name;  
  3. private int age;  
Person.hbm.xml按照常规配置即可

Phone:

[java]
  1. private int id;  
  2. private String type;  
  3. private String description;  
  4. private Person person;  //关联一个用户  
Phone.hbm.xml:

[html]
  1. <hibernate-mapping>  
  2.     <class name="com.akwolf.n_1.Phone" table="PHONE">  
  3.         <id name="id" type="int">  
  4.             <column name="ID" />  
  5.             <generator class="native" />  
  6.         </id>  
  7.         <property name="type" type="java.lang.String">  
  8.             <column name="TYPE" />  
  9.         </property>  
  10.         <property name="description" type="java.lang.String">  
  11.             <column name="DESCRIPTION" />  
  12.         </property>  
  13.         <!-- lazy=false -->  
  14.         <many-to-one name="person" class="com.akwolf.n_1.Person" fetch="join" lazy="false">  
  15.             <column name="PERSON_ID" />  
  16.         </many-to-one>  
  17.     </class>  
  18. </hibernate-mapping>  

为了使效果明显一点,假定一个手机对应一个不同的用户,现在想数据库中添加一些数据:

[java]

  1. @Test  
  2. public void testSave1() {  
  3.     Session session = HibernateUtil.getSessionFactory().getCurrentSession();  
  4.     session.beginTransaction();  
  5.     Person person;  
  6.     Phone phone;  
  7.   
  8.     for (int i = 0; i < 10; i++) {  
  9.         person = new Person(0"zhangsan" + i, 21 + i);  
  10.   
  11.         phone = new Phone(0"glay" + i, "Android智能手机", person);  
  12.         session.save(person);  
  13.         session.save(phone);  
  14.     }  
  15.     session.getTransaction().commit();  
  16. }  

进行一下下面的查询会看到,控制台输出大量的sql

[java]
  1. @Test  
  2. public void testQuery1() {  
  3.     Session session = HibernateUtil.getSessionFactory().getCurrentSession();  
  4.     session.beginTransaction();  
  5.     List<Phone> list = (List<Phone>) session.createQuery("from Phone")  
  6.             .list();  
  7.     for (Phone phone : list) {  
  8.         System.out.println(phone.getId()); //对关联的Person并不感兴趣   
  9.         // System.out.println(phone.getId()+"---"+phone.getPerson().getName());   
  10.     }  
  11.     session.getTransaction().commit();  
  12. }  

解决方案

1、还是在Phone.hbm.xml中把对于Person关联的映射属性不进行lazy属性的设置,默认为lazy加载

[html]
  1. <hibernate-mapping>  
  2.     <class name="com.akwolf.n_1.Phone" table="PHONE">  
  3.         <id name="id" type="int">  
  4.             <column name="ID" />  
  5.             <generator class="native" />  
  6.         </id>  
  7.         <property name="type" type="java.lang.String">  
  8.             <column name="TYPE" />  
  9.         </property>  
  10.         <property name="description" type="java.lang.String">  
  11.             <column name="DESCRIPTION" />  
  12.         </property>  
  13.         <!-- lazy=true -->  
  14.         <many-to-one name="person" class="com.akwolf.n_1.Person" fetch="join">  
  15.             <column name="PERSON_ID" />  
  16.         </many-to-one>  
  17.     </class>  
  18. </hibernate-mapping>  
2、在lazy=false的情况下,在hql中使用join fetch进行查询(session.createCriteria就是采用连接查询的方式),如:

[java]
  1. @Test  
  2. public void testQuery2() {  
  3.     Session session = HibernateUtil.getSessionFactory().getCurrentSession();  
  4.     session.beginTransaction();  
  5.     List<Phone> list = (List<Phone>) session.createQuery(  
  6.             "from Phone p left join fetch p.person per").list();  
  7.     for (Phone phone : list) {  
  8.         // System.out.println(phone.getId());   
  9.         System.out.println(phone.getId() + "---"  
  10.                 + phone.getPerson().getName());  
  11.     }  
  12.     session.getTransaction().commit();  
  13. }  
3、对于在Person中设置batch-size应该不算是一种解决方案,对于海量的数据,设置的一些batch-size对于大体来说是无关痛痒的。

ok,这就是小弟看视频对于1+N问题的一点理解,有理解更为深刻的大虾不吝赐教。。

相关内容