一段代码教你看懂Hibernate一级缓存与懒加载

来源:互联网 发布:百度竞价推广优化 编辑:程序博客网 时间:2024/05/22 00:29

        Hibernate一级缓存是session级别的缓存,是事务级别的缓存,由Hibernate自己管理。本文想通过一段代码的输出结果来说明一下Hibernate一级缓存与懒加载。代码所在的开发环境为Spring MVC+Spring+Hibernate其中事务控制在Service层。

     开始上关键代码(这段代码放到了Controller,如果放到Service将genericService改成genericDAO):    

  UserInfo user_get_1=(UserInfo)genericService.findById(UserInfo.class, "6");  UserInfo user_get_2=(UserInfo)genericService.findById(UserInfo.class, "6");  UserInfo user_getEvict_1=(UserInfo)genericService.loadById(UserInfo.class, "6");  UserInfo user_getEvict_2=(UserInfo)genericService.loadById(UserInfo.class, "6");  UserInfo user_load_1=(UserInfo)genericService.loadObjectById(UserInfo.class, "6");  UserInfo user_load_2=(UserInfo)genericService.loadObjectById(UserInfo.class, "6");  System.out.println(user_get_1==user_getEvict_1);  System.out.println(user_getEvict_1==user_getEvict_2);  System.out.println(user_get_1==user_getEvict_2);  System.out.println(user_load_1==user_load_2);  System.out.println(user_get_1==user_load_1);  String sex_get_1=user_get_1.getSex().getName();  String sex_load_1=user_load_1.getSex().getName();  String jobStatus_load_1=user_load_1.getJobStatus().getName();  String name_get_1=user_get_1.getName();  String name_getEvict_2=user_getEvict_2.getName();  String id=user_get_1.getId();  String name_load_1=user_load_1.getName();  String name_load_2=user_load_2.getName();    

其中findById,loadById,loadObjectById三种方法分别为:

 @SuppressWarnings("unchecked") public Object findById(final Class c, final Serializable id) {  return this.getHibernateTemplate().get(c, id); }
 public Object loadById(Class c, Serializable id) {  Object o=this.getHibernateTemplate().get(c, id);  this.getHibernateTemplate().evict(o);  return o; }
@SuppressWarnings("unchecked")public Object loadObjectById(final Class c, final Serializable id) {return this.getHibernateTemplate().load(c, id);}

 

附上UserInfo实体类,关联了外键字典,且设置为懒加载

  @Entity@Table(name = "USER_INFO")@JsonIgnoreProperties(value = { "hibernateLazyInitializer", "handler", "fieldHandler" })public class UserInfo implements Serializable {
<p> private static final long serialVersionUID = 2891353226199297040L;</p><p> @Id @Column(name = "id", length = 36) @GeneratedValue(generator = "uuid") @GenericGenerator(name = "uuid", strategy = "org.hibernate.id.UUIDGenerator") @JsonProperty("id") private String id;</p><p> /** 姓名 **/ @ForeignShow @Header(name = "姓名") @Column(name = "name", length = 100) private String name;</p><p> /** 性别 */ @Header(name = "性别") @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "dictSex") private Dict sex;  /** 岗位状态 **/ @Header(name = "岗位状态") @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "dictJobStatus") private Dict jobStatus;</p>    

把关键代码放在不同的地方,是否配置OpenSessionInViewFilter来看代码的输出情况。

1、代码放在Service层,配置了OpenSessionInViewFilter

这里先列出Web.xml中OpenSessionInViewFilter的配置,主要是延迟Session生命周期用。

        <filter>  <filter-name>hibernateFilter</filter-name><filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class><init-param><param-name>sessionFactoryBeanName</param-name><param-value>sessionFactory</param-value></init-param><init-param><param-name>flushMode</param-name><param-value>AUTO</param-value></init-param></filter><filter-mapping><filter-name>hibernateFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping> 


关键代码输出:

