使用Atomikos时遇到了一个奇怪的问题。
我有一个小测试应用程序(Spring + Hibernate)。它使用两个不同的数据源,我需要在非Java EE容器上测试JTA功能(在我的例子中是Tomcat)。
当我使用MySQL作为数据库时,一切都得到保存而没有问题。但是当我切换到PostgreSQL时,数据没有保存到数据库中。
有趣的是,如果我没有使用@Transactional
并手动开始和提交事务 - 一切正常。但是当使用@Transactional
时 - 数据没有被保存。我可以看到 hibernate_sequence 表在数据库中得到更新(数字增加),而只有数据本身没有。尽管事实上我在Atomikos日志中看到数据已经提交等等,所以这一切再次使用SAME代码可以正常使用MySQL。只有在使用PostgreSQL时才会出现此问题。
我在PostgreSQL版本9.4(Linux上为64位)和9.5(Windows 10上为64位)上使用不同的PostgreSQL JDBC driver versions(JDBC4和JDBC3)进行了测试。
Atomikos测试版本: 4.0.4 (截至目前为止)和 3.9.3 。
春季版: 4.0.9 。
Hibernate版本: 3.6.10.Final 。
请注意
我需要那些精确的Spring和Hibernate版本来模拟我需要Atomikos集成的工作应用程序。
这是我的 spring.xml 配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
<tx:annotation-driven />
<tx:jta-transaction-manager />
<context:component-scan base-package="com.byteslounge.spring.tx.service.impl" />
<context:component-scan base-package="com.byteslounge.spring.tx.servlet" />
<context:component-scan base-package="com.byteslounge.spring.tx.entity" />
<bean id="sessionFactory1" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="annotatedClasses">
<list>
<value>com.byteslounge.spring.tx.entity.TableOne</value>
</list>
</property>
<property name="dataSource" ref="dataSource1" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.connection.autocommit">false</prop>
<prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
<prop key="hibernate.current_session_context_class">jta</prop>
<prop key="javax.persistence.transactionType">jta</prop>
<prop key="hibernate.transaction.factory_class">com.atomikos.icatch.jta.hibernate3.AtomikosJTATransactionFactory </prop>
<prop key="hibernate.transaction.manager_lookup_class">com.atomikos.icatch.jta.hibernate3.TransactionManagerLookup</prop>
</props>
</property>
</bean>
<bean id="dataSource1" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close">
<property name="uniqueResourceName" value="DataSource1" />
<property name="xaDataSource" ref="dataBase1" />
<property name="maxPoolSize" value="20" />
<property name="minPoolSize" value="10"/>
</bean>
<bean id="dataBase1" class="org.postgresql.xa.PGXADataSource" lazy-init="true">
<property name="user" value="tester1" />
<property name="password" value="123456" />
<property name="serverName" value="localhost" />
<property name="portNumber" value="5432" />
<property name="databaseName" value="test_db1" />
<property name="url" value="jdbc:postgresql://localhost:5432/test_db1" />
</bean>
<bean id="sessionFactory2" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="annotatedClasses">
<list>
<value>com.byteslounge.spring.tx.entity.TableTwo</value>
</list>
</property>
<property name="dataSource" ref="dataSource2" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.connection.autocommit">false</prop>
<prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
<prop key="hibernate.current_session_context_class">jta</prop>
<prop key="javax.persistence.transactionType">jta</prop>
<prop key="hibernate.transaction.factory_class">com.atomikos.icatch.jta.hibernate3.AtomikosJTATransactionFactory </prop>
<prop key="hibernate.transaction.manager_lookup_class">com.atomikos.icatch.jta.hibernate3.TransactionManagerLookup</prop>
</props>
</property>
</bean>
<bean id="dataSource2" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close">
<property name="uniqueResourceName" value="DataSource2" />
<property name="xaDataSource" ref="dataBase2" />
<property name="maxPoolSize" value="20" />
<property name="minPoolSize" value="10"/>
</bean>
<bean id="dataBase2" class="org.postgresql.xa.PGXADataSource" lazy-init="true">
<property name="user" value="tester2" />
<property name="password" value="123456" />
<property name="serverName" value="localhost" />
<property name="portNumber" value="5432" />
<property name="databaseName" value="test_db2" />
<property name="url" value="jdbc:postgresql://localhost:5432/test_db2" />
</bean>
<!-- Atomikos -->
<bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init" destroy-method="close">
<property name="forceShutdown" value="false" />
</bean>
<bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
<property name="transactionTimeout" value="3000" />
</bean>
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager" depends-on="atomikosTransactionManager,atomikosUserTransaction">
<property name="transactionManager" ref="atomikosTransactionManager" />
<property name="userTransaction" ref="atomikosUserTransaction" />
<property name="allowCustomIsolationLevels" value="true" />
</bean>
<!-- DAO -->
<bean id="tableOneDao" class="com.byteslounge.spring.tx.dao.impl.TableOneDaoImpl">
<property name="sessionFactory" ref="sessionFactory1" />
</bean>
<bean id="tableTwoDao" class="com.byteslounge.spring.tx.dao.impl.TableTwoDaoImpl">
<property name="sessionFactory" ref="sessionFactory2" />
</bean>
</beans>
下面是一个代码示例,用于说明如何保存数据:
@Transactional(rollbackFor=Exception.class)
public void persist(TableOne tableOne, TableTwo tableTwo) throws Exception {
tableOneDao.save(tableOne);
tableTwoDao.save(tableTwo);
}
DAO类具有相同的保存逻辑:
@Service
public class TableOneDaoImpl extends HibernateDaoSupport implements TableOneDao {
@Override
public void save(TableOne tableOne) throws Exception {
Session session = null;
try {
session = getSessionFactory().openSession();
session.save(tableOne);
} catch (Exception e) {
throw new Exception("Could not save tableOne!", e);
} finally {
if (session != null) {
session.close();
}
}
}
}
并且听到我在执行数据保存逻辑时得到的日志:
13:48:58.521 [http-bio-8080-exec-3] DEBUG com.atomikos.icatch.imp.CompositeTransactionManagerImp - createCompositeTransaction ( 10000 ): created new ROOT transaction with id 192.168.50.67.tm147946973850200001
13:48:58.605 [http-bio-8080-exec-3] DEBUG org.hibernate.impl.SessionImpl - opened session at timestamp: 14794697385
13:48:58.610 [http-bio-8080-exec-3] DEBUG com.atomikos.icatch.imp.CompositeTransactionImp - registerSynchronization ( com.atomikos.icatch.jta.Sync2Sync@57e48ad3 ) for transaction 192.168.50.67.tm147946973850200001
13:48:58.610 [http-bio-8080-exec-3] DEBUG org.hibernate.jdbc.JDBCContext - successfully registered Synchronization
13:48:58.612 [http-bio-8080-exec-3] DEBUG org.hibernate.jdbc.AbstractBatcher - about to open PreparedStatement (open PreparedStatements: 0, globally: 0)
13:48:58.612 [http-bio-8080-exec-3] DEBUG org.hibernate.jdbc.ConnectionManager - opening JDBC connection
13:48:58.612 [http-bio-8080-exec-3] DEBUG com.atomikos.jdbc.AbstractDataSourceBean - AtomikosDataSoureBean 'DataSource1': getConnection()...
13:48:58.612 [http-bio-8080-exec-3] INFO com.atomikos.jdbc.AbstractDataSourceBean - AtomikosDataSoureBean 'DataSource1': init...
13:48:58.613 [http-bio-8080-exec-3] DEBUG org.hibernate.SQL - select nextval ('hibernate_sequence')
Hibernate: select nextval ('hibernate_sequence')
13:48:58.616 [http-bio-8080-exec-3] DEBUG com.atomikos.icatch.imp.CompositeTransactionImp - addParticipant ( XAResourceTransaction: 3139322E3136382E35302E36372E746D313437393436393733383530323030303031:3139322E3136382E35302E36372E746D31 ) for transaction 192.168.50.67.tm147946973850200001
13:48:58.616 [http-bio-8080-exec-3] DEBUG com.atomikos.datasource.xa.XAResourceTransaction - XAResource.start ( 3139322E3136382E35302E36372E746D313437393436393733383530323030303031:3139322E3136382E35302E36372E746D31 , XAResource.TMNOFLAGS ) on resource DataSource1 represented by XAResource instance org.postgresql.xa.PGXAConnection@7b57a36a
13:48:58.617 [http-bio-8080-exec-3] DEBUG com.atomikos.icatch.imp.CompositeTransactionImp - registerSynchronization ( com.atomikos.jdbc.AtomikosConnectionProxy$JdbcRequeueSynchronization@ede1e016 ) for transaction 192.168.50.67.tm147946973850200001
13:48:58.617 [http-bio-8080-exec-3] DEBUG com.atomikos.jdbc.AtomikosConnectionProxy - atomikos connection proxy for Pooled connection wrapping physical connection org.postgresql.jdbc3g.Jdbc3gConnection@2a39bdf5: calling prepareStatement(select nextval ('hibernate_sequence'))...
13:48:58.627 [http-bio-8080-exec-3] DEBUG org.hibernate.id.SequenceGenerator - Sequence identifier generated: BasicHolder[java.lang.Integer[43]]
13:48:58.627 [http-bio-8080-exec-3] DEBUG org.hibernate.jdbc.AbstractBatcher - about to close PreparedStatement (open PreparedStatements: 1, globally: 1)
13:48:58.627 [http-bio-8080-exec-3] DEBUG org.hibernate.jdbc.ConnectionManager - aggressively releasing JDBC connection
13:48:58.627 [http-bio-8080-exec-3] DEBUG org.hibernate.jdbc.ConnectionManager - releasing JDBC connection [ (open PreparedStatements: 0, globally: 0) (open ResultSets: 0, globally: 0)]
13:48:58.627 [http-bio-8080-exec-3] DEBUG com.atomikos.jdbc.AtomikosConnectionProxy - atomikos connection proxy for Pooled connection wrapping physical connection org.postgresql.jdbc3g.Jdbc3gConnection@2a39bdf5: isClosed()...
13:48:58.627 [http-bio-8080-exec-3] DEBUG com.atomikos.jdbc.AtomikosConnectionProxy - atomikos connection proxy for Pooled connection wrapping physical connection org.postgresql.jdbc3g.Jdbc3gConnection@2a39bdf5: calling getWarnings...
13:48:58.627 [http-bio-8080-exec-3] DEBUG com.atomikos.jdbc.AtomikosConnectionProxy - atomikos connection proxy for Pooled connection wrapping physical connection org.postgresql.jdbc3g.Jdbc3gConnection@2a39bdf5: calling clearWarnings...
13:48:58.627 [http-bio-8080-exec-3] DEBUG com.atomikos.jdbc.AtomikosConnectionProxy - atomikos connection proxy for Pooled connection wrapping physical connection org.postgresql.jdbc3g.Jdbc3gConnection@2a39bdf5: close()...
13:48:58.627 [http-bio-8080-exec-3] DEBUG com.atomikos.datasource.xa.XAResourceTransaction - XAResource.end ( 3139322E3136382E35302E36372E746D313437393436393733383530323030303031:3139322E3136382E35302E36372E746D31 , XAResource.TMSUCCESS ) on resource DataSource1 represented by XAResource instance org.postgresql.xa.PGXAConnection@7b57a36a
13:48:58.628 [http-bio-8080-exec-3] DEBUG org.hibernate.event.def.AbstractSaveEventListener - generated identifier: 43, using strategy: org.hibernate.id.SequenceGenerator
13:48:58.638 [http-bio-8080-exec-3] DEBUG com.atomikos.icatch.jta.Sync2Sync - beforeCompletion() called on Synchronization: org.hibernate.transaction.synchronization.HibernateSynchronizationImpl@3aa81ddb
13:48:58.638 [http-bio-8080-exec-3] DEBUG com.atomikos.icatch.imp.CompositeTransactionImp - commit() done (by application) of transaction 192.168.50.67.tm147946973850200001
13:48:58.641 [http-bio-8080-exec-3] DEBUG com.atomikos.datasource.xa.XAResourceTransaction - XAResource.commit ( 3139322E3136382E35302E36372E746D313437393436393733383530323030303031:3139322E3136382E35302E36372E746D31 , true ) on resource DataSource1 represented by XAResource instance org.postgresql.xa.PGXAConnection@7b57a36a
13:48:58.643 [http-bio-8080-exec-3] DEBUG com.atomikos.icatch.jta.Sync2Sync - afterCompletion ( STATUS_COMMITTED ) called on Synchronization: org.hibernate.transaction.synchronization.HibernateSynchronizationImpl@3aa81ddb
我还将 postgresql.conf 中的max_prepared_transactions参数设置为至少与 max_connections 一样大,因为它建议在相关的Atomikos {{3对于PostgreSQL。
我想再次强调,相同的代码可以正常使用MySQL 。只有在配置此Web应用程序以使用PostgreSQL时,才会出现这个奇怪的问题。
任何人都知道可能会出现什么问题?
答案 0 :(得分:3)
以下答案给了我一个提示:
Hibernate not saving Object in the Database?
以下是一个工作示例(请注意session.flush()
,在关闭 会话之前必须将其称为):
@Service
public class TableOneDaoImpl extends HibernateDaoSupport implements TableOneDao {
@Override
public void save(TableOne tableOne) throws Exception {
Session session = null;
try {
session = getSessionFactory().openSession();
session.save(tableOne);
session.flush();
} catch (Exception e) {
throw new Exception("Could not save tableOne!", e);
} finally {
if (session != null) {
session.close();
}
}
}
}
而且,当使用MySQL时,它可以正常运行而不需要显式刷新。
上面提到的测试Web应用程序使用纯 Hibernate 配置和Hibernate的SessionFactory(即没有 persistence.xml 和JPA entityManager )。但我也有相同的测试应用程序配置为使用 JPA 。在该应用程序中,一切都可以正常工作,而无需使用MySQL和PostgreSQL进行明确的刷新。