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的相关包引用 ...
随机推荐
- 有趣但是没有用的linux命令
1,小火车 #yum install sl 2,黑客帝国,代码雨 # wget https://jaist.dl.sourceforge.net/project/cmatrix/cmatrix/1.2 ...
- C++入门经典-例7.1-对象之访问类成员
1:建立一个类CPerson. (1)在person.h文件中代码: class CPerson { public: //数据成员 int m_iIndex; ]; short m_shAge; do ...
- bedtools 用法大全
原文:https://cloud.tencent.com/developer/article/1078324 前言: bedtools等工具号称是可以代替普通的生物信息学数据处理工程师的!我这里用一个 ...
- LeetCode 77. 组合(Combinations)
题目描述 给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合. 示例: 输入: n = 4, k = 2 输出: [ [2,4], [3,4], [2,3], [1,2], ...
- PHP 分页+查询
首先是主页面,与上篇分页页面相同 <table width="100%" border="1" cellpadding="0" cel ...
- WCF绑定(Binding)
一个Binding由一个有序的binding元素栈所组成,其中的每一个元素都指定了连接到ServiceEndpoint的一个方面.在这个栈中的最底两层都是必须要有的.最底下的一层是传输binding元 ...
- c++ STL之unordered_set
unordered_set的特点: 自身值就是主键,所以值唯一并且不可修改 基于hash表的无序排列 unordered_set基于哈希表,是无序的. 在一个 unordered_set 容器中,元素 ...
- kubernetes学习:CKA考试认证
考点 CKA认证针对考核成为当业界的Kubernetes管理员所需的技能. CKA认证考试包括这些一般领域及其在考试中的权重: 应用程序生命周期管理 - 8% 安装.配置和验证 - 12% 核心概 ...
- Linux 源码安装nginx
编译参数详解:https://www.cnblogs.com/houyongchong/p/compileArgs.html 配置参数详解:https://www.cnblogs.com/houyon ...
- 数据存储--SQLite
SQLite是一个开源嵌入式关系数据库,实现自包容,零配置,单个文件就是数据库,方便存储和转移,在window不用安装,直接解压到文件夹,并设置path. Ubuntu下安装:sudo apt ins ...