UserInfo user_get_1=(UserInfo)genericDAO.findById(UserInfo.class, "6");//首次执行,控制台打印出一条查询user_info的sql,并缓存UserInfo user_get_2=(UserInfo)genericDAO.findById(UserInfo.class, "6");//同一事务,缓存中有,控制台不打印sqlUserInfo user_getEvict_1=(UserInfo)genericDAO.loadById(UserInfo.class, "6");//同一事务,缓存中有,控制台不打印sql,取出后,清理出缓存UserInfo user_getEvict_2=(UserInfo)genericDAO.loadById(UserInfo.class, "6");//同一事务,缓存中没有,从数据库中查,打印查询user_info的sqlUserInfo user_load_1=(UserInfo)genericDAO.loadObjectById(UserInfo.class, "6");//生成代理对象,并缓存,控制台不打印sql,UserInfo user_load_2=(UserInfo)genericDAO.loadObjectById(UserInfo.class, "6");//同一事务,缓存中已有代理对象System.out.println(user_get_1==user_getEvict_1);//true 缓存中用的同一个对象System.out.println(user_getEvict_1==user_getEvict_2);//false 从缓存中清除后,重新取得对象System.out.println(user_get_1==user_getEvict_2);//false 从缓存中清除后,重新取得对象System.out.println(user_load_1==user_load_2);//true 第二次取得也是缓存中的代理对象,同一个对象System.out.println(user_get_1==user_load_1);//false 一个是直接取得对象,一个是代理对象String sex_get_1=user_get_1.getSex().getName();//懒加载,控制台打印查询字典表dict的sql,并缓存String sex_load_1=user_load_1.getSex().getName();//在控制台打印查询user_info的sql,没有打印查询字典表dict的sql,说明在缓存中找到了String jobStatus_load_1=user_load_1.getJobStatus().getName();//打印查询字典表dict的sql,并缓存String name_get_1=user_get_1.getName();//控制台无输出String name_getEvict_2=user_getEvict_2.getName();//控制台无输出String id=user_get_1.getId();//控制台无输出String name_load_1=user_load_1.getName();//控制台无输出String name_load_2=user_load_2.getName();//控制台无输出,因为和user_load_1是同一个缓存对象

       通过以上的分析可知,通过id获取对象,有get和load两种方式,get会查询数据库,load不会立即查询数据库,而是生成代理对象。两者获得的对象都会以id(不是数据库的主键)为键缓存起来,后续如果查询,在缓存中找到则不会连接数据库查询,从而提高性能。

       配置成的懒加载的字典,在查询user_info表时,并不会去字典表查找,而是在使用到的时候getSex().getName()去查询数据库,(getSex()不会去查询数据库)。并将该结果缓存。即便user_load_1.getSex().getName()时,只会查询user_info表,而没有查询字典表,是因为缓存中已经有该对象了。

2、代码放在Controller层,配置了OpenSessionInViewFilter

     把代码提升到Controller层,会是个什么结果呢?实际上输出结果和1是一样的,为了在Controller层使用懒加载,必须将Session的生命周期从一个事务提升到Request、Resopnse(即Controller的一个请求方法),而配置的OpenSessionInViewFilter正是起到延长Session生命周期的作用。

 

3、代码放在Service层,不配置OpenSessionInViewFilter

     因为OpenSessionInViewFilter不会影响事务控制层Service,所以结果还是和1是一样的。

4、代码放在Controller层,不配置OpenSessionInViewFilter

     这次结果是不一样的,先看结果:     

UserInfo user_get_1=(UserInfo)genericService.findById(UserInfo.class, "6");//打印查询user_info的sqlUserInfo user_get_2=(UserInfo)genericService.findById(UserInfo.class, "6");//打印查询user_info的sqlUserInfo user_getEvict_1=(UserInfo)genericService.loadById(UserInfo.class, "6");//打印查询user_info的sqlUserInfo user_getEvict_2=(UserInfo)genericService.loadById(UserInfo.class, "6");//打印查询user_info的sqlUserInfo user_load_1=(UserInfo)genericService.loadObjectById(UserInfo.class, "6");//控制台无输出UserInfo user_load_2=(UserInfo)genericService.loadObjectById(UserInfo.class, "6");//控制台无输出System.out.println(user_get_1==user_getEvict_1);//falseSystem.out.println(user_getEvict_1==user_getEvict_2);//falseSystem.out.println(user_get_1==user_getEvict_2);//falseSystem.out.println(user_load_1==user_load_2);//falseSystem.out.println(user_get_1==user_load_1);//falseString sex_get_1=user_get_1.getSex().getName();//懒加载异常 no sessionString sex_load_1=user_load_1.getSex().getName();//懒加载异常 no sessionString jobStatus_load_1=user_load_1.getJobStatus().getName();//懒加载异常 no sessionString name_get_1=user_get_1.getName();//正常执行,控制台无输出String name_getEvict_2=user_getEvict_2.getName();//正常执行,控制台无输出String id=user_get_1.getId();//正常执行,控制台无输出String name_load_1=user_load_1.getName();//懒加载异常 no sessionString name_load_2=user_load_2.getName();//懒加载异常 no session

       在Controller中因为没有配置OpenSessionInViewFilter,每一次取数据都会得到一个新的对象,在Service层Session关闭,懒加载也会抛出no session的异常。

      在实际开发中,看到很多系统都配置了OpenSessionInViewFilter,将Session生命周期延长到了控制层,在使用set方法赋值后会发现数据库中的值也改变了。这是因为在Session关闭前,如果发现缓存和内存对象不一致时会将变化同步到数据库(即使不调用update方法也会同步)。其实如果好好规划,把该写到Service层的代码写到Service层,OpenSessionInViewFilter也是可以不用的。



 

 

 


 

0 0
原创粉丝点击