Javers SQL 个连接永远在使用中
Javers SQL connections in use forever
我有一个问题,数据库连接不是 javers 'released'。
我们使用 Hibernate 5.0.6,Hikari 作为我们的连接池,MSSQL Server 作为我们的数据库。 Javers配置如下(片段):
JaversBuilder.javers().
registerJaversRepository(SqlRepositoryBuilder.sqlRepository().
withConnectionProvider(() -> ((SessionFactoryImpl) sessionFactory).getServiceRegistry().
getService(org.hibernate.engine.jdbc.connections.spi.ConnectionProvider.class).getConnection()).
withDialect(DialectName.MSSQL).
build()).
build();
通过这种方式获取连接效果很好。如果没有更多可用,连接池将打开数据库连接。然而,javers获得的连接是永远'inUse'。
61366 [Hikari Housekeeping Timer (pool HikariPool-0)] DEBUG HikariPool - Before cleanup pool stats HikariPool-0 (total=100, inUse=100, avail=0, waiting=1)
61366 [Hikari Housekeeping Timer (pool HikariPool-0)] DEBUG HikariPool - After cleanup pool stats HikariPool-0 (total=100, inUse=100, avail=0, waiting=1)
61366 [HikariCP connection filler (pool HikariPool-0)] DEBUG HikariPool - After fill pool stats HikariPool-0 (total=100, inUse=100, avail=0, waiting=1)
我必须手动关闭连接吗?如果我尝试这样做(一秒钟后关闭我给 Javers 的每个连接),连接池将被清除。但是,这种方法不是一种选择。
有什么我想念的吗?如果我必须手动关闭连接,是否至少有可能收到来自 Javers 的不再需要连接的通知?
您不应该关闭连接,事实上您也不应该为 JaVers 打开新连接。
主要思想是 JaVers 重用 应用程序打开和关闭的连接和事务。
示例场景:
- 应用程序从池中获取连接
- 应用程序执行一些 DML 操作,例如插入新的 Person#1
- JaVers 从应用程序借用连接并为 Person#1 插入审计记录
- 应用程序提交(或回滚)事务和returns连接到池
如果您使用的是 JPA API,您可以使用与 JaVers 捆绑的 JpaHibernateConnectionProvider
:
package org.javers.spring.jpa;
... //
public class JpaHibernateConnectionProvider implements ConnectionProvider{
@PersistenceContext
private EntityManager entityManager;
@Override
public Connection getConnection() {
SessionImpl session = (SessionImpl)entityManager.unwrap(Session.class);
return session.connection();
}
}
如果您使用的是纯 Hibernate,您可以编写类似的代码,从当前 Hibernate 会话(绑定到当前线程)中提取连接。
好的,这是我的解决方案。收集Javers获取的所有连接并关闭(close()不关闭而是释放到连接池!)Javers工作完成后:
A class 收集连接:
public class ConnectionCleanup {
private static final Logger LOG = LoggerFactory.getLogger(ConnectionCleanup.class);
private static final ThreadLocal<List<Connection>> connections = new ThreadLocal<>();
public static void add(final Connection connection) {
List<Connection> listConnections = connections.get();
if (listConnections == null) {
listConnections = new ArrayList<>();
connections.set(listConnections);
}
listConnections.add(connection);
}
public static void cleanup() {
final List<Connection> listConnections = connections.get();
if (listConnections == null) {
return;
}
for (final Connection con : listConnections) {
try {
if (!con.isClosed()) {
con.close();
}
} catch (final Exception ex) {
LOG.error("Caught an", ex);
}
}
connections.set(null);
}
}
包装 SqlRepository 以在 Javers 工作完成后调用清理:
public class CleanupJaversSqlRepository implements JaversRepository {
private final JaversSqlRepository repository;
public CleanupJaversSqlRepository(final JaversSqlRepository repository) {
this.repository = repository;
}
@Override
public Optional<CdoSnapshot> getLatest(final GlobalId globalId) {
final Optional<CdoSnapshot> latest = repository.getLatest(globalId);
ConnectionCleanup.cleanup();
return latest;
}
@Override
public void persist(final Commit commit) {
repository.persist(commit);
ConnectionCleanup.cleanup();
}
@Override
public CommitId getHeadId() {
final CommitId commitId = repository.getHeadId();
ConnectionCleanup.cleanup();
return commitId;
}
@Override
public void setJsonConverter(final JsonConverter jsonConverter) {
repository.setJsonConverter(jsonConverter);
ConnectionCleanup.cleanup();
}
@Override
public void ensureSchema() {
repository.ensureSchema();
ConnectionCleanup.cleanup();
}
@Override
public List<CdoSnapshot> getStateHistory(
final GlobalId globalId,
final QueryParams queryParams) {
final List<CdoSnapshot> stateHistory = repository.getStateHistory(globalId, queryParams);
ConnectionCleanup.cleanup();
return stateHistory;
}
@Override
public List<CdoSnapshot> getPropertyStateHistory(
final GlobalId globalId,
final String propertyName,
final QueryParams queryParams) {
final List<CdoSnapshot> stateHistory = repository.getPropertyStateHistory(
globalId, propertyName, queryParams);
ConnectionCleanup.cleanup();
return stateHistory;
}
@Override
public List<CdoSnapshot> getStateHistory(
final ManagedType givenClass,
final QueryParams queryParams) {
final List<CdoSnapshot> stateHistory = repository.getStateHistory(givenClass, queryParams);
ConnectionCleanup.cleanup();
return stateHistory;
}
@Override
public List<CdoSnapshot> getPropertyStateHistory(
final ManagedType givenClass,
final String propertyName,
final QueryParams queryParams) {
final List<CdoSnapshot> stateHistory = repository.getPropertyStateHistory(
givenClass, propertyName, queryParams);
ConnectionCleanup.cleanup();
return stateHistory;
}
@Override
public List<CdoSnapshot> getValueObjectStateHistory(
final EntityType ownerEntity,
final String path,
final QueryParams queryParams) {
final List<CdoSnapshot> stateHistory = repository.getValueObjectStateHistory(
ownerEntity, path, queryParams);
ConnectionCleanup.cleanup();
return stateHistory;
}
}
最后,把它们放在一起:
JaversBuilder.javers().
registerJaversRepository(new CleanupJaversSqlRepository(SqlRepositoryBuilder.sqlRepository().
withConnectionProvider(() -> {
final Connection connection = ((SessionFactoryImpl) sessionFactory).
getServiceRegistry().getService(
org.hibernate.engine.jdbc.connections.spi.ConnectionProvider.class).
getConnection();
ConnectionCleanup.add(connection);
return connection;
}).
withDialect(DialectName.MSSQL).
build())).
build();
我有一个问题,数据库连接不是 javers 'released'。
我们使用 Hibernate 5.0.6,Hikari 作为我们的连接池,MSSQL Server 作为我们的数据库。 Javers配置如下(片段):
JaversBuilder.javers().
registerJaversRepository(SqlRepositoryBuilder.sqlRepository().
withConnectionProvider(() -> ((SessionFactoryImpl) sessionFactory).getServiceRegistry().
getService(org.hibernate.engine.jdbc.connections.spi.ConnectionProvider.class).getConnection()).
withDialect(DialectName.MSSQL).
build()).
build();
通过这种方式获取连接效果很好。如果没有更多可用,连接池将打开数据库连接。然而,javers获得的连接是永远'inUse'。
61366 [Hikari Housekeeping Timer (pool HikariPool-0)] DEBUG HikariPool - Before cleanup pool stats HikariPool-0 (total=100, inUse=100, avail=0, waiting=1)
61366 [Hikari Housekeeping Timer (pool HikariPool-0)] DEBUG HikariPool - After cleanup pool stats HikariPool-0 (total=100, inUse=100, avail=0, waiting=1)
61366 [HikariCP connection filler (pool HikariPool-0)] DEBUG HikariPool - After fill pool stats HikariPool-0 (total=100, inUse=100, avail=0, waiting=1)
我必须手动关闭连接吗?如果我尝试这样做(一秒钟后关闭我给 Javers 的每个连接),连接池将被清除。但是,这种方法不是一种选择。
有什么我想念的吗?如果我必须手动关闭连接,是否至少有可能收到来自 Javers 的不再需要连接的通知?
您不应该关闭连接,事实上您也不应该为 JaVers 打开新连接。
主要思想是 JaVers 重用 应用程序打开和关闭的连接和事务。
示例场景:
- 应用程序从池中获取连接
- 应用程序执行一些 DML 操作,例如插入新的 Person#1
- JaVers 从应用程序借用连接并为 Person#1 插入审计记录
- 应用程序提交(或回滚)事务和returns连接到池
如果您使用的是 JPA API,您可以使用与 JaVers 捆绑的 JpaHibernateConnectionProvider
:
package org.javers.spring.jpa;
... //
public class JpaHibernateConnectionProvider implements ConnectionProvider{
@PersistenceContext
private EntityManager entityManager;
@Override
public Connection getConnection() {
SessionImpl session = (SessionImpl)entityManager.unwrap(Session.class);
return session.connection();
}
}
如果您使用的是纯 Hibernate,您可以编写类似的代码,从当前 Hibernate 会话(绑定到当前线程)中提取连接。
好的,这是我的解决方案。收集Javers获取的所有连接并关闭(close()不关闭而是释放到连接池!)Javers工作完成后:
A class 收集连接:
public class ConnectionCleanup {
private static final Logger LOG = LoggerFactory.getLogger(ConnectionCleanup.class);
private static final ThreadLocal<List<Connection>> connections = new ThreadLocal<>();
public static void add(final Connection connection) {
List<Connection> listConnections = connections.get();
if (listConnections == null) {
listConnections = new ArrayList<>();
connections.set(listConnections);
}
listConnections.add(connection);
}
public static void cleanup() {
final List<Connection> listConnections = connections.get();
if (listConnections == null) {
return;
}
for (final Connection con : listConnections) {
try {
if (!con.isClosed()) {
con.close();
}
} catch (final Exception ex) {
LOG.error("Caught an", ex);
}
}
connections.set(null);
}
}
包装 SqlRepository 以在 Javers 工作完成后调用清理:
public class CleanupJaversSqlRepository implements JaversRepository {
private final JaversSqlRepository repository;
public CleanupJaversSqlRepository(final JaversSqlRepository repository) {
this.repository = repository;
}
@Override
public Optional<CdoSnapshot> getLatest(final GlobalId globalId) {
final Optional<CdoSnapshot> latest = repository.getLatest(globalId);
ConnectionCleanup.cleanup();
return latest;
}
@Override
public void persist(final Commit commit) {
repository.persist(commit);
ConnectionCleanup.cleanup();
}
@Override
public CommitId getHeadId() {
final CommitId commitId = repository.getHeadId();
ConnectionCleanup.cleanup();
return commitId;
}
@Override
public void setJsonConverter(final JsonConverter jsonConverter) {
repository.setJsonConverter(jsonConverter);
ConnectionCleanup.cleanup();
}
@Override
public void ensureSchema() {
repository.ensureSchema();
ConnectionCleanup.cleanup();
}
@Override
public List<CdoSnapshot> getStateHistory(
final GlobalId globalId,
final QueryParams queryParams) {
final List<CdoSnapshot> stateHistory = repository.getStateHistory(globalId, queryParams);
ConnectionCleanup.cleanup();
return stateHistory;
}
@Override
public List<CdoSnapshot> getPropertyStateHistory(
final GlobalId globalId,
final String propertyName,
final QueryParams queryParams) {
final List<CdoSnapshot> stateHistory = repository.getPropertyStateHistory(
globalId, propertyName, queryParams);
ConnectionCleanup.cleanup();
return stateHistory;
}
@Override
public List<CdoSnapshot> getStateHistory(
final ManagedType givenClass,
final QueryParams queryParams) {
final List<CdoSnapshot> stateHistory = repository.getStateHistory(givenClass, queryParams);
ConnectionCleanup.cleanup();
return stateHistory;
}
@Override
public List<CdoSnapshot> getPropertyStateHistory(
final ManagedType givenClass,
final String propertyName,
final QueryParams queryParams) {
final List<CdoSnapshot> stateHistory = repository.getPropertyStateHistory(
givenClass, propertyName, queryParams);
ConnectionCleanup.cleanup();
return stateHistory;
}
@Override
public List<CdoSnapshot> getValueObjectStateHistory(
final EntityType ownerEntity,
final String path,
final QueryParams queryParams) {
final List<CdoSnapshot> stateHistory = repository.getValueObjectStateHistory(
ownerEntity, path, queryParams);
ConnectionCleanup.cleanup();
return stateHistory;
}
}
最后,把它们放在一起:
JaversBuilder.javers().
registerJaversRepository(new CleanupJaversSqlRepository(SqlRepositoryBuilder.sqlRepository().
withConnectionProvider(() -> {
final Connection connection = ((SessionFactoryImpl) sessionFactory).
getServiceRegistry().getService(
org.hibernate.engine.jdbc.connections.spi.ConnectionProvider.class).
getConnection();
ConnectionCleanup.add(connection);
return connection;
}).
withDialect(DialectName.MSSQL).
build())).
build();