我目前正在使用 Hikari 连接池。
当我在不同的线程中运行
hikariDataSource.getConnection()
时,我得到了相同的连接。
在我看来,通过
@Transactional
,JDBC连接首先设置在TransactionSynchronizationManager
中,也就是ThreadLocal。而 JDBC 连接仅在线程内有效。但是,正如您在 userRepositoryJDBC.findUser(..)
方法中看到的那样,即使在不同线程中获得连接后,活动连接数仍然相同。
总结一下我的想法,
nio-8080-exec-2
线程本地有JDBC连接,但是当db-thread-1
线程请求连接时,HikariCP也返回同样的连接给db-thread-1
。我认为这个问题是由于 lambda 捕获而发生的。当我使用 CompletableFuture 和 lambda 表达式运行一个新线程时,lambda 捕获局部变量(具有包含该连接的 TransactionSynchronizationManager)。
但即使这是因为lambda捕获,
db-thread-1
仍然无法打印TransactionSynchronizationManager.getCurrentTransactionName()
.
所以,请告诉我我错过了什么,错了什么。
我的代码在下面。
[nio-8080-exec-2] c.c.domain.user.service.UserServiceImpl : currentTXName:chatting.chat.domain.user.service.UserServiceImpl.login
[nio-8080-exec-2] c.c.domain.user.service.UserServiceImpl : HikariCP[Total:10, Active:1, Idle:9, Wait:0]
[nio-8080-exec-2] c.c.d.u.repository.UserRepositoryJDBC : currentTXName:chatting.chat.domain.user.service.UserServiceImpl.login
[nio-8080-exec-2] c.c.d.u.repository.UserRepositoryJDBC : HikariCP[Total:10, Active:1, Idle:9, Wait:0]
[ db-thread-1] c.c.d.u.repository.UserRepositoryJDBC : currentTXName:null
[ db-thread-1] c.c.d.u.repository.UserRepositoryJDBC : HikariCP[Total:10, Active:1, Idle:9, Wait:0] before get connection
[ db-thread-1] c.c.d.u.repository.UserRepositoryJDBC : HikariCP[Total:10, Active:1, Idle:9, Wait:0] after get connection
[ db-thread-1] c.c.d.u.repository.UserRepositoryJDBC : HikariCP[Total:10, Active:0, Idle:10, Wait:0]after release connection
@Service
public class UserServiceImpl {
...
@Transactional
public DeferredResult<ResponseEntity<?>> login(String userId, String userPw) throws CustomException{
DeferredResult<ResponseEntity<?>> dr = new DeferredResult<>();
printCurrentTXName(); // login
printHikariStatus(); // [HikariCP:Active=1]
userRepositoryJDBC.findUser(userId, userPw)
.thenComposeAsync(user->{
printHikariCPInfo(); // [HikariCP:Active=0]
...
},serviceExecutor).thenAcceptAsync((rs)->{
log.info("currentTXName:"+TransactionSynchronizationManager.getCurrentTransactionName());
printHikariCPInfo();
...
},serviceExecutor)
.exceptionally(e-> {
...
});
return dr;
}
}
public class UserRepositoryJDBC {
...
@Transactional
public CompletableFuture<User> findUser(String user_id, String user_pw) {
printCurrentTXName(); // login
printHikariStatus(); // [HikariCP:Active=1]
return CompletableFuture.supplyAsync(()->{
PreparedStatement pstmt = null;
Connection conn = null;
ResultSet rs = null;
printCurrentTXName(); // null
...
try {
printHikariStatus(); // before get connection [HikariCP:Active=1]
conn = hikariDataSource.getConnection();
log.info(printHikariCPInfo()+" after get connection"); // Still Active is 1, even if get connection in different thread
...
} catch (SQLException e) {
throw new RuntimeException(e);
} catch (CustomException customException){
throw customException;
} finally {
if(rs != null) try { rs.close();} catch(SQLException ex) {}
if(pstmt != null) try { pstmt.close();} catch(SQLException ex) {}
if(conn != null) try {
conn.close();
log.info(printHikariCPInfo()+" after release connection");
} catch(SQLException ex) {}
}
},databaseExecutor).exceptionally(e->{
...
});
}
}
为什么HikariCP在不同的线程中返回相同的连接?如果 lambda 表达式捕获局部变量,为什么
TransactionSynchronizationManager.getCurrentTransactionName()
为空?