2016-02-01 55 views
0

我有一个数据库连接没有被javers'释放'的问题。Javers SQL连接永久使用

我们使用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收到一条通知:它不再需要连接?

回答

0

好的,这里是我的解决方案。收集由Javers获得的所有连接,并关闭它们(close()方法不会关闭,但他们释放到连接池!)Javers工作完成后:

类收集连接:

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(); 
+0

在一个数据库事务中保存域对象和javers审计数据不合适 –

0

你不应该关闭连接,实际上你不应该为JaVers打开新的连接。

主要思想是JaVers 重复使用连接和事务由应用程序打开和关闭。

示例方案:

  1. 应用需要从池中
  2. 应用做一些DML操作,例如连接插入新的Person#1
  3. JaVers借用了应用程序的连接,并插入的人#1
  4. 应用程序提交的审计记录(或回滚)事务,并返回连接到池

如果你正在使用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会话(界于当前线程)连接。

+0

我在'当前'线程中没有Hibernate会话,需要打开一个新线程,这非常接近从池中获取连接的方法。如果我们放弃使用Hibernate的事实会怎么样。我如何知道Javers何时完成其SQL语句,以便我可以将连接放回池(这是关闭连接时它会从连接池中进行的操作)? – gustav112233

+0

JaVers同步工作,因此您可以在javers.commit()完成后关闭连接。 –