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

        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");//同一事务,缓存中有,控制台不打印sql
UserInfo user_getEvict_1=(UserInfo)genericDAO.loadById(UserInfo.class, "6");//同一事务,缓存中有,控制台不打印sql,取出后,清理出缓存
UserInfo user_getEvict_2=(UserInfo)genericDAO.loadById(UserInfo.class, "6");//同一事务,缓存中没有,从数据库中查,打印查询user_info的sql
UserInfo 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的sql
UserInfo user_get_2=(UserInfo)genericService.findById(UserInfo.class, "6");//打印查询user_info的sql
UserInfo user_getEvict_1=(UserInfo)genericService.loadById(UserInfo.class, "6");//打印查询user_info的sql
UserInfo user_getEvict_2=(UserInfo)genericService.loadById(UserInfo.class, "6");//打印查询user_info的sql
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);//false
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);//false
System.out.println(user_get_1==user_load_1);//false
String sex_get_1=user_get_1.getSex().getName();//懒加载异常 no session
String sex_load_1=user_load_1.getSex().getName();//懒加载异常 no session
String jobStatus_load_1=user_load_1.getJobStatus().getName();//懒加载异常 no session
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();//懒加载异常 no session
String name_load_2=user_load_2.getName();//懒加载异常 no session

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

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



 

 

 


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值