spring boot JPA 数据库连接池释放
当JPA获取数据库数据连接时,如果连接数超过最大连接数的配置,系统就会报错:
Unable to acquire JDBC Connection
和:
Caused by: java.sql.SQLTransientConnectionException: HikariPool- - Connection is not available, request timed out after 30002ms.
at com.zaxxer.hikari.pool.HikariPool.createTimeoutException(HikariPool.java:) ~[HikariCP-3.2..jar:?]
at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:) ~[HikariCP-3.2..jar:?]
at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:) ~[HikariCP-3.2..jar:?]
at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:) ~[HikariCP-3.2..jar:?]
at com.zaxxer.hikari.HikariDataSource$$FastClassBySpringCGLIB$$eeb1ae86.invoke(<generated>) ~[HikariCP-3.2..jar:?]
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:) ~[spring-core-5.1..RELEASE.jar:5.1..RELEASE]
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:) ~[spring-aop-5.1..RELEASE.jar:5.1..RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:) ~[spring-aop-5.1..RELEASE.jar:5.1..RELEASE]
at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:) ~[spring-aop-5.1..RELEASE.jar:5.1..RELEASE]
at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:) ~[spring-aop-5.1..RELEASE.jar:5.1..RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:) ~[spring-aop-5.1..RELEASE.jar:5.1..RELEASE]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:) ~[spring-aop-5.1..RELEASE.jar:5.1..RELEASE]
at com.zaxxer.hikari.HikariDataSource$$EnhancerBySpringCGLIB$$5e528ef9.getConnection(<generated>) ~[HikariCP-3.2..jar:?]
at org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl.getConnection(DatasourceConnectionProviderImpl.java:) ~[hibernate-core-5.3..Final.jar:5.3..Final]
at org.hibernate.internal.NonContextualJdbcConnectionAccess.obtainConnection(NonContextualJdbcConnectionAccess.java:) ~[hibernate-core-5.3..Final.jar:5.3..Final]
at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.acquireConnectionIfNeeded(LogicalConnectionManagedImpl.java:) ~[hibernate-core-5.3..Final.jar:5.3..Final]
at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.getPhysicalConnection(LogicalConnectionManagedImpl.java:) ~[hibernate-core-5.3..Final.jar:5.3..Final]
at org.hibernate.engine.jdbc.internal.StatementPreparerImpl.connection(StatementPreparerImpl.java:) ~[hibernate-core-5.3..Final.jar:5.3..Final]
at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$.doPrepare(StatementPreparerImpl.java:) ~[hibernate-core-5.3..Final.jar:5.3..Final]
at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$StatementPreparationTemplate.prepareStatement(StatementPreparerImpl.java:) ~[hibernate-core-5.3..Final.jar:5.3..Final]
at org.hibernate.engine.jdbc.internal.StatementPreparerImpl.prepareQueryStatement(StatementPreparerImpl.java:) ~[hibernate-core-5.3..Final.jar:5.3..Final]
at org.hibernate.loader.Loader.prepareQueryStatement(Loader.java:) ~[hibernate-core-5.3..Final.jar:5.3..Final]
at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:) ~[hibernate-core-5.3..Final.jar:5.3..Final]
at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:) ~[hibernate-core-5.3..Final.jar:5.3..Final]
at org.hibernate.loader.Loader.doQuery(Loader.java:) ~[hibernate-core-5.3..Final.jar:5.3..Final]
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:) ~[hibernate-core-5.3..Final.jar:5.3..Final]
at org.hibernate.loader.Loader.doList(Loader.java:) ~[hibernate-core-5.3..Final.jar:5.3..Final]
at org.hibernate.loader.Loader.doList(Loader.java:) ~[hibernate-core-5.3..Final.jar:5.3..Final]
at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:) ~[hibernate-core-5.3..Final.jar:5.3..Final]
at org.hibernate.loader.Loader.list(Loader.java:) ~[hibernate-core-5.3..Final.jar:5.3..Final]
at org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:) ~[hibernate-core-5.3..Final.jar:5.3..Final]
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.list(QueryTranslatorImpl.java:) ~[hibernate-core-5.3..Final.jar:5.3..Final]
at org.hibernate.engine.query.spi.HQLQueryPlan.performList(HQLQueryPlan.java:) ~[hibernate-core-5.3..Final.jar:5.3..Final]
at org.hibernate.internal.SessionImpl.list(SessionImpl.java:) ~[hibernate-core-5.3..Final.jar:5.3..Final]
at org.hibernate.query.internal.AbstractProducedQuery.doList(AbstractProducedQuery.java:) ~[hibernate-core-5.3..Final.jar:5.3..Final]
at org.hibernate.query.internal.AbstractProducedQuery.list(AbstractProducedQuery.java:) ~[hibernate-core-5.3..Final.jar:5.3..Final]
... more
连接数的配置:
spring:
datasource:
hikari:
minimum-idle: 1
maximum-pool-size: 5
如果不配置的话,默认都是10.
我们使用entitymanager进行查询和其他操作时,调用这个方法org.springframework.orm.jpa.SharedEntityManagerCreator.DeferredQueryInvocationHandler#invoke,红色代码是会数据库连接池进行进行释放。SharedEntityManagerCreator这个类是这个关键,有兴趣可以重点看一下。
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// Invocation on Query interface coming in... if (method.getName().equals("equals")) {
// Only consider equal when proxies are identical.
return (proxy == args[0]);
}
else if (method.getName().equals("hashCode")) {
// Use hashCode of EntityManager proxy.
return hashCode();
}
else if (method.getName().equals("unwrap")) {
// Handle JPA 2.0 unwrap method - could be a proxy match.
Class<?> targetClass = (Class<?>) args[0];
if (targetClass == null) {
return this.target;
}
else if (targetClass.isInstance(proxy)) {
return proxy;
}
}
else if (method.getName().equals("getOutputParameterValue")) {
if (this.entityManager == null) {
Object key = args[0];
if (this.outputParameters == null || !this.outputParameters.containsKey(key)) {
throw new IllegalArgumentException("OUT/INOUT parameter not available: " + key);
}
Object value = this.outputParameters.get(key);
if (value instanceof IllegalArgumentException) {
throw (IllegalArgumentException) value;
}
return value;
}
} // Invoke method on actual Query object.
try {
Object retVal = method.invoke(this.target, args);
if (method.getName().equals("registerStoredProcedureParameter") && args.length == 3 &&
(args[2] == ParameterMode.OUT || args[2] == ParameterMode.INOUT)) {
if (this.outputParameters == null) {
this.outputParameters = new LinkedHashMap<>();
}
this.outputParameters.put(args[0], null);
}
return (retVal == this.target ? proxy : retVal);
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
finally {
if (queryTerminatingMethods.contains(method.getName())) {
// Actual execution of the query: close the EntityManager right
// afterwards, since that was the only reason we kept it open.
if (this.outputParameters != null && this.target instanceof StoredProcedureQuery) {
StoredProcedureQuery storedProc = (StoredProcedureQuery) this.target;
for (Map.Entry<Object, Object> entry : this.outputParameters.entrySet()) {
try {
Object key = entry.getKey();
if (key instanceof Integer) {
entry.setValue(storedProc.getOutputParameterValue((Integer) key));
}
else {
entry.setValue(storedProc.getOutputParameterValue(key.toString()));
}
}
catch (IllegalArgumentException ex) {
entry.setValue(ex);
}
}
}
EntityManagerFactoryUtils.closeEntityManager(this.entityManager);
this.entityManager = null;
}
}
}
如果我们使用@Transactional注解时,就不是上会的代码来释放连接池的,是下面的代码:
org.springframework.orm.jpa.JpaTransactionManager#doCleanupAfterCompletion
@Override
protected void doCleanupAfterCompletion(Object transaction) {
JpaTransactionObject txObject = (JpaTransactionObject) transaction; // Remove the entity manager holder from the thread, if still there.
// (Could have been removed by EntityManagerFactoryUtils in order
// to replace it with an unsynchronized EntityManager).
if (txObject.isNewEntityManagerHolder()) {
TransactionSynchronizationManager.unbindResourceIfPossible(obtainEntityManagerFactory());
}
txObject.getEntityManagerHolder().clear(); // Remove the JDBC connection holder from the thread, if exposed.
if (getDataSource() != null && txObject.hasConnectionHolder()) {
TransactionSynchronizationManager.unbindResource(getDataSource());
ConnectionHandle conHandle = txObject.getConnectionHolder().getConnectionHandle();
if (conHandle != null) {
try {
getJpaDialect().releaseJdbcConnection(conHandle,
txObject.getEntityManagerHolder().getEntityManager());
}
catch (Exception ex) {
// Just log it, to keep a transaction-related exception.
logger.error("Could not close JDBC connection after transaction", ex);
}
}
} getJpaDialect().cleanupTransaction(txObject.getTransactionData()); // Remove the entity manager holder from the thread.
if (txObject.isNewEntityManagerHolder()) {
EntityManager em = txObject.getEntityManagerHolder().getEntityManager();
if (logger.isDebugEnabled()) {
logger.debug("Closing JPA EntityManager [" + em + "] after transaction");
}
EntityManagerFactoryUtils.closeEntityManager(em);
}
else {
logger.debug("Not closing pre-bound JPA EntityManager after transaction");
}
}
这是两种逻辑,如果我们在@Transactional注解的方法会使用em的方法进行数据库的操作的话,会采用第二种方式来释放,
如果不加@Transactional注解的话,采用第一种方法来释放。
所以为了避免发现特殊情况,出现不释放连接池的情况,可以采用加@Transactional注解的方式 处理,如下面的代码:
@Transactional
@RequestMapping("/search")
public List search() { CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Tuple> query = cb.createTupleQuery();
Root<BillDO> root = query.from(BillDO.class); List<Predicate> predicates = new ArrayList<>();
predicates.add(cb.equal(root.get(BillDO.Fields.hisId),"3035-M6748091-1695675075-1"));
query.where(Iterables.toArray(predicates, Predicate.class)); query.multiselect(Arrays.asList(
root.get(BillDO.Fields.hisId),
root.get(BillDO.Fields.hospitalName),
root.get(BillDO.Fields.hospitalId),
root.get(BillDO.Fields.id),
root.get(BillDO.Fields.diseaseName)
));
TypedQuery<Tuple> typedQuery = entityManager.createQuery(query);
// Query unwrap = typedQuery.unwrap(Query.class);
QueryImpl unwrap = typedQuery.unwrap(QueryImpl.class); Query query1 = unwrap.setResultTransformer(
new CisAliasToBeanResultTransformer(BillDO.class, query.getSelection())); List resultList = query1.list();
return resultList;
}
如果不加@Transactional注解的话,那么连接池将不会释放,连接数会一直增加,直到超过最大连接数的配置。
可以通过下面的配置,来观察hikari数据库连接连接池的变化:
logging:
level:
root: warn
com:
zaxxer:
hikari: trace
这个代码是Hikari回收连接的:com.zaxxer.hikari.pool.HikariPool#recycle
@Override
void recycle(final PoolEntry poolEntry)
{
metricsTracker.recordConnectionUsage(poolEntry); connectionBag.requite(poolEntry);
}
spring boot JPA 数据库连接池释放的更多相关文章
- spring boot +druid数据库连接池配置
1.启动应用入口修改 增加servlet注解 import javax.sql.DataSource; import org.apache.ibatis.session.SqlSessionFacto ...
- spring boot jpa 使用update 报错解决办法
在spring boot jpa 中自定义sql,执行update操作报错解决办法: 在@Query(...)上添加 @Modifying@Transactional注解
- Spring Boot(五):Spring Boot Jpa 的使用
在上篇文章Spring Boot(二):Web 综合开发中简单介绍了一下 Spring Boot Jpa 的基础性使用,这篇文章将更加全面的介绍 Spring Boot Jpa 常见用法以及注意事项. ...
- Spring Boot + JPA(hibernate 5) 开发时,数据库表名大小写问题
(转载)Spring Boot + JPA(hibernate 5) 开发时,数据库表名大小写问题 这几天在用spring boot开发项目, 在开发的过程中遇到一个问题hibernate在执 ...
- Spring Boot Jpa 的使用
Spring Boot Jpa 介绍 首先了解 Jpa 是什么? Jpa (Java Persistence API) 是 Sun 官方提出的 Java 持久化规范.它为 Java 开发人员提供了一种 ...
- spring boot自定义线程池以及异步处理
spring boot自定义线程池以及异步处理@Async:什么是线程池?线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务.线程池线程都是后台线程.每个线程都使 ...
- (转)Spring Boot(五):Spring Boot Jpa 的使用
http://www.ityouknow.com/springboot/2016/08/20/spring-boot-jpa.html 在上篇文章Spring Boot(二):Web 综合开发中简单介 ...
- Spring boot JPA 用自定义主键策略 生成自定义主键ID
最近学习Spring boot JPA 学习过程解决的一些问题写成随笔,大家一起成长.这次遇到自定义主键的问题 package javax.persistence; public enum Gener ...
- Spring Boot(十五):spring boot+jpa+thymeleaf增删改查示例
Spring Boot(十五):spring boot+jpa+thymeleaf增删改查示例 一.快速上手 1,配置文件 (1)pom包配置 pom包里面添加jpa和thymeleaf的相关包引用 ...
随机推荐
- Java Date转Json报错解决方案
报错信息为: Caused by: java.lang.reflect.InvocationTargetException at sun.reflect.NativeMethodAccessorImp ...
- php phpexcel 创建excel
public function createExcel($result=[],$fileName=''){ \think\Loader::import('extend.excel.PHPExcel', ...
- 如何使用Navicat监控mysql数据库服务器
https://jingyan.baidu.com/article/c33e3f48de5208ea15cbb525.html 打开Navicat 点击[工具]菜单,选择[服务器监控]下的[MyS ...
- vue中getters不更新数据解决办法
在 Vue.js devtools 中看到,我们明明更改了仓库 state 中的数据,但是我们的 getters 就是值渲染一次之后就不再重新渲染了 解决方法:使用 Vue.set() 方法,就是专门 ...
- 微服务一键启动脚本shell没有环境变量的
#!/bin/bash#######################################################export JAVA_HOME=/root/data/app/jd ...
- DP&图论 DAY 5 下午
DP&图论 DAY 5 下午 树链剖分 每一条边要么属于重链要么轻边 证明: https://www.cnblogs.com/sagitta/p/5660749.html 轻边重链都是交 ...
- SV服务器
什么是服务端? 就是安装了服务端软件的电脑 下载 VisualSVN是一款图形化svn服务器. 官 网: http://www.visualsvn.com/server/ 下载地址: http://w ...
- python 生成随机红包
假设红包金额为money,数量是num,并且红包金额money>=num*0.01 原理如下,从1~money*100的数的集合中,随机抽取num-1个数,然后对这些数进行排序,在排序后的集合前 ...
- 接入上一篇SSM框架整合示例
需要创建相应的包与文件夹 Book数据表作为本示例数据 搭建项目开始 首先有bean后 private int id; private String name; private int cnt; 第一 ...
- ubuntu快速联网
1:打开ubuntu 2:设置 特殊:red hat设置视频:http://www.jikexueyuan.com/course/1349_3.html?ss=1