我在 Web 应用程序中使用带有 OpenJPA 的 Tomcat JDBC 连接池。应用程序看不到更新的数据。具体来说,另一个 java 应用程序在数据库中添加或删除记录,但 Web 应用程序永远不会看到这些更新。这是一个相当严重的问题。我一定缺少一些基本的东西。
如果我从实现中删除连接池,Web 应用程序会看到更新。就好像 Web 应用程序的提交永远不会在 Connection 上调用。
版本信息:
Tomcat JDBC 连接池:org.apache.tomcat tomcat-jdbc 7.0.21
OpenJPA:org.apache.openjpa openjpa 2.0.1
下面是创建 DataSource 的代码片段(DataSourceHelper.findOrCreateDataSource 方法):
PoolConfiguration props = new PoolProperties();
props.setUrl(URL);
props.setDefaultAutoCommit(false);
props.setDriverClassName(dd.getClass().getName());
props.setUsername(username);
props.setPassword(pw);
props.setJdbcInterceptors("org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;"+
"org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer;"+
"org.apache.tomcat.jdbc.pool.interceptor.SlowQueryReportJmx;"+
"org.apache.tomcat.jdbc.pool.interceptor.ResetAbandonedTimer");
props.setLogAbandoned(true);
props.setSuspectTimeout(120);
props.setJmxEnabled(true);
props.setInitialSize(2);
props.setMaxActive(100);
props.setTestOnBorrow(true);
if (URL.toUpperCase().contains(DB2)) {
props.setValidationQuery("VALUES (1)");
} else if (URL.toUpperCase().contains(MYSQL)) {
props.setValidationQuery("SELECT 1");
props.setConnectionProperties("relaxAutoCommit=true");
} else if (URL.toUpperCase().contains(ORACLE)) {
props.setValidationQuery("select 1 from dual");
}
props.setValidationInterval(3000);
dataSource = new DataSource();
dataSource.setPoolProperties(props);
下面是使用 DataSource 创建 EntityManagerFactory 的代码:
//props contains the connection url, user name, and password
DataSource dataSource = DataSourceHelper.findOrCreateDataSource("DATAMGT", URL, username, password);
props.put("openjpa.ConnectionFactory", dataSource);
emFactory = (OpenJPAEntityManagerFactory) Persistence.createEntityManagerFactory("DATAMGT", props);
如果我像这样注释掉 DataSource,那么它就可以工作。请注意,OpenJPA 在 props 中有足够的信息来配置连接,而无需使用 DataSource。
//props contains the connection url, user name, and password
//DataSource dataSource = DataSourceHelper.findOrCreateDataSource("DATAMGT", URL, username, password);
//props.put("openjpa.ConnectionFactory", dataSource);
emFactory = (OpenJPAEntityManagerFactory) Persistence.createEntityManagerFactory("DATAMGT", props);
所以不知何故,OpenJPA 和连接池的组合无法正常工作。
更新:
实际上,当底层数据库是 MySQL 时,它似乎会失败。如果底层数据库是 DB2,它在有和没有池的情况下都能正常工作。
更新#2:
我在池中添加了一个 JdbcInterceptor 以记录在连接上调用的方法。如果数据库是 DB2,则在创建 EntityManager 时调用 setAutoCommit(true)。当数据库是 MySQL 时,它不会被调用。
这将解释行为的差异。即使应用程序在 EntityManager 上调用提交,连接上也没有相应的提交。由于事务期间执行的所有查询都是只读的,因此 OpenJPA 似乎认为不需要提交。
这是来自 MySQL 的日志:
INFO : .store.EMHandler.getConfig: ******************Start JPA Properties:
INFO : .store.EMHandler.getConfig: *********openjpa.ConnectionDriverName: com.mysql.jdbc.Driver
INFO : .store.EMHandler.getConfig: *********openjpa.ConnectionPassword: *******
INFO : .store.EMHandler.getConfig: *********openjpa.ConnectionUserName: *******
INFO : .store.EMHandler.getConfig: *********openjpa.ConnectionURL: jdbc:mysql://localhost:3306/datamgt
INFO : .store.EMHandler.getConfig: *********openjpa.Log: log4j
INFO : .store.EMHandler.getConfig: ***** Found Driver :com.mysql.jdbc.Driver class: class com.mysql.jdbc.Driver
INFO : .store.EMHandler.getConfig: ******************End JPA Properties:
DEBUG: .store.impl.EMHandlerImpl.em: ********EntityManagerFactory created
DEBUG: .store.impl.EMHandlerImpl.em: ******** Creating EntityManager
DEBUG: .store.util.CommitInterceptor.invoke: Method called on connection !****getAutoCommit Args:
DEBUG: .store.util.CommitInterceptor.invoke: Method called on connection getMetaData Args:
DEBUG: .store.util.CommitInterceptor.invoke: Method called on connection getMetaData Args:
DEBUG: .store.util.CommitInterceptor.invoke: Method called on connection close Args:
DEBUG: .store.impl.EMHandlerImpl.em: ********Entity manager created
这是来自 DB2 的日志(注意 setAutoCommit):
INFO : .store.EMHandler.getConfig: ******************Start JPA Properties:
INFO : .store.EMHandler.getConfig: *********openjpa.ConnectionDriverName: com.ibm.db2.jcc.DB2Driver
INFO : .store.EMHandler.getConfig: *********openjpa.ConnectionPassword: *******
INFO : .store.EMHandler.getConfig: *********openjpa.ConnectionUserName: *******
INFO : .store.EMHandler.getConfig: *********openjpa.ConnectionURL: jdbc:db2://localhost:50000/DATAMGT
INFO : .store.EMHandler.getConfig: *********openjpa.Log: log4j
INFO : .store.EMHandler.getConfig: ***** Found Driver :com.ibm.db2.jcc.DB2Driver class: class com.ibm.db2.jcc.DB2Driver
INFO : .store.EMHandler.getConfig: ******************End JPA Properties:
DEBUG: .store.impl.EMHandlerImpl.em: ********EntityManagerFactory created
DEBUG: .store.impl.EMHandlerImpl.em: ******** Creating EntityManager
DEBUG: .store.util.CommitInterceptor.invoke: Method called on connection !****getAutoCommit Args:
DEBUG: .store.util.CommitInterceptor.invoke: Method called on connection !****setAutoCommit Args: true
DEBUG: .store.util.CommitInterceptor.invoke: Method called on connection getMetaData Args:
DEBUG: .store.util.CommitInterceptor.invoke: Method called on connection getMetaData Args:
DEBUG: .store.util.CommitInterceptor.invoke: Method called on connection createStatement Args:
DEBUG: .store.util.CommitInterceptor.invoke: Method called on connection getTransactionIsolation Args:
DEBUG: .store.util.CommitInterceptor.invoke: Method called on connection close Args:
DEBUG: .store.impl.EMHandlerImpl.em: ********Entity manager created
自从发现这一点以来,我在创建 EntityManagerFactory 之前尝试将 autoCommit 设置为 true:
dataSource.setDefaultAutoCommit(true);
这没有效果。我已经阅读了关于 OpenJPA 将 autoCommit 设置为 false 的 stackoverflow 上的其他帖子,并且我在日志中看到了这一点,但仅在提交包含数据库更新的事务时。
我最近一直在检查 transactionIsolationLevel,结果发现 MySQL 的默认级别是 4,而 DB2 是 2。这是 java.sql.Connection 类中这些的定义。请注意,2 比 4 更轻松,所以这可能不是原因。
/**
* A constant indicating that
* dirty reads are prevented; non-repeatable reads and phantom
* reads can occur. This level only prohibits a transaction
* from reading a row with uncommitted changes in it.
*/
int TRANSACTION_READ_COMMITTED = 2;
/**
* A constant indicating that
* dirty reads and non-repeatable reads are prevented; phantom
* reads can occur. This level prohibits a transaction from
* reading a row with uncommitted changes in it, and it also
* prohibits the situation where one transaction reads a row,
* a second transaction alters the row, and the first transaction
* rereads the row, getting different values the second time
* (a "non-repeatable read").
*/
int TRANSACTION_REPEATABLE_READ = 4;