Atomikos:使用PostgreSQL时未保存数据

时间:2016-11-20 19:15:16

标签: java spring postgresql hibernate atomikos

使用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时,才会出现这个奇怪的问题。

任何人都知道可能会出现什么问题?

1 个答案:

答案 0 :(得分:3)

解决方案

好的,神秘的解决了!事实证明,我需要在会话对象上明确调用flush()

以下答案给了我一个提示:
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进行明确的刷新。