当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. 有趣但是没有用的linux命令

    1,小火车 #yum install sl 2,黑客帝国,代码雨 # wget https://jaist.dl.sourceforge.net/project/cmatrix/cmatrix/1.2 ...

  2. C++入门经典-例7.1-对象之访问类成员

    1:建立一个类CPerson. (1)在person.h文件中代码: class CPerson { public: //数据成员 int m_iIndex; ]; short m_shAge; do ...

  3. bedtools 用法大全

    原文:https://cloud.tencent.com/developer/article/1078324 前言: bedtools等工具号称是可以代替普通的生物信息学数据处理工程师的!我这里用一个 ...

  4. LeetCode 77. 组合(Combinations)

    题目描述 给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合. 示例: 输入: n = 4, k = 2 输出: [ [2,4], [3,4], [2,3], [1,2], ...

  5. PHP 分页+查询

    首先是主页面,与上篇分页页面相同 <table width="100%" border="1" cellpadding="0" cel ...

  6. WCF绑定(Binding)

    一个Binding由一个有序的binding元素栈所组成,其中的每一个元素都指定了连接到ServiceEndpoint的一个方面.在这个栈中的最底两层都是必须要有的.最底下的一层是传输binding元 ...

  7. c++ STL之unordered_set

    unordered_set的特点: 自身值就是主键,所以值唯一并且不可修改 基于hash表的无序排列 unordered_set基于哈希表,是无序的. 在一个 unordered_set 容器中,元素 ...

  8. kubernetes学习:CKA考试认证

    考点 CKA认证针对考核成为当业界的Kubernetes管理员所需的技能. CKA认证考试包括这些一般领域及其在考试中的权重: 应用程序生命周期管理 -  8% 安装.配置和验证 -  12% 核心概 ...

  9. Linux 源码安装nginx

    编译参数详解:https://www.cnblogs.com/houyongchong/p/compileArgs.html 配置参数详解:https://www.cnblogs.com/houyon ...

  10. 数据存储--SQLite

    SQLite是一个开源嵌入式关系数据库,实现自包容,零配置,单个文件就是数据库,方便存储和转移,在window不用安装,直接解压到文件夹,并设置path. Ubuntu下安装:sudo apt ins ...