我在Web应用程序中使用Tomcat JDBC连接池和OpenJPA。该应用程序没有看到更新的数据。具体来说,另一个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上调用commit,Connection上也没有相应的提交。由于在事务期间执行的所有查询都是只读的,因此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);
这没有效果。我已经阅读了关于stackoverflow的其他帖子,OpenJPA将autoCommit设置为false,我在日志中看到了这一点,但仅在提交包含数据库更新的事务时。
我最近一直在检查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;
答案 0 :(得分:1)
答案是OpenJPA中存在一个错误。解决方法是自己调用连接上的提交。
从EntityManager获取连接,将其强制转换为java.sql.Connection并调用commit()。
错误是OpenJPA从不在只读事务的连接上调用commit。如果autoCommit设置为true,这将没有问题,但OpenJPA坚持让autoCommit为false。创建EntityManager时,如果基础连接的autoCommit设置为true,则OpenJPA将其设置为false。
在下面的日志片段中,我在创建EntityManager之前从DataSource获得连接,并记录autoCommit和transactionIsolation。在创建EntityManager期间,日志显示autoCommit然后设置为false。
DEBUG: .store.util.CommitInterceptor.invoke: Method called on connection !****getAutoCommit Args:
DEBUG: .store.util.CommitInterceptor.invoke: Method called on connection getTransactionIsolation Args:
DEBUG: .store.impl.EMHandlerImpl.em: Isolation level: 4 autoCommit: true
DEBUG: .store.util.CommitInterceptor.invoke: Method called on connection close Args:
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: false
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时,会发生相反的情况。在下面的日志片段中,autoCommit未设置为false。
DEBUG: .store.util.CommitInterceptor.invoke: Method called on connection !****getAutoCommit Args:
DEBUG: .store.util.CommitInterceptor.invoke: Method called on connection getTransactionIsolation Args:
DEBUG: .store.impl.EMHandlerImpl.em: Isolation level: 2 autoCommit: true
DEBUG: .store.util.CommitInterceptor.invoke: Method called on connection close Args:
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 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
此外,如果autoCommit设置开始为false,则OpenJPA将其设置为DB2,并将其单独留给MySQL。我不打算为这些案例显示日志。
为了完整起见,我将提及我在更新期间观察到的内容。对于DB2,autoCommit设置为false,执行更新,在连接上调用commit,并将autoCommit设置回true。这是DB2的日志片段:
DEBUG: .store.impl.EMHandlerImpl.commitTransaction: >>>calling COMMIT transaction 544096693
DEBUG: .store.util.CommitInterceptor.invoke: Method called on connection !****getAutoCommit Args:
DEBUG: .store.util.CommitInterceptor.invoke: Method called on connection getTransactionIsolation Args:
DEBUG: .store.util.CommitInterceptor.invoke: Method called on connection !****getAutoCommit Args:
DEBUG: .store.util.CommitInterceptor.invoke: Method called on connection !****setAutoCommit Args: false
DEBUG: .store.util.CommitInterceptor.invoke: Method called on connection isReadOnly Args:
DEBUG: .store.util.CommitInterceptor.invoke: Method called on connection prepareStatement Args: INSERT INT 1003 1007
DEBUG: .store.util.CommitInterceptor.invoke: Method called on connection prepareStatement Args: INSERT INT 1003 1007
DEBUG: .store.util.CommitInterceptor.invoke: Method called on connection prepareStatement Args: INSERT INT 1003 1007
DEBUG: .store.util.CommitInterceptor.invoke: Method called on connection prepareStatement Args: UPDATE DAT 1003 1007
DEBUG: .store.util.CommitInterceptor.invoke: Method called on connection prepareStatement Args: UPDATE DAT 1003 1007
DEBUG: .store.util.CommitInterceptor.invoke: Method called on connection !****commit Args:
DEBUG: .store.util.CommitInterceptor.invoke: Method called on connection !****setAutoCommit Args: true
DEBUG: .store.util.CommitInterceptor.invoke: Method called on connection close Args:
DEBUG: .store.impl.EMHandlerImpl.commitTransaction: >>>finished COMMIT transaction 544096693
对于MySQL,因为autoCommit已经为false,所以在更新期间不会更改。无论如何,这是日志:
DEBUG: .store.impl.EMHandlerImpl.commitTransaction: >>>calling COMMIT transaction 2103121779
DEBUG: .store.util.CommitInterceptor.invoke: Method called on connection !****getAutoCommit Args:
DEBUG: .store.util.CommitInterceptor.invoke: Method called on connection !****getAutoCommit Args:
DEBUG: .store.util.CommitInterceptor.invoke: Method called on connection isReadOnly Args:
DEBUG: .store.util.CommitInterceptor.invoke: Method called on connection prepareStatement Args: INSERT INT 1003 1007
DEBUG: .store.util.CommitInterceptor.invoke: Method called on connection prepareStatement Args: INSERT INT 1003 1007
DEBUG: .store.util.CommitInterceptor.invoke: Method called on connection prepareStatement Args: INSERT INT 1003 1007
DEBUG: .store.util.CommitInterceptor.invoke: Method called on connection prepareStatement Args: UPDATE DAT 1003 1007
DEBUG: .store.util.CommitInterceptor.invoke: Method called on connection prepareStatement Args: UPDATE DAT 1003 1007
DEBUG: .store.util.CommitInterceptor.invoke: Method called on connection !****commit Args:
DEBUG: .store.util.CommitInterceptor.invoke: Method called on connection close Args:
DEBUG: .store.impl.EMHandlerImpl.commitTransaction: >>>finished COMMIT transaction 2103121779
有趣的是,此错误仅在使用连接池时显示。我建议这是因为在重用的连接上永远不会调用commit。如果没有连接池,OpenJPA每次都会获得一个全新的连接,因此所有同时发生的更新都会在下一个查询中找到。
答案 1 :(得分:0)
朱莉, 我正在使用tomcat 7(带有本机池)和openjpa 2.2。我遇到了一个问题,JPA有时会(!)在页面刷新后带来过时的数据。
当我添加
时defaultAutoCommit="true"
在连接池配置(server.xml)中,完成了这一操作。
defaultAutoCommit - (布尔值)此池创建的连接的默认自动提交状态。如果未设置,则默认为JDBC驱动程序默认值(如果未设置,则不会调用setAutoCommit方法。)