只要在HQL中指定了外连接,那么不论配置文件中是预先抓取或是立即或延迟检索,都会失效。
以上面为例:
学生对身份证采取预先抓取和立即检索,对班级是预先抓取,班级对学生是延迟加载。
Session session = SessionUtil.getSession();
Transaction tran = session.beginTransaction();
Query query = session.createQuery("from Student as s left join s.teamID");
List list = query.list();
tran.commit();
session.close();
Object st[] = (Object[])list.get(0);
Student s = (Student) st[0];
System.out.println(s.getName());
System.out.println(s.getTeamID().getName());
System.out.println(s.getTeamID().getStudents().size());
在查询的LIST中包含两个长度为2的数组,每一个数组包含两个对象,学生对象和班级对象。
打印:
1.Hibernate: select student0_.ID as ID1_0_, team1_.ID as ID0_1_, student0_.Name as Name1_0_, student0_.TeamID as TeamID1_0_, team1_.Name as Name0_1_ from test.student student0_ left outer join test.team team1_ on student0_.TeamID=team1_.ID
2.Hibernate: select card0_.ID as ID2_0_, card0_.StudentID as StudentID2_0_, card0_.Name as Name2_0_ from test.card card0_ where card0_.ID=?
3.Hibernate: select card0_.ID as ID2_0_, card0_.StudentID as StudentID2_0_, card0_.Name as Name2_0_ from test.card card0_ where card0_.ID=?
tuping
Exception in thread "main" org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.vo.Team.students, no session or session was closed
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:358)
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:350)
at org.hibernate.collection.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:97)
at org.hibernate.collection.PersistentSet.size(PersistentSet.java:139)
at com.test.Client1.main(Client1.java:28)
二中
对1的解释:因为采取的是HQL中的左连接的方式,所以会忽略配置中预先抓取的方式。
对2.3的解释:在配置文件中的检索策略只能影响到session.get()或load方法,对于直接使用HQL的方式将忽略配置文件的预先抓取的检索策略,而是直接采取HQL中的方式。
1)得到学生对象以后,学生对身份证的预先抓取策略失效,二是采取的是立即加载得到身份证。
2)得到班级对象以后,由于班级对学生采用了延迟加载(这个策略将不会忽略),于是班级的学生集合并没有得到初始化。所以才打印出例外。
将班级对学生的加载方式改为立即加载就OK了。
打印:
1.Hibernate: select student0_.ID as ID1_0_, team1_.ID as ID0_1_, student0_.Name as Name1_0_, student0_.TeamID as TeamID1_0_, team1_.Name as Name0_1_ from test.student student0_ left outer join test.team team1_ on student0_.TeamID=team1_.ID
2.Hibernate: select card0_.ID as ID2_0_, card0_.StudentID as StudentID2_0_, card0_.Name as Name2_0_ from test.card card0_ where card0_.ID=?
3.Hibernate: select card0_.ID as ID2_0_, card0_.StudentID as StudentID2_0_, card0_.Name as Name2_0_ from test.card card0_ where card0_.ID=?
4.Hibernate: select students0_.teamID as teamID3_, students0_.ID as ID3_, students0_.ID as ID1_2_, students0_.Name as Name1_2_, students0_.TeamID as TeamID1_2_, team1_.ID as ID0_0_, team1_.Name as Name0_0_, card2_.ID as ID2_1_, card2_.StudentID as StudentID2_1_, card2_.Name as Name2_1_ from test.student students0_ left outer join test.team team1_ on students0_.TeamID=team1_.ID left outer join test.card card2_ on students0_.ID=card2_.ID where students0_.teamID=?
tuping
二中
2
这里有个疑问,因为第2.3已经得到了CARD的对象,为什么第4句还要去数据库取一次数据。这是因为第一次发送SQL语句从数据库取身份证对象时,学生对身份证采取的是预先抓取被忽略,所以采取了立即加载的策略。
而班级对学生采用HQL指定的左连接取得对象。
虽然班级和学生对象都取得了。但是由于集合的特殊性,还需要取得集合对学生对象的引用。由于学生对身份证采取的是预先抓取,所有才会有第4句。
如果在语句中改用Fetch进行连接
Session session = SessionUtil.getSession();
Transaction tran = session.beginTransaction();
Query query = session.createQuery("from Student as s left join fetch s.teamID");
List list = query.list();
tran.commit();
session.close();
// Object st[] = (Object[])list.get(0);这种得到对象的方式就是错的。
//Student s = (Student) st[0];
//通过查询虽然与上面生成的SQL语句是一样的,但是再内存中得到的对象却发生了变化。此时检索出来的是两个学生元素。所以通过LIST得到的集合是学生的集合。
Student s = (Student) list.get(0);
System.out.println(s.getName());
System.out.println(s.getTeamID().getName());
System.out.println(s.getTeamID().getStudents().size());
HQL忽略配置文件预先抓取的深度。
如果HQL语句中出现了外连接from Team t left outer join t.students
则班级对学生的预先抓取的策略将被HQL指定的左外连接所覆盖。
举例:
学生对身份证采取预先抓取和立即加载,学生对班级采取预先抓取,班级对学生采取预先抓取和延迟检索。
通过查寻得到的结果是学生对身份证的预先抓取也被忽略了。HQL忽略配置文件预先抓取策略深度就是HQL语句中指定的对象,HIBERNATE对这些对象直接的属性配置忽略预先抓取,采取立即或延迟检索的策略。假设身份证还关联其他对象,则这些对象的抓取策略按照配置文件的设定,预先抓取不会被忽略。
总结:
1.仅仅从使用角度来看,预先抓取和立即检索的效果一样,只不过预先抓取可以减少SQL语句条数。
2.预先抓取的关键字是JOIN和FETCH,而外连接的关键字是JOIN
3.预先抓取将初始化代理对象的引用,吧对象的数据填充完毕,但是外连接只是把对象组装好,不会初始化对象之间的引用关系。