2

我正在尝试使用一个实体来坚持。当我自己处理交易时,我的测试用例有效(数据保存在数据库中)。

public User saveUser(User user) {
    getEm().getTransaction().begin();
    getEm().persist(user);
    getEm().getTransaction().commit();      
    return user;
}

(实体管理器来自我的 DAO 类扩展的模板类)

当我配置 spring 来处理事务时,它会停止持久化数据。

测试上下文.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:tx="http://www.springframework.org/schema/tx" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:jdbc="http://www.springframework.org/schema/jdbc"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans-3.1.xsd 
    http://www.springframework.org/schema/jdbc 
    http://www.springframework.org/schema/jdbc/spring-jdbc-3.1.xsd 
    http://www.springframework.org/schema/tx 
    http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">

    <tx:annotation-driven transaction-manager="transactionManager"/>

    <bean id="entityManagerFactory"
        class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="persistenceUnitName" value="domainPU" />
        <property name="loadTimeWeaver">
            <bean
                class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
        </property>
    </bean>

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://192.168.5.111/testdb" />
        <property name="username" value="sdgdsgs" />
        <property name="password" value="sdfgdsg" />
    </bean>

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory">
            <ref local="entityManagerFactory" />
        </property>
    </bean>

</beans>

用户存储库测试:

@ContextConfiguration(locations = { "classpath:test-context.xml", "classpath:module-context.xml" })
@TransactionConfiguration(defaultRollback=false)
public class UsersRepositoryTest extends AbstractTransactionalTestNGSpringContextTests {

    @Autowired
    private UsersRepository usersRepository;

    @Test(dependsOnMethods = {"findUserByLogin"})
    public void registerUser() throws DataAccessException {
        User myUser = getUserHomer();
        usersRepository.saveUser(myUser);

        User returnedUser = usersRepository.getUserByLogin(myUser.getLogin());

        assertNotNull(returnedUser);
                ...
    }
}

堆栈跟踪:

2012-10-22 08:26:26 org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean createNativeEntityManagerFactory
INFO: Building JPA container EntityManagerFactory for persistence unit 'domainPU'
2012-10-22 08:26:26 org.hibernate.annotations.common.Version <clinit>
INFO: HCANN000001: Hibernate Commons Annotations {4.0.1.Final}
2012-10-22 08:26:26 org.hibernate.Version logVersion
INFO: HHH000412: Hibernate Core {4.1.6.Final}
2012-10-22 08:26:26 org.hibernate.cfg.Environment <clinit>
INFO: HHH000206: hibernate.properties not found
2012-10-22 08:26:26 org.hibernate.cfg.Environment buildBytecodeProvider
INFO: HHH000021: Bytecode provider name : javassist
2012-10-22 08:26:26 org.hibernate.ejb.Ejb3Configuration configure
INFO: HHH000204: Processing PersistenceUnitInfo [
    name: domainPU
    ...]
2012-10-22 08:26:26 org.hibernate.service.jdbc.connections.internal.ConnectionProviderInitiator instantiateExplicitConnectionProvider
INFO: HHH000130: Instantiating explicit connection provider: org.hibernate.ejb.connection.InjectedDataSourceConnectionProvider
2012-10-22 08:26:27 org.hibernate.dialect.Dialect <init>
INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect
2012-10-22 08:26:27 org.hibernate.engine.jdbc.internal.LobCreatorBuilder useContextualLobCreation
INFO: HHH000424: Disabling contextual LOB creation as createClob() method threw error : java.lang.reflect.InvocationTargetException
2012-10-22 08:26:27 org.hibernate.engine.transaction.internal.TransactionFactoryInitiator initiateService
INFO: HHH000268: Transaction strategy: org.hibernate.engine.transaction.internal.jdbc.JdbcTransactionFactory
2012-10-22 08:26:27 org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory <init>
INFO: HHH000397: Using ASTQueryTranslatorFactory
2012-10-22 08:26:27 org.springframework.context.support.AbstractApplicationContext$BeanPostProcessorChecker postProcessAfterInitialization
INFO: Bean 'entityManagerFactory' of type [class org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2012-10-22 08:26:27 org.springframework.context.support.AbstractApplicationContext$BeanPostProcessorChecker postProcessAfterInitialization
INFO: Bean 'hibernateTranslator' of type [class org.springframework.orm.hibernate4.HibernateExceptionTranslator] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2012-10-22 08:26:27 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@5c28305d: defining beans [org.springframework.aop.config.internalAutoProxyCreator,org.springframework.transaction.annotation.AnnotationTransactionAttributeSource#0,org.springframework.transaction.interceptor.TransactionInterceptor#0,org.springframework.transaction.config.internalTransactionAdvisor,entityManagerFactory,dataSource,transactionManager,org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor#0,hibernateTranslator,usersDao,zoneDao,usersRepository,zoneRepository,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.annotation.internalPersistenceAnnotationProcessor,org.springframework.context.annotation.ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor#0]; root of factory hierarchy
2012-10-22 08:26:27 org.springframework.test.context.transaction.TransactionalTestExecutionListener startNewTransaction
INFO: Began transaction (1): transaction manager [org.springframework.orm.jpa.JpaTransactionManager@3341b736]; rollback [false]
Hibernate: select user0_.id as id0_, user0_.active as active0_, user0_.creation_date as creation3_0_, user0_.email as email0_, user0_.last_login_date as last5_0_, user0_.login as login0_, user0_.password as password0_, user0_1_.AVATAR as AVATAR1_, user0_1_.FIRST_NAME as FIRST2_1_, user0_1_.ID as ID1_, user0_1_.LAST_NAME as LAST4_1_, case when user0_1_.id is not null then 1 when user0_.id is not null then 0 end as clazz_ from user user0_ left outer join client user0_1_ on user0_.id=user0_1_.id where user0_.login=?
Hibernate: select phone0_.id as id2_0_, phone0_.phone_number as phone2_2_0_, phone0_.user_id as user3_2_0_ from phone phone0_ where phone0_.id=?
Hibernate: select address0_.ID as ID6_0_, address0_.apartment_number as apartment2_6_0_, address0_.city as city6_0_, address0_.country as country6_0_, address0_.house_number as house5_6_0_, address0_.post_code as post6_6_0_, address0_.street as street6_0_ from address address0_ where address0_.ID=?
Hibernate: select user0_.id as id0_, user0_.active as active0_, user0_.creation_date as creation3_0_, user0_.email as email0_, user0_.last_login_date as last5_0_, user0_.login as login0_, user0_.password as password0_, user0_1_.AVATAR as AVATAR1_, user0_1_.FIRST_NAME as FIRST2_1_, user0_1_.ID as ID1_, user0_1_.LAST_NAME as LAST4_1_, case when user0_1_.id is not null then 1 when user0_.id is not null then 0 end as clazz_ from user user0_ left outer join client user0_1_ on user0_.id=user0_1_.id where user0_.login=?
2012-10-22 08:26:27 org.springframework.test.context.transaction.TransactionalTestExecutionListener endTransaction
INFO: Committed transaction after test execution for test context [[TestContext@63edf84f testClass = UsersRepositoryTest, testInstance = pl.eports.zonen.user.repository.UsersRepositoryTest@7e3bc473, testMethod = findUserByLogin@UsersRepositoryTest, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@1c493dca testClass = UsersRepositoryTest, locations = '{classpath:test-context.xml, classpath:module-context.xml}', classes = '{}', activeProfiles = '{}', contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader']]]
2012-10-22 08:26:27 org.springframework.test.context.transaction.TransactionalTestExecutionListener startNewTransaction
INFO: Began transaction (2): transaction manager [org.springframework.orm.jpa.JpaTransactionManager@3341b736]; rollback [false]
Hibernate: select user0_.id as id0_, user0_.active as active0_, user0_.creation_date as creation3_0_, user0_.email as email0_, user0_.last_login_date as last5_0_, user0_.login as login0_, user0_.password as password0_, user0_1_.AVATAR as AVATAR1_, user0_1_.FIRST_NAME as FIRST2_1_, user0_1_.ID as ID1_, user0_1_.LAST_NAME as LAST4_1_, case when user0_1_.id is not null then 1 when user0_.id is not null then 0 end as clazz_ from user user0_ left outer join client user0_1_ on user0_.id=user0_1_.id where user0_.login=?
2012-10-22 08:26:27 org.springframework.test.context.transaction.TransactionalTestExecutionListener endTransaction
INFO: Committed transaction after test execution for test context [[TestContext@63edf84f testClass = UsersRepositoryTest, testInstance = pl.eports.zonen.user.repository.UsersRepositoryTest@7e3bc473, testMethod = registerUser@UsersRepositoryTest, testException = java.lang.AssertionError: expected object to not be null, mergedContextConfiguration = [MergedContextConfiguration@1c493dca testClass = UsersRepositoryTest, locations = '{classpath:test-context.xml, classpath:module-context.xml}', classes = '{}', activeProfiles = '{}', contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader']]]
PASSED: findUserByLogin
FAILED: registerUser
java.lang.AssertionError: expected object to not be null
    at org.testng.Assert.fail(Assert.java:94)
        ...

从stacktrace可以看出,transaction好像已经启动完成了,但是没有产生sql插入,也没有数据插入db(数据回滚设置为false)

我尝试在persist() 之后强制刷新数据,但spring 托管事务似乎到那时已经完成。

我在论坛上寻找过类似的问题,但没有一个有效。如有必要,我可以提供更多代码。

我正在使用 Spring 3.1.2 和 Hibernate 4.1.6.Final

更新:

DAO:(@Transactional 注解暂时放在服务层上)

@Repository
public class JpaUsersDAO extends JpaDAOTemplate<User> implements UsersDAO {

public JpaUsersDAO(EntityManagerFactory factory) {
    super(factory.createEntityManager(), User.class);
}

    @Transactional
    public User saveUser(User user) {
        getEm().persist(user);  
        return user;
    }
}

DAO模板:

public abstract class JpaDAOTemplate<T extends Serializable> {

    private EntityManager em;

    private Class<T> clazz;

    public JpaDAOTemplate(EntityManager em, final Class<T> clazz) {
        this.em = em;
        this.clazz = clazz;
    }

    public EntityManager getEm() {
        return em;
    }

    public T getById(Integer id) {...}
    ...
}

豆定义:

<bean id="usersDao"
    class="pl.package.user.dao.impl.JpaUsersDAO">
    <constructor-arg ref="entityManagerFactory" />
</bean>

似乎事务在选择之后和调用 getEm().persist() 之前的某处丢失 -调用 persist() 时org.hibernate.ejb.TransactionImpl#tx 为空,但它是org.hibernate.engine.transaction.internal。调用前的jdbc.JDBCTransaction(当进行 sql 选择语句调用时)

更新 2 调试级别堆栈跟踪:

[2012-10-23 11:15:14,546]DEBUG 38629[main] - org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:365) - Creating new transaction with name [registerUser]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
[2012-10-23 11:15:33,252]DEBUG 57335[main] - org.hibernate.internal.SessionImpl.<init>(SessionImpl.java:316) - Opened session at timestamp: 13509837332
[2012-10-23 11:15:33,252]DEBUG 57335[main] - org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:368) - Opened new EntityManager [org.hibernate.ejb.EntityManagerImpl@18025c5c] for JPA transaction
[2012-10-23 11:15:40,085]DEBUG 64168[main] - org.hibernate.engine.transaction.spi.AbstractTransactionImpl.begin(AbstractTransactionImpl.java:158) - begin
[2012-10-23 11:15:40,101]DEBUG 64184[main] - org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.obtainConnection(LogicalConnectionImpl.java:295) - Obtaining JDBC connection
[2012-10-23 11:15:40,101]DEBUG 64184[main] - org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.obtainConnection(LogicalConnectionImpl.java:301) - Obtained JDBC connection
[2012-10-23 11:15:40,101]DEBUG 64184[main] - org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.doBegin(JdbcTransaction.java:69) - initial autocommit status: true
[2012-10-23 11:15:40,101]DEBUG 64184[main] - org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.doBegin(JdbcTransaction.java:71) - disabling autocommit
[2012-10-23 11:16:06,934]DEBUG 91017[main] - org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:407) - Not exposing JPA transaction [org.hibernate.ejb.EntityManagerImpl@18025c5c] as JDBC transaction because JpaDialect [org.springframework.orm.jpa.DefaultJpaDialect@5f343722] does not support JDBC Connection retrieval
[2012-10-23 11:16:06,934]DEBUG 91017[main] - org.springframework.test.context.transaction.TransactionalTestExecutionListener.isRollback(TransactionalTestExecutionListener.java:357) - No method-level @Rollback override: using default rollback [false] for test context [[TestContext@5809fdee testClass = UsersRepositoryTest, testInstance = pl.eports.zonen.user.repository.UsersRepositoryTest@3fde891b, testMethod = registerUser@UsersRepositoryTest, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@77fe4169 testClass = UsersRepositoryTest, locations = '{classpath:test-context.xml, classpath:module-context.xml}', classes = '{}', activeProfiles = '{}', contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader']]]
[2012-10-23 11:16:06,934] INFO 91017[main] - org.springframework.test.context.transaction.TransactionalTestExecutionListener.startNewTransaction(TransactionalTestExecutionListener.java:275) - Began transaction (1): transaction manager [org.springframework.orm.jpa.JpaTransactionManager@37977909]; rollback [false]
[2012-10-23 11:17:08,103]DEBUG152186[main] - org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:245) - Returning cached instance of singleton bean 'org.springframework.transaction.interceptor.TransactionInterceptor#0'
[2012-10-23 11:17:08,118]DEBUG152201[main] - org.springframework.transaction.interceptor.AbstractFallbackTransactionAttributeSource.getTransactionAttribute(AbstractFallbackTransactionAttributeSource.java:106) - Adding transactional method 'saveUser' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
[2012-10-23 11:17:08,118]DEBUG152201[main] - org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:245) - Returning cached instance of singleton bean 'txManager'
[2012-10-23 11:17:27,073]DEBUG171156[main] - org.springframework.orm.jpa.JpaTransactionManager.doGetTransaction(JpaTransactionManager.java:331) - Found thread-bound EntityManager [org.hibernate.ejb.EntityManagerImpl@18025c5c] for JPA transaction
[2012-10-23 11:18:04,031]DEBUG208114[main] - org.springframework.transaction.support.AbstractPlatformTransactionManager.handleExistingTransaction(AbstractPlatformTransactionManager.java:470) - Participating in existing transaction

最终更新:

问题在于我将 EntityManagerFactory 注入我的 DAO 的方式。我使用 applicationContext.xml 配置文件来注入它(这种方式 EMF 不参与事务)。为了让它工作,我将我的 DAO 类更改为:

@Repository
public class JpaUsersDAO extends JpaDAOTemplate<User> implements UsersDAO {

    @PersistenceContext
    private EntityManager em;
...
}
4

1 回答 1

2

您正在使用Spring 3.0. 我不清楚你的编码public User saveUser(User user)方法。即使您使用Spring 3.0,您也可以自己处理交易(Bean Management Transaction)。它只需要使用@Transactional(propagation = Propagation.REQUIRED)注释。propagation根据您的流程更改类型。

示例 DAO:

GroupDAO.java

@Repository("GroupDAO")
public class GroupDAO {
    @PersistenceContext
    protected EntityManager em;

    @Transactional(propagation = Propagation.REQUIRED)
    public void insert(Group group) {
        try {
            em.persist(group);
            em.flush();
        } catch (PersistenceException pe) {
        }
    }
}

spring-bean.xml 带有EclipseLink JPA<-- 仅供参考。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:faces="http://www.springframework.org/schema/faces"
       xmlns:int-security="http://www.springframework.org/schema/integration/security"
       xmlns:tx="http://www.springframework.org/schema/tx" xmlns:sec="http://www.springframework.org/schema/security"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/integration/security http://www.springframework.org/schema/integration/security/spring-integration-security-2.0.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd
        http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/faces http://www.springframework.org/schema/faces/spring-faces-2.0.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">

    <context:annotation-config/>    
    <context:component-scan base-package="your-package"/>
    <tx:annotation-driven transaction-manager="transactionManager" />
    <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>

    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://192.168.5.111/testdb"/>
        <property name="username" value="your-username"/>
        <property name="password" value="your-password"/>
    </bean>

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>

    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<!--        <property name="dataSource" ref="dataSource"/>-->
        <property name="persistenceUnitName" value="domainPU"/>
        <property name="jpaVendorAdapter" ref="jpaVendorAdapter"/>
        <property name="jpaDialect">
            <bean class="org.springframework.orm.jpa.vendor.EclipseLinkJpaDialect"/>
        </property>
        <property name="jpaPropertyMap">
            <props>
                <prop key="eclipselink.weaving">false</prop>
            </props>
        </property>

        <property name="loadTimeWeaver">
            <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver">
            </bean>
        </property>
    </bean>

    <bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter">
        <property name="databasePlatform" value="org.eclipse.persistence.platform.database.MySQLPlatform"/>
<!--        <property name="databasePlatform" value="org.eclipse.persistence.platform.database.OraclePlatform" />-->
        <property name="generateDdl" value="false"/>
        <property name="showSql" value="true"/>
    </bean>
</beans>    
于 2012-10-23T06:01:30.537 回答