1、135 Hibernate一级缓存13.5Hibernate一级缓存缓存(Cache)是计算机领域非常通用的概念。它介于应用程序和永久性数据存储源(如硬盘上的文件或者数据库)之间,其作用是降低应用程序直接读写永久性数据存储源的频率,从而提高应用的运行性能。缓存中的数据是数据存储源中数据的副本,应用程序在运行时直接读写缓存中的数据,只在某些特定时刻按照缓存中的数据来同步更新数据存储源。技术要点正确使用缓存,可以大幅度提高系统的性能,但是错误使用缓存,反而会造成意想不到的结果,所以,理解Hibernate的缓存机制非常重要。Hibernate的缓存按照作用范围可以分为一级缓存和二级缓存,本节介绍一
2、级缓存。Hibernate的一级缓存是由Session提供的,因此它只存在于Session的生命周期中,当程序调用save()、update()、saveorupdate()等方法,及调用查询接口list、filter、iterate时,如session缓存中还不存在相应的对象,Hibernate会把该对象加入到一级缓存中,当Session关闭时,该Session所管理的一级缓存也会立即被清除。注意:Hibernate的一级缓存是Session所内置的,不能被卸载,也不能进行任何配置。一级缓存采用的是keyvalue的Map方式来实现的,在缓存实体对象时,对象的主关键字ID是Map的key,实
3、体对象就是对应的值。所以说,一级缓存是以实体对象为单位进行存储的,在访问时使用的是主关键字ID,虽然,Hibernate对一级缓存使用的是自动维护的功能,没有提供任何配置功能,但是可以通过Session中所提供的方法来对一级缓存的管理进行手工干预。本节代码演示Hibernate如下知识点:使用Hibernate一级缓存。了解Session使用缓存的常见方法。理解Session的load方法和get方法的区别。使用Log4j日志查看SQL执行情况。实现代码为了查看Hibernate如何访问数据库表,需要使用Log4j日志,读者可以将Hibernate安装包中projectetc目录下的log4j
4、.properties属性文件复制到项目路径中,默认的log4j.properties文件会造成大量的调试信息,这里对该属性文件进行了修改,只显示SQL访问信息,便于查看。log4j.properties文件内容代码如下:#定义标准输出#log4j.appender.stdout=org.apache.log4j.ConsoleAppenderlog4j.appender.stdout.Target=System.outlog4j.appender.stdout.layout=org.apache.log4j.PatternLayoutlog4j.appender.stdout.layout.
5、ConversionPattern=%dABSOLUTE%5p%c1:%L%m%n#定义日志文件hibernate.log#log4j.appender.file=org.apache.log4j.FileAppender#log4j.appender.file.File=hibernate.log#log4j.appender.file.layout=org.apache.log4j.PatternLayout#log4j.appender.file.layout.ConversionPattern=%dABSOLUTE%5p%c1:%L%m%n#设置日志优先级#log4j.rootLogg
6、er=warn, stdoutlog4j.rootLogger=debug, stdout#log4j.logger.org.hibernate=infolog4j.logger.org.hibernate=warn#log HQL query parser activity#log4j.logger.org.hibernate.hql.ast.AST=debug#log just the SQL显示SQL信息log4j.logger.org.hibernate.SQL=debug#log JDBC bind parameters#log4j.logger.org.hibernate.type
7、=infolog4j.logger.org.hibernate.type=warn#log schema export/update#log4j.logger.org.hibernate.tool.hbm2ddl=warn#log HQL parse treeslog4j.logger.org.hibernate.hql=debug#log cache activity#显示Hibernate缓存信息log4j.logger.org.hibernate.cache=debug#log transaction activitylog4j.logger.org.hibernate.transact
8、ion=warn#log JDBC resource acquisitionlog4j.logger.org.hibernate.jdbc=warn注意:Log4j的类库必须在CLASSPATH路径或者是项目的lib路径下,才能正常运行,默认的Hibernate类库已经包含了Log4j类库。增加1个Hibernate一级缓存的测试类:SessionCache.java,内容如下:package helloworld.session.test;import org.hibernate.Session;import org.hibernate.SessionFactory;import org.h
9、ibernate.cfg.Configuration;public class SessionCachepublic static void main(Stringargs)/Configuration管理Hibernate配置Configuration config=new Configuration().configure();/根据Configuration建立SessionFactory/SessionFactory用来建立SessionSessionFactory sessionFactory=config.buildSessionFactory();/建立session,相当于建立
10、JDBC的ConnectionSession session=sessionFactory.openSession();/同一个session中建立2个实体对象Box box1=(Box)session.get(Box.class, new Integer(1);/box2使用了session缓存,并重新没有访问数据库Box box2=(Box)session.get(Box.class, new Integer(1);session.close();/session关闭,session缓存随即清除System.out.println(第一个session关闭);session=session
11、Factory.openSession();/重新建立sessionSystem.out.println(创建第二个session);box1=(Box)session.get(Box.class, new Integer(1);/重新访问数据库session.clear();/清除session缓存box2=(Box)session.get(Box.class, new Integer(1);session.close();sessionFactory.close();/关闭sessionFactory运行该示例,结果如下:14:12:42,734 DEBUG SQL:346select b
12、ox0.id as id00,box0.width as width00,box0.length aslength00,box0.height as height00,box0.name as name00from ssh.box box0where box0.id=?第一个session关闭创建第二个session14:12:42,796 DEBUG SQL:346select box0.id as id00,box0.width as width00,box0.length aslength00,box0.height as height00,box0.name as name00from
13、 ssh.box box0where box0.id=?14:12:42,843 DEBUG SQL:346select box0.id as id00,box0.width as width00,box0.length aslength00,box0.height as height00,box0.name as name00from ssh.box box0where box0.id=?上面测试代码获得对象使用了session的get方法,下面将get方法替换为load方法,测试类为SessionCacheLoad.java,内容如下:package helloworld.session.
14、test;import org.hibernate.Session;import org.hibernate.SessionFactory;import org.hibernate.cfg.Configuration;public class SessionCacheLoadpublic static void main(Stringargs)/Configuration管理Hibernate配置Configuration config=new Configuration().configure();/根据Configuration建立SessionFactory/SessionFactory
15、用来建立SessionSessionFactory sessionFactory=config.buildSessionFactory();/建立session,相当于建立JDBC的ConnectionSession session=sessionFactory.openSession();/同一个session中建立2个实体对象Box box1=(Box)session.get(Box.class, new Integer(1);System.out.println(第一个box);/box2使用了session缓存,并重新没有访问数据库Box box2=(Box)session.get(B
16、ox.class, new Integer(1);System.out.println(第二个box);session.close();/session关闭,session缓存随即清除session=sessionFactory.openSession();System.out.println(创建第二个session);box1=(Box)session.load(Box.class, new Integer(1);/重新访问数据库System.out.println(第一个box);box1.getName();box2=(Box)session.load(Box.class, new I
17、nteger(1);System.out.println(第二个box);box2.getName();session.close();sessionFactory.close();/关闭sessionFactory运行该测试程序,结果如下:14:25:04,750 DEBUG SQL:346select box0.id as id00,box0.width as width00,box0.length aslength00,box0.height as height00,box0.name as name00from ssh.box box0where box0.id=?第一个box第二个b
18、ox创建第二个session第一个box14:25:04,906 DEBUG SQL:346select box0.id as id00,box0.width as width00,box0.length as length00,box0.height as height00,box0.name as name00from ssh.box box0where box0.id=?第二个box源程序解读(1)为了查看Hibernate对数据库的访问情况,使用了Log4j日志,log4j.properties属性文件中,设置log4j.logger.org.hibernate.SQL=debug,即
19、可输出SQL语句信息,本例中,同时会输出hql和cache信息,但是本例并没有配置Hibernate的二级缓存,所以不会有cache信息输出。(2)第一个测试程序中的第一个session中,有2个ID都是1的box对象,从控制台的日志可以看到,这个session只访问一次数据库。第一个box对象的数据是从数据库中查询而来的,然后该对象就保存在session的缓存中,当第二个box对象调用get方法时,首先检查session缓存中是否有ID为1的对象,如果有,就从缓存中获得数据,并不会再次访问数据库。(3)第二个session中,不同的是加入了clear方法,即将session缓存数据清空,这样
20、在创建第二个box对象时,由于缓存中找不到该对象,则会再次使用SQL通过JDBC查询数据库获得数据。(4)第二个测试程序帮助我们来区分load和get方法之间的区别。(5)使用get方法获得持久化对象时,首先查找session缓存(Hibernate一级缓存)是否有该对象,如果有,则获得该对象;如果没有,就会访问数据库,如果数据库中找不到数据,则返回null。(6)load方法也是获得数据,但是不同的地方是load方法已经假定数据库中一定存在该数据的,如果在数据库中找不到该数据,则会抛出一个org.hibernate.ObjectNotFoundException异常。(7)load方法获得对象的过程:load方法首先在session缓存中查找对象,如果找不到则查找sessionfactory缓存(Hibernate二级缓存),如果再找不到则访问数据库。(8)值得注意的是,load方法是假定数据库中一定有该数据的,所以使用代理来延迟加载对象,只有在程序中使用到了该对象的属性(非主键属性)时,Hibernate才会进入load方法的获得对象过程。所以说,如果数据库中不存在该记录,异常是在程序访问该对象属性时抛出的,而不是在创建这个对象时就抛出。(9)第二个程序中的第二个session中可以看到,只有在访问box对象的name属性时才执行数据库查询的,而不是在创建box1时执行。
copyright@ 2008-2023 冰点文库 网站版权所有
经营许可证编号:鄂ICP备19020893号-2