Domanda

I am starting a project using Spring 3.2.1.RELEASE, Hibernate 4.1.9.FINAL as jpa provider, spring data 1.2.0.RELEASE for repository and bitronix 2.1.3 as transaction manager. I am new to all of these technologies so I'm sorry if I'm missing some huge point.

I am running a simple unit test to create a User object in a database:

@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);
}
}

My test-context.xml used is as follows:

<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>

The Repository implementation is a standard JPARepository:

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

And the service implementation only makes calls to the repository:

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

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

The problem is that when I execute the test, it passes, but no user is created in the database. I thought it might be because of the Rollback behavior so I set the defaultRollback=false in the User test. Here is some information provided by the transaction framework when set to DEBUG that might be relevant:

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'

The logs show a testException=[null] but I have no idea why that would be.. I even tried to use the saveAndFlush() method provided by the JPARepository but doing so I get the error javax.persistence.TransactionRequiredException: no transaction is in progress So if anyone has some idea of what I am doing wrong and can point me in the right direction I would very much appreciate the help.

È stato utile?

Soluzione

After some careful reading of the logs, I found this warning:

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

This was related to a property that I had set in the jpaPropertyMap bean in my tests-context.xml file:

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

It turns out that once I removed this line in the configuration file I could see hibernate performing an "insert" action and the user was actually created in the database. Since I am new to spring and all the technologies I used, I must have copied the configuration from somewhere without really analysing it. So, I am now able to run junit4 tests in a project using spring data, hibernate and bitronix as transaction manager.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top