当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 数据库连接池释放的更多相关文章

  1. spring boot +druid数据库连接池配置

    1.启动应用入口修改 增加servlet注解 import javax.sql.DataSource; import org.apache.ibatis.session.SqlSessionFacto ...

  2. spring boot jpa 使用update 报错解决办法

    在spring boot jpa 中自定义sql,执行update操作报错解决办法: 在@Query(...)上添加 @Modifying@Transactional注解

  3. Spring Boot(五):Spring Boot Jpa 的使用

    在上篇文章Spring Boot(二):Web 综合开发中简单介绍了一下 Spring Boot Jpa 的基础性使用,这篇文章将更加全面的介绍 Spring Boot Jpa 常见用法以及注意事项. ...

  4. Spring Boot + JPA(hibernate 5) 开发时,数据库表名大小写问题

      (转载)Spring Boot + JPA(hibernate 5) 开发时,数据库表名大小写问题   这几天在用spring boot开发项目, 在开发的过程中遇到一个问题hibernate在执 ...

  5. Spring Boot Jpa 的使用

    Spring Boot Jpa 介绍 首先了解 Jpa 是什么? Jpa (Java Persistence API) 是 Sun 官方提出的 Java 持久化规范.它为 Java 开发人员提供了一种 ...

  6. spring boot自定义线程池以及异步处理

    spring boot自定义线程池以及异步处理@Async:什么是线程池?线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务.线程池线程都是后台线程.每个线程都使 ...

  7. (转)Spring Boot(五):Spring Boot Jpa 的使用

    http://www.ityouknow.com/springboot/2016/08/20/spring-boot-jpa.html 在上篇文章Spring Boot(二):Web 综合开发中简单介 ...

  8. Spring boot JPA 用自定义主键策略 生成自定义主键ID

    最近学习Spring boot JPA 学习过程解决的一些问题写成随笔,大家一起成长.这次遇到自定义主键的问题 package javax.persistence; public enum Gener ...

  9. Spring Boot(十五):spring boot+jpa+thymeleaf增删改查示例

    Spring Boot(十五):spring boot+jpa+thymeleaf增删改查示例 一.快速上手 1,配置文件 (1)pom包配置 pom包里面添加jpa和thymeleaf的相关包引用 ...

随机推荐

  1. SSH中一些典型的问题

    struts2 1-1:为什么每次请求都要创建一个Action对象? 是出于对线程安全的考虑,每个request都不会相互影响 1-2:ModelDriven拦截器的配置中refreshModelBe ...

  2. 06.旋转数组的最小数字 Java

    题目描述 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转. 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素. 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋 ...

  3. C++入门经典-例8.7-多态,利用虚函数实现动态绑定

    1:多态性是面向对象程序设计的一个重要特征,利用多态性可以设计和实现一个易于扩展的系统.在C++语言中,多态是指具有不同功能的函数可以用同一个函数名,这样就可以用一个函数名调用不同内容的函数,发出同样 ...

  4. Leetcode题目34.在排序数组中查找元素的第一个和最后一个位置(中等)

    题目描述: 给定一个按照升序排列的整数数组 nums,和一个目标值 target.找出给定目标值在数组中的开始位置和结束位置. 你的算法时间复杂度必须是 O(log n) 级别. 如果数组中不存在目标 ...

  5. Windows下设置U盘自动复制文件到本地

    一.打开记事本,把下面的代码复制进去 set fso=createobject("scripting.filesystemobject") set ws=createobject( ...

  6. GitHub:Alibaba

    ylbtech-GitHub:Alibaba 1.返回顶部   2.返回顶部   3.返回顶部   4.返回顶部   5.返回顶部 1. https://github.com/alibaba 2.   ...

  7. 惠州双月湾游记 & 攻略

    惠州双月湾游记&攻略 2019 年的 11 月底和小朱.Josie 约了快乐周末去惠州双月湾玩! 我和时猪一起从武汉出发到广州,然后和他们俩一起从广州自驾去的惠州.大致行程如下: Day 1: ...

  8. &&、()、||决定linux命令的执行顺序

    在执行某个命令时,有时需要依赖前面的命令是否执行成功.假如你想通过ssh命令复制很多数据文件到另外的机器后,删除所有源有文件,所以在删除源有文件之前首先要确定复制是不是执行成功.只要执行复制成功才可以 ...

  9. Linux编辑网络连接

    Linux编辑网络连接   实验目标: 通过本实验掌握新建网络连接.修改hosts文件.修改主机名的方法. 实验步骤: 1.新建一个名为review的网络连接,并配置ip地址,启用新连接 2.修改ho ...

  10. 致远OA利用POC

    批量检测url 在脚本同目录下建立url.txt 放入待检测的URL 运行脚本 # Wednesday, 26 June 2019 # Author:nianhua # Blog:https://gi ...