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的相关包引用 ...
随机推荐
- LeetCode 36. 有效的数独(Valid Sudoku)
题目描述 判断一个 9x9 的数独是否有效.只需要根据以下规则,验证已经填入的数字是否有效即可. 数字 1-9 在每一行只能出现一次. 数字 1-9 在每一列只能出现一次. 数字 1-9 在每一个以粗 ...
- ThinkPHP5+Bootstrap的极速后台开发框架。
fastamin:https://doc.fastadmin.net/docs/index.html 后台读取mysql 注释,所以要注意mysql中注释的填写规则
- D建立app项目(mui)
参考 http://dev.dcloud.net.cn/mui/getting-started/ 1.ios需要下载iTunes,确保手机能连上电脑 2.mui可参考手册 http://dev.dcl ...
- 石川es6课程---12、Promise
石川es6课程---12.Promise 一.总结 一句话总结: 用同步的方式来书写异步代码,让异步书写变的特别简单 用同步的方式来书写异步代码Promise 让异步操作写起来,像在写同步操作的流程, ...
- Windows 设置定时任务
cmd 运行 control 命令打开控制面板,找到 管理工具 -> 任务计划程序 一.添加定时任务 创建任务 基本信息 触发器,这里设置开机启动 操作,这里执行一个程序.若为脚本,注意起始于路 ...
- is幻梦 Linux命令之文件和目录操作命令(二)——查看文件内容cat、more、less、tail、head
一.cat命令 用法:cat [选项][文件] 1.主要用来查看文件内容 2.-n 在文件内容的每一行上加上行号 3.再不加任何选项和文件名的时候,将标准输入的内容复制到标准输出 这个时候一般用于重定 ...
- SQLServer-设置-Table:阻止保存要求重新创建表的更改
ylbtech-SQLServer-设置-Table:阻止保存要求重新创建表的更改 1.返回顶部 ·不允许保存更改,阻止保存要求重新创建表的更改 · 2.返回顶部 · https://jingyan. ...
- MVC和MVP区别
从这幅图可以看到,我们可以看到在MVC里,View是可以直接访问Model的!从而,View里会包含Model信息,不可避免的还要包括一些业务逻辑. 在MVC模型里,更关注的Model的不变,而同时有 ...
- 利用ExpandableListView实现常用号码查询功能的实现
package com.loaderman.expandablelistviewdemo; import android.content.Context; import android.databas ...
- OpenStack Blazar 架构解析与功能实践
目录 文章目录 目录 Blazar Blazar 的安装部署 Blazar 的软件架构 Blazar 的资源模型与状态机 Blazar 的主机资源预留功能(Host Reservation) 代码实现 ...