0

我正在使用 Spring 3.2.1.RELEASE、Hibernate 4.1.9.FINAL 作为 jpa 提供程序、spring data 1.2.0.RELEASE 用于存储库和 bitronix 2.1.3 作为事务管理器来启动一个项目。我对所有这些技术都是新手,所以如果我遗漏了一些重要的观点,我很抱歉。

我正在运行一个简单的单元测试来在数据库中创建一个用户对象:

@ContextConfiguration(locations={"classpath:tests-context.xml"})
@Transactional
@TransactionConfiguration(transactionManager="transactionManager", defaultRollback=false)
@RunWith(SpringJUnit4ClassRunner.class)
public class UserTest extends AbstractTransactionalJUnit4SpringContextTests
{
@Autowired
protected UserMgmtService userService;

@Test
public void createUserTest()
{
    User user = new User();
    user.setFirstName("jonny");
    user.setLastName("doe");
    user.setUsername("user5");
    user.setPasswordHash("1238");
    user.setEmail("user5@domain.com");
    System.out.println("Created user" + user);
    User testUser = userService.saveUser(user);
    System.out.println("Saved user" + testUser);
    Assert.assertEquals("The user should be equal to saved user", user, testUser);
}
}

我使用的 test-context.xml 如下:

<context:annotation-config/>

<!--  Bitronix Transaction Manager embedded configuration -->
<bean id="btmConfig" factory-method="getConfiguration"
    class="bitronix.tm.TransactionManagerServices">
</bean>

<!-- DataSource definition -->

<bean id="dataSource" class="bitronix.tm.resource.jdbc.PoolingDataSource"
    init-method="init" destroy-method="close">
    <property name="className" value="bitronix.tm.resource.jdbc.lrc.LrcXADataSource" />
    <property name="uniqueName" value="jdbc/MySQL_Test_Repository" />
    <property name="minPoolSize" value="0" />
    <property name="maxPoolSize" value="5" />
    <property name="allowLocalTransactions" value="true" />
    <property name="driverProperties">
        <props>
            <prop key="driverClassName">com.mysql.jdbc.Driver</prop>
            <prop key="url">jdbc:mysql://localhost:3306 MySQL_Test_Repository</prop>
            <prop key="user">root</prop>
            <prop key="password">123654</prop>
        </props>
    </property>
</bean>

 <!-- create BTM transaction manager -->
 <bean id="BitronixTransactionManager" factory-method="getTransactionManager"
    class="bitronix.tm.TransactionManagerServices" depends-on="btmConfig,dataSource"
    destroy-method="shutdown" />

<!-- Transaction manager -->
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager" 
        depends-on="BitronixTransactionManager">
    <property name="transactionManager" ref="BitronixTransactionManager" />
    <property name="userTransaction" ref="BitronixTransactionManager" />
    <property name="allowCustomIsolationLevels" value="true" />
    </bean> 

<!-- AOP configuration for transactions --> 
<aop:config>
    <aop:pointcut id="userServiceMethods"
        expression="execution(* users.service.UserMgmtService.*(..))" />

    <aop:advisor advice-ref="transactionAdvice"
    pointcut-ref="userServiceMethods" />

</aop:config> 

<!-- Transaction configuration -->
<tx:advice id="transactionAdvice" transaction-manager="transactionManager">    
    <tx:attributes>
        <tx:method name="*" rollback-for="Exception" />
    </tx:attributes>
</tx:advice>

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


   <!-- bean declaring the property map for the entity manager factory bean -->
   <bean id="jpaPropertyMap" class="org.springframework.beans.factory.config.MapFactoryBean">
    <property name="sourceMap">
        <map>
            <entry key="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect"/>

            <!-- show the sql code executed -->
            <entry key="hibernate.show_sql" value="true"/>

            <!-- format the shown sql code -->
            <entry key="hibernate.format_sql" value="true"/>

            <!-- enables hibernate to create db schemas from classes;
            Possible values: validate | update | create | create-drop 
            with create-drop the schema will be created/dropped for each hibernate sesssion open/close-->
            <entry key="hibernate.hbm2ddl.auto" value="update"/> 

            <!-- sets the hibernate classname for the TransactionManagerLookup;
            hibernate wraps and hides the underlying transaction system and needs a reference
            to the transaction manager used  -->
            <entry key="hibernate.transaction.factory_class" value="org.hibernate.transaction.JTATransactionFactory"/>
            <entry key="hibernate.transaction.manager_lookup_class" 
                    value="org.hibernate.transaction.BTMTransactionManagerLookup"/>
        </map>
    </property>
