`
jinnianshilongnian
  • 浏览: 21499313 次
  • 性别: Icon_minigender_1
博客专栏
5c8dac6a-21dc-3466-8abb-057664ab39c7
跟我学spring3
浏览量:2417763
D659df3e-4ad7-3b12-8b9a-1e94abd75ac3
Spring杂谈
浏览量:3008052
43989fe4-8b6b-3109-aaec-379d27dd4090
跟开涛学SpringMVC...
浏览量:5638951
1df97887-a9e1-3328-b6da-091f51f886a1
Servlet3.1规范翻...
浏览量:259806
4f347843-a078-36c1-977f-797c7fc123fc
springmvc杂谈
浏览量:1597024
22722232-95c1-34f2-b8e1-d059493d3d98
hibernate杂谈
浏览量:250106
45b32b6f-7468-3077-be40-00a5853c9a48
跟我学Shiro
浏览量:5858142
Group-logo
跟我学Nginx+Lua开...
浏览量:701782
5041f67a-12b2-30ba-814d-b55f466529d5
亿级流量网站架构核心技术
浏览量:784896
社区版块
存档分类
最新评论

spring data jpa bug分析—初始化时EntityManager不关闭原因

 
阅读更多

环境

spring 3.2.3 RELEASE

spring data jpa 1.3.1.RELEASE

hibernate core  4.2.2.Final

 

问题

周末没事想把hibernate二级缓存监控集成到应用中,之前hibernate3.6的时候集成过(之前没用spring data jpa,所以怀疑是它的问题),不过为了和现有风格的统一,重新写了一遍,在写的过程中遇到一个问题:

hibernate Session打开的次数 比 Session关闭的次数 多很多次。而且这个比例是固定的,打开以前的hibernate3.6的发现没什么问题。打开和关闭次数是相等的才对。

 

经过源码跟踪,发现是spring data jpa,打开hibernate会话后没有关闭造成的。原因是spring data jpa在生成接口的QL时,需要去验证QL是否正确,此时它创建了Query但没有执行也没有释放造成的。

这是一个bug。已提交:https://jira.springsource.org/browse/DATAJPA-350

 

分析:

1、用过spring data jpa的朋友都应该知道,我们只需要写一些约定的接口并声称QL,或者在接口上使用如@Query指定QL,它能自动创建实现类并帮我们执行QL。此处不分析具体的步骤了。

2、在找到这些接口和@Qeury后,spring data jpa会验证这些QL是否正确。

3、最简单的是是使用SimpleQuery

org.springframework.data.jpa.repository.query.SimpleJpaQuery:

 

SimpleJpaQuery(JpaQueryMethod method, EntityManager em, String queryString) {
……

        // Try to create a Query object already to fail fast
        if (!method.isNativeQuery()) {
            try {
                em.createQuery(query.getQuery());
            } catch (RuntimeException e) {
                // Needed as there's ambiguities in how an invalid query string shall be expressed by the persistence provider
                // http://java.net/projects/jpa-spec/lists/jsr338-experts/archive/2012-07/message/17
                throw e instanceof IllegalArgumentException ? e : new IllegalArgumentException(e);
            }
        }
……
}

 大家已经注意到了,em.createQuery(query.getQuery()); 创建了Query 但没有执行,也没有关闭它。

 

 

4、因为我们spring data jpa 和spring集成了,所以注入的EntityManager是通过:

org.springframework.orm.jpa.SharedEntityManagerCreator创建的EntityManager代理对象;具体去参考源码;

5、当我们执行创建Query时:

 

// Regular EntityManager operations.
			boolean isNewEm = false;
			if (target == null) {
				logger.debug("Creating new EntityManager for shared EntityManager invocation");
				target = (!CollectionUtils.isEmpty(this.properties) ?
						this.targetFactory.createEntityManager(this.properties) :
						this.targetFactory.createEntityManager());
				isNewEm = true;
			}

			// Invoke method on current EntityManager.
			try {
				Object result = method.invoke(target, args);
				if (result instanceof Query) {
					Query query = (Query) result;
					if (isNewEm) {
						Class[] ifcs = ClassUtils.getAllInterfacesForClass(query.getClass(), this.proxyClassLoader);
						result = Proxy.newProxyInstance(this.proxyClassLoader, ifcs,
								new DeferredQueryInvocationHandler(query, target));
						isNewEm = false;
					}
					else {
						EntityManagerFactoryUtils.applyTransactionTimeout(query, this.targetFactory);
					}
				}
				return result;
			}
			catch (InvocationTargetException ex) {
				throw ex.getTargetException();
			}
			finally {
				if (isNewEm) {
					EntityManagerFactoryUtils.closeEntityManager(target);
				}
			}

 

此时大家注意到了,如果创建的是Query,isNewEm=false;而且Query也被代理了,那么finally不会关闭;

6、Query会执行完毕后执行关闭:

 

if (method.getName().equals("getResultList") || method.getName().equals("getSingleResult") ||
						method.getName().equals("executeUpdate")) {
					EntityManagerFactoryUtils.closeEntityManager(this.em);
				}

 

7、问题就出在这,我们第【3】步创建了Query但没有执行,造成Session一直不释放,所以造成二级缓存查到的关闭的Session数量比打开的少很多。

 

解决方案:

自己获取EntityManager执行并关闭,这样就不会有问题了。

 

        EntityManager target = null;
        // Try to create a Query object already to fail fast
        if (!method.isNativeQuery()) {
            try {
                target = em.getEntityManagerFactory().createEntityManager();
                target.createQuery(query.getQuery());
            } catch (RuntimeException e) {
                // Needed as there's ambiguities in how an invalid query string shall be expressed by the persistence provider
                // http://java.net/projects/jpa-spec/lists/jsr338-experts/archive/2012-07/message/17
                throw e instanceof IllegalArgumentException ? e : new IllegalArgumentException(e);
            } finally {
                EntityManagerFactoryUtils.closeEntityManager(target);
            }

 

看了一下spring data jpa 1.4版本,没有修复,下午提交下bug。 

 

 

另外使用hibernate 4做二级缓存监控的注意了:

 

hibernate4没有记录close Statement,即查看二级缓存的close Statement永远是0。也是个bug。

https://hibernate.atlassian.net/browse/HHH-8287 

 

12
5
分享到:
评论
15 楼 zhaoduo_79490175 2015-03-10  
请教一下,我tomcat日志里面时常会出现NoClassDefFoundError:EntityManagerFactoryUtils
可是我有这个orm的jar包,也可能是我关闭tomcat时候报的错误,这是什么原因呢?
14 楼 bolo 2014-05-13  
我遇到个这样的问题,两个表,一个user,一个role,user里面有个role_id,作了ManyToOne与role的关联,使用了Lazy加载策略,还有@LazyToOne(value = LazyToOneOption.PROXY)这个注解。spring data jpa是1.5.2,hibernate4.3.1,我在Junit里面测试一下这个延时加载是否有效果,结果控制台没打印出异常,但是Junit失败,抛出org.hibernate.LazyInitializationException: could not initialize proxy - no Session这个异常,这应该是代理对象延迟加载问题,请问下博主有没有遇到过这样的问题?这与spring data jpa有关吗?
13 楼 jacking124 2013-09-02  
jinnianshilongnian 写道
jacking124 写道
张sir!,Hibernate提交数据量大于40就开始提示NO row !!

这个还真没遇到过? 能给个示例吗  站内信我

给你发信息了!
12 楼 jinnianshilongnian 2013-09-02  
jacking124 写道
张sir!,Hibernate提交数据量大于40就开始提示NO row !!

这个还真没遇到过? 能给个示例吗  站内信我
11 楼 jacking124 2013-09-02  
张sir!,Hibernate提交数据量大于40就开始提示NO row !!
10 楼 jinnianshilongnian 2013-06-04  
dwangel 写道
感觉问题不在这里,如楼主所说 hibernate 3.6没有,hibernate 4有。
那么应该是hibernate发生变化导致的。

照理说 (!method.isNativeQuery())  非native的query不需要打开到实际数据库的链接,容器应当内部处理,而且对之有缓存以提高效率。

可能是hibernate内部实现,或者配置变化导致的。

hibernate4 确实和 3 内部发生了很大变化。 不过确实是spring data jpa引起的问题
9 楼 jinnianshilongnian 2013-06-04  
dwangel 写道
感觉问题不在这里,如楼主所说 hibernate 3.6没有,hibernate 4有。
那么应该是hibernate发生变化导致的。

照理说 (!method.isNativeQuery())  非native的query不需要打开到实际数据库的链接,容器应当内部处理,而且对之有缓存以提高效率。

可能是hibernate内部实现,或者配置变化导致的。

不好意思

估计是我说的不明白,我用hibernate3.6时 并没有使用jpa,也就是说按理说跟hibernate实现应该无关。所以可能出问题的地方是spring data jpa


此处createQuery的目的是:验证ql是否正确,
8 楼 dwangel 2013-06-04  
感觉问题不在这里,如楼主所说 hibernate 3.6没有,hibernate 4有。
那么应该是hibernate发生变化导致的。

照理说 (!method.isNativeQuery())  非native的query不需要打开到实际数据库的链接,容器应当内部处理,而且对之有缓存以提高效率。

可能是hibernate内部实现,或者配置变化导致的。
7 楼 qmzpanda 2013-06-03  
 
6 楼 blueram 2013-06-03  
非常感谢
5 楼 jinnianshilongnian 2013-06-03  
blueram 写道
请问你的缓存监控是怎么做的呀,想试试

我现在做的不复杂,主要功能:事务的成功数,命中率等,二级缓存的清理,QL的执行等

简单的截图


具体可参考
https://github.com/zhangkaitao/es/tree/master/web/src/main/webapp/WEB-INF/jsp/admin/monitor
4 楼 blueram 2013-06-03  
请问你的缓存监控是怎么做的呀,想试试
3 楼 jinnianshilongnian 2013-06-03  
lengyun3566 写道
好文章  目前正在翻译一本书《Spring Data》,看来还得往深了学习一下

我现在翻译websocket规范 停留在第八章 很久了   
2 楼 jinnianshilongnian 2013-06-03  
lengyun3566 写道
好文章  目前正在翻译一本书《Spring Data》,看来还得往深了学习一下

很高效啊,翻译那么多了
1 楼 lengyun3566 2013-06-03  
好文章  目前正在翻译一本书《Spring Data》,看来还得往深了学习一下

相关推荐

    Spring Data JPA中文文档[1.4.3]_springdatajpa_erlang_waitxpf_

    开发者可以通过 `EntityManager` 和 `EntityManagerFactory` 进行这些操作,但 Spring Data JPA 提供的 Repository 接口让这些操作更加简洁。 3. **Query Methods**:Spring Data JPA 支持通过方法名自动转换为 JPA...

    Spring Data JPA API(Spring Data JPA 开发文档).CHM

    Spring Data JPA API。 Spring Data JPA 开发文档。 官网 Spring Data JPA API。

    Spring Data JPA 笔记

    例如,Spring Data JPA支持自动化的查询生成,只需定义Repository接口,无需编写任何实现代码,就可以执行CRUD(创建、读取、更新、删除)操作。此外,它还支持复杂的查询方法命名,如findByXXX,根据方法名自动构建...

    spring注解+spring data jpa文档+JPA文档.rar

    Spring框架的核心特性包括依赖注入(DI)和面向切面编程(AOP),并且它还提供了对数据库操作的支持,这主要通过Spring Data JPA和Java Persistence API(JPA)实现。 Spring注解是Spring框架中的一大特色,它极大...

    Spring Data JPA的优点和难点.pdf

    Spring Data JPA是Spring生态中的一个强大ORM框架,它极大地提高了Java开发者在处理数据库操作时的效率。Spring Data JPA的主要优点在于其高度的开发效率、成熟的语法结构以及与Spring框架的紧密集成。 1. **开发...

    Spring Data JPA从入门到精通

    'SpringDataJPA从入门到精通'分为12章 内容包括整体认识JPA、JPA基础查询方法、定义查询方法、注解式查询方法、@Entity实例里面常用注解详解、JpaRepository扩展详解、JPA的MVC扩展REST支持、DataSource的配置、乐观...

    spring data jpa 教程

    Spring Data JPA 是一个基于 Java 的开源框架,它属于 Spring Data 家族的一部分,旨在简化 Java 应用中的数据访问层代码,特别针对基于 JPA(Java Persistence API)的数据持久化操作。本教程将详细介绍 Spring ...

    Spring Data JPA Demo

    Spring Data JPA 是一个强大的框架,它简化了与Java Persistence API (JPA) 的交互,JPA 是Java 开发者用来管理和持久化应用程序数据的一种标准。在这个“Spring Data JPA Demo”项目中,我们将深入探讨如何利用...

    Spring Data JPA.zip

    **Spring Data JPA 深度解析** Spring Data JPA 是 Spring Framework 的一个重要模块,它为 Java ...掌握 Spring Data JPA 能够显著提高开发效率,降低数据访问层的复杂性,是现代 Java Web 开发不可或缺的一项技能。

    springdatajpa.pdf

    SpringDataJPA是Spring框架中用于简化数据持久层操作的一个模块,它基于Java持久层API(Java Persistence API,JPA)...这样的特性,使得SpringDataJPA成为了Java开发者在进行数据持久化层开发时的一个非常重要的选择。

    手动创建 SpringMvc +SpringDataJpa+Hibernate+ freemarker mavenProject+ 环境切换 webDemo

    在本项目中,我们主要探讨如何手动构建一个基于SpringMVC、Spring Data JPA、Hibernate以及FreeMarker模板引擎的Maven工程,同时实现环境切换功能。这个基础框架为日常开发工作提供了必要的支持。 首先,SpringMVC...

    Spring Data JPA中文文档[1.4.3].zip

    Spring Data JPA是Java开发中的一个关键框架,它简化了与关系型数据库的交互,特别是基于Java Persistence API (JPA)。这个框架是Spring生态系统的组成部分,为开发者提供了声明式数据访问的方式,允许通过简单的...

    springBoot整合springData JPA

    Spring Boot简化了应用的初始搭建以及配置,而Spring Data JPA则是Spring Framework的一个模块,专门用于简化JPA(Java Persistence API)的使用,提供了一种声明式的方式来操作数据库。 首先,我们需要了解Spring ...

    spring data jpa简单案例

    Spring Data JPA 提供了自动化的 CRUD 操作接口。创建一个继承自 `JpaRepository` 的接口,例如 `UserRepository.java`: ```java import org.springframework.data.jpa.repository.JpaRepository; public ...

    spring data jpa + spring + json demo

    在使用Spring Data JPA时,开发者无需编写大量的DAO(Data Access Object)层代码,只需要定义Repository接口,Spring Data JPA就会自动生成实现。这极大地提高了开发效率和代码的可维护性。此外,通过集成Spring,...

    Spring Boot整合SpringDataJPA

    在现代Java Web开发中,Spring Boot框架以其便捷的配置、快速的应用启动以及强大的依赖管理而备受推崇。...通过灵活的配置和强大的功能,Spring Data JPA成为Java开发者在处理数据持久化时的首选工具。

    其实spring data jpa比mybatis更好用.zip_JPA mybatis

    标题"其实spring data jpa比mybatis更好用.zip_JPA mybatis"指出本主题将探讨Spring Data JPA与MyBatis之间的比较,并暗示在某些方面,Spring Data JPA可能更为优越。描述中提到"全方位介绍jpa",表明内容将深入讲解...

    spring学习:spring data jpa

    Spring Data JPA是Spring框架的一个模块,主要目的是简化Java企业级应用中数据访问层的开发。这个框架构建在JPA(Java Persistence API)之上,提供了一种声明式的方式来操作数据库,使得开发者无需编写大量的SQL...

Global site tag (gtag.js) - Google Analytics