懒加载机制(lazy)的意思就是延迟加载。今天对这个lazy进行了一次测试。
mysql数据库表
CREATE TABLE `teacher` ( `teacher_Id` int(20) unsigned NOT NULL AUTO_INCREMENT, `teacher_Name` varchar(100) DEFAULT NULL, PRIMARY KEY (`teacher_Id`) ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8; CREATE TABLE `student` ( `student_Id` int(20) unsigned NOT NULL AUTO_INCREMENT, `student_Name` varchar(100) DEFAULT NULL, `teacher_Id` int(20) unsigned, PRIMARY KEY (`student_Id`), CONSTRAINT `student_teacher_foreignKey` FOREIGN KEY (`teacher_Id`) REFERENCES `teacher` (`teacher_Id`) ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8;
然后dao类和pojo类均是使用了hibernate逆向工程生成的标准类。可参考博文 http://my.oschina.net/u/1863518/blog/325412 代码结构基本一致,只是字段名不同。同时这篇博文也是对其的一个补充。
其中Student类属性
public class Student implements java.io.Serializable { // Fields private Integer studentId; private Teacher teacher; private String studentName; }
Teacher类属性
public class Teacher implements java.io.Serializable { // Fields private Integer teacherId; private String teacherName; private Set students = new HashSet(0); }
数据库内数据
+------------+--------------+------------+
| Student_id | Student_name | Teacher_id |
+------------+--------------+------------+
| 3 | student_one | 3 |
| 4 | student_two | 4 |
+------------+--------------+------------+
+------------+--------------+
| Teacher_id | Teacher_name |
+------------+--------------+
| 3 | teacher_one |
| 4 | teacher_two |
+------------+--------------+
在hibernatelazy有三个取值,false proxy no-proxy。现在分别对这三种情况进行测试。
测试代码
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); StudentDAO studentDAO = StudentDAO.getFromApplicationContext(ac); TeacherDAO teacherDAO = TeacherDAO.getFromApplicationContext(ac); Student temp = studentDAO.findById(3); System.out.println(temp); System.out.println(temp.getTeacher());
注意,这里的每种情况均对one-to-many 和 many-to-one进行了修改
lazy="false"
输出结果
bean.Student@1928dc07 bean.Teacher@4f953cc
lazy="proxy"
输出结果
bean.Student@78214ca6 Exception in thread "main" org.hibernate.LazyInitializationException: could not initialize proxy - no Session at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:149) at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:195) at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:185) at bean.Teacher_$$_javassist_1.toString(Teacher_$$_javassist_1.java) at java.lang.String.valueOf(String.java:2854) at java.io.PrintStream.println(PrintStream.java:821) at dao.LazyMain.main(LazyMain.java:18)
lazy="no-proxy"
输出结果
bean.Student@680849bd Exception in thread "main" org.hibernate.LazyInitializationException: could not initialize proxy - no Session at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:149) at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:195) at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:185) at bean.Teacher_$$_javassist_1.toString(Teacher_$$_javassist_1.java) at java.lang.String.valueOf(String.java:2854) at java.io.PrintStream.println(PrintStream.java:821) at dao.LazyMain.main(LazyMain.java:18)
首先对比false 和非false的区别,当设置为非false的时候,hibernate的懒加载机制在查询获取结果的过程中如果发现该对象中存在其他的对象的引用,或在其被其他对象引用。那么hibernate并不会立刻就加载,即Student类中的teacher对象和Teacher类的students对象都不会被加载,它们只会存在一个代理,使用debug可以看到Student类中teacher的属性为
teacher Teacher_$$_javassist_1 (id=70)
Teacher类中的students的属性为
students PersistentSet (id=67)
两个都无法使用,均为代理生成的类,teacher对象所有属性均为null。而students对象里面大量的属性也为null,似乎整个set接口都没被初始化。(set的实例化过程没明白,后续会对代理类进行一次跟踪检测)
而其他资料所说的能够根据需要的时候自动加载也没发现可以加载成功,只要一访问到这两个属性都会报
could not initialize proxy - no Session 错误。 觉得可能是spring集成了hibernate在dao层对数据库的访问是
public Teacher findById(java.lang.Integer id) { log.debug("getting Teacher instance with id: " + id); try { Teacher instance = (Teacher) getCurrentSession().get("bean.Teacher", id); return instance; } catch (RuntimeException re) { log.error("get failed", re); throw re; } }
考虑到可能是getCurrentSession() 方法,
public void setSessionFactory(SessionFactory sessionFactory) { System.out.println(sessionFactory.getClass().getName()); this.sessionFactory = sessionFactory; } private Session getCurrentSession() { return sessionFactory.getCurrentSession(); }
再setSessionFactory中输出传入的参数sessionFactory
发现是来自org.hibernate.internal.SessionFactoryImpl而不是再applicationContext.xml中的org.springframework.orm.hibernate4.LocalSessionFactoryBean,猜测可能是加载配置文件的时候生成了SessionFactoryImpl对象再传入
进入到SessionFactoryImpl中查看源码,发现
public Session getCurrentSession() throws HibernateException { if ( currentSessionContext == null ) { throw new HibernateException( "No CurrentSessionContext configured!" ); } return currentSessionContext.currentSession(); }
发现currentSessionContext,继续寻找,
currentSessionContext = buildCurrentSessionContext(); private CurrentSessionContext buildCurrentSessionContext() { .... }
再往下暂时看不明白,查阅了其他资料之后,在http://lijiejava.iteye.com/blog/733971
对于getCurrentSession()方法:
(1)其所在方法必须进行事务控制
(2)Session在第一次被使用的时候,或者第一次调用getCurrentSession()的时候,其生命周期就开始。然后它被 Hibernate绑定到当前线程。当事务结束的时候,不管是提交还是回滚,Hibernate也会把Session从当前线程剥离,并且关闭它。假若你 再次调用getCurrentSession(),你会得到一个新的Session,并且开始一个新的工作单元。
对为什么没有自动加载作出了一个解释,后续还会对整个hibernate和spring集成源码进行一次分析,再次分析下它们的机制。
在继续往下测试proxy和no-proxy的区别时,从网上的资料上看,它们的区别在于能否访问到many-to-one字段的属性.但是访问这个字段的时候都会报no-session错误。
到此,这个测试到此告一段落,发现对整个hibernate的机制还是不是特别熟悉,尤其是配置文件这一块,因为没有系统的看过,感觉还是许多东西不明白,希望有大牛可以指导一下,推荐一些优秀的资料。