</bean>

    <!-- bean declaring the entity manager factory -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="persistenceUnitName" value="ruleEditor.persistence"/>
    <property name="dataSource" ref="dataSource" />
    <property name="jpaPropertyMap" ref="jpaPropertyMap"/>
    </bean>

    <!-- The location where Spring scans for interfaces implementing the JPARepository
    and creates their implementation -->
    <jpa:repositories base-package="users.repository" />

    <!--  Persistence annotations for post processing -->
    <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />


<!-- User service implementation bean -->
  <bean id="userMgmtService" class="users.service.impl.UserMgmtServiceImpl"/> 

</beans>

Repository 实现是一个标准的 JPARepository:

public interface UserRepository extends JpaRepository<User, Long>
{
User findByUsername(String username);
}

并且服务实现只调用存储库:

@Service("userMgmtService")
public class UserMgmtServiceImpl implements UserMgmtService
{
@Autowired
private UserRepository userRepository;

@Override
public User saveUser(User user)
{
    User savedUser = userRepository.save(user);
    return savedUser;
}

问题是当我执行测试时,它通过了,但没有在数据库中创建用户。我认为这可能是因为回滚行为,所以我在用户测试中设置了 defaultRollback=false。以下是事务框架在设置为 DEBUG 时提供的一些可能相关的信息:

Running UserTest
2013-02-12 13:01:12 INFO  XmlBeanDefinitionReader:315 - Loading XML bean definitionsfrom class path resource [tests-context.xml]
2013-02-12 13:01:12 INFO  GenericApplicationContext:510 - Refreshing  org.springframework.context.support.GenericApplicationContext@7d95609: startup date [Tue  Feb 12 13:01:12 CET 2013]; root of context hierarchy
2013-02-12 13:01:12 INFO  GenericApplicationContext:1374 - Bean 'dataSource' of type [class bitronix.tm.resource.jdbc.PoolingDataSource] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2013-02-12 13:01:12 INFO  GenericApplicationContext:1374 - Bean 'jpaPropertyMap' of type [class org.springframework.beans.factory.config.MapFactoryBean] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2013-02-12 13:01:12 INFO  GenericApplicationContext:1374 - Bean 'jpaPropertyMap' of type [class java.util.LinkedHashMap] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2013-02-12 13:01:12 INFO  LocalContainerEntityManagerFactoryBean:264 - Building JPA container EntityManagerFactory for persistence unit 'ruleEditor.persistence'

2013-02-12 13:01:13 INFO  SchemaUpdate:182 - HHH000228: Running hbm2ddl schema update
2013-02-12 13:01:13 INFO  SchemaUpdate:193 - HHH000102: Fetching database metadata
2013-02-12 13:01:13 INFO  SchemaUpdate:205 - HHH000396: Updating schema
2013-02-12 13:01:13 INFO  TableMetadata:65 - HHH000261: Table found: MySQL_Test_Repository.user
2013-02-12 13:01:13 INFO  TableMetadata:66 - HHH000037: Columns: [id, enabled, first_name, username, email, password_hash, last_name]
2013-02-12 13:01:13 INFO  TableMetadata:68 - HHH000108: Foreign keys: []
2013-02-12 13:01:13 INFO  TableMetadata:69 - HHH000126: Indexes: [id, username, primary]
2013-02-12 13:01:13 INFO  SchemaUpdate:240 - HHH000232: Schema update complete
2013-02-12 13:01:13 INFO  BitronixTransactionManager:390 - Bitronix Transaction Manager version 2.1.3
2013-02-12 13:01:13 WARN  Configuration:649 - cannot get this JVM unique ID. Make sure it is configured and you only use ASCII characters. Will use IP address instead (unsafe for production usage!).
2013-02-12 13:01:13 INFO  Recoverer:152 - recovery committed 0 dangling transaction(s) and rolled back 0 aborted transaction(s) on 1 resource(s) [jdbc/MySQL_Test_Repository] 
2013-02-12 13:01:14 INFO  GenericApplicationContext:1374 - 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)
2013-02-12 13:01:14 INFO  GenericApplicationContext:1374 - Bean 'org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor#0' of type [class org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2013-02-12 13:01:14 INFO  GenericApplicationContext:1374 - Bean 'org.springframework.transaction.annotation.AnnotationTransactionAttributeSource#0' of type [class org.springframework.transaction.annotation.AnnotationTransactionAttributeSource] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2013-02-12 13:01:14 INFO  GenericApplicationContext:1374 - Bean 'org.springframework.transaction.config.internalTransactionAdvisor' of type [class org.springframework.transaction.interceptor.BeanFactoryTransactionAttributeSourceAdvisor] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2013-02-12 13:01:14 INFO  JtaTransactionManager:470 - Using JTA UserTransaction: a BitronixTransactionManager with 0 in-flight transaction(s)
2013-02-12 13:01:14 INFO  JtaTransactionManager:481 - Using JTA TransactionManager: a BitronixTransactionManager with 0 in-flight transaction(s)
2013-02-12 13:01:14 DEBUG NameMatchTransactionAttributeSource:94 - Adding transactional method [*] with attribute [PROPAGATION_REQUIRED,ISOLATION_DEFAULT,-Exception]
2013-02-12 13:01:14 DEBUG AnnotationTransactionAttributeSource:106 - Adding transactional method 'createUserTest' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
2013-02-12 13:01:14 DEBUG AnnotationTransactionAttributeSource:106 - Adding transactional method 'createUserTest' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
2013-02-12 13:01:14 DEBUG JtaTransactionManager:365 - Creating new transaction with name [createUserTest]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
2013-02-12 13:01:14 INFO  TransactionalTestExecutionListener:279 - Began transaction (1): transaction manager  [org.springframework.transaction.jta.JtaTransactionManager@2a3998f8]; rollback [false]
Created userusers.model.User@63aa9dc1[userId=0,first name=jonny,last name=doe,email=user5@domain.com,username=user5,password=1238,enabled=false]
2013-02-12 13:01:14 DEBUG JtaTransactionManager:470 - Participating in existing transaction
2013-02-12 13:01:14 DEBUG JtaTransactionManager:470 - Participating in existing transaction
Saved userusers.model.User@6b38579e[userId=0,first name=jonny,last name=doe,email=user5@domain.com,username=user5,password=1238,enabled=false]
2013-02-12 13:01:14 DEBUG JtaTransactionManager:752 - Initiating transaction commit
2013-02-12 13:01:14 WARN  Preparer:69 - executing transaction with 0 enlisted resource
2013-02-12 13:01:14 INFO  TransactionalTestExecutionListener:299 - Committed  transaction after test execution for test context [TestContext@5a93c236 testClass =  UserTest, testInstance = UserTest@1ab395af, testMethod = createUserTest@UserTest, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@42821db testClass = UserTest, locations = '{classpath:tests-context.xml}', classes = '{}', contextInitializerClasses = '[]', activeProfiles = '{}', contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader']]
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 3.029 sec
2013-02-12 13:01:14 INFO  GenericApplicationContext:1042 - Closing org.springframework.context.support.GenericApplicationContext@7d95609: startup date [Tue Feb 12 13:01:12 CET 2013]; root of context hierarchy
2013-02-12 13:01:14 INFO  BitronixTransactionManager:320 - shutting down Bitronix Transaction Manager
2013-02-12 13:01:14 INFO  LocalContainerEntityManagerFactoryBean:441 - Closing JPA EntityManagerFactory for persistence unit 'ruleEditor.persistence'

日志显示 testException=[null] 但我不知道为什么会这样。我什至尝试使用 JPARepository 提供的 saveAndFlush() 方法但是这样做我得到了错误javax.persistence.TransactionRequiredException: no transaction is in progress 所以如果有人知道我我做错了,可以为我指明正确的方向,我将非常感谢您的帮助。

4

1 回答 1

1

在仔细阅读日志后,我发现了这个警告:

WARN  Ejb3Configuration:1309 - HHH000193: Overriding hibernate.transaction.factory_class is dangerous, this might break the EJB3 specification implementation

这与我在 tests-context.xml 文件中的 jpaPropertyMap bean 中设置的属性有关:

<entry key="hibernate.transaction.factory_class"  value="org.hibernate.transaction.JTATransactionFactory" />

事实证明,一旦我在配置文件中删除了这一行,我就可以看到 hibernate 执行“插入”操作,并且用户实际上是在数据库中创建的。由于我是 spring 和我使用的所有技术的新手,我一定是从某个地方复制了配置而没有真正分析它。因此,我现在可以在项目中使用 spring data、hibernate 和 bitronix 作为事务管理器来运行 junit4 测试。

于 2013-02-13T10:43:06.270 回答