一段代码教你看懂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也是可以不用的。
- 一段代码教你看懂Hibernate一级缓存与懒加载
- Hibernate一级缓存与二级缓存
- Hibernate一级缓存与二级缓存
- Hibernate---一级缓存与二级缓存
- Hibernate一级缓存与二级缓存
- hibernate一级缓存与快照
- Hibernate-对象状态、一级缓存和懒加载
- Hibernate (七) session常用方法、懒加载、一级缓存
- Hibernate中的一级缓存、二级缓存和懒加载
- Hibernate中的一级缓存、二级缓存和懒加载
- Hibernate中的一级缓存、二级缓存和懒加载
- 一段代码看懂Java类的加载和初始化
- Hibernate中一级缓存与二级缓存
- Hibernate一级缓存与二级缓存的解析
- Hibernate的一级缓存与二级缓存
- hibernate的一级缓存与二级缓存
- hibernate一级缓存介绍与实践
- Hibernate一级缓存【Hibernate】
- IOS-plist文件DES加密
- 日期处理
- oracle中常用函数大全
- 如何获得Hibernate的数据库底层异常
- Thermal计算方式小结
- 一段代码教你看懂Hibernate一级缓存与懒加载
- 页面跳转
- 乐观锁更新失败重试解决方案
- 刚体
- IOS 中禁止横屏
- MAC Tree 实现的方法(NSOutlineView) 2 (实现了简单的选择和动态修改不同的数据)
- APCI ASL
- 【北风网】十一大放“价”,折上折,全场IT课程3折起
- Javascript中parseInt在XSS中的应用