During Spring unit test, data written to db but test not seeing the data
I wrote a test case that extends AbstractTransactionalJUnit4SpringContextTests
. The single test case I've written creates an instance of class User and attempts to write it to the database using Hibernate. The test code then uses SimpleJdbcTemplate
to execute a simple select count(*) from the user table to determine if the user was persisted to the database or not. The test always fails though. I was suspect because in the Spring controller I wrote, the ability to save an instance of User to the db is successful.
So I added the Rollback annotation to the unit test and sure enough, the data is written to the database since I can even see it in the appropriate table -- the transaction isn't rolled back when the test case is finished.
Here's my test case:
@ContextConfiguration(locations = {
"classpath:context-daos.xml",
"classpath:context-dataSource.xml",
"classpath:context-hibernate.xml"})
public class UserDaoTest extends AbstractTransactionalJUnit4SpringContextTests {
@Autowired
private UserDao userDao;
@Test
@Rollback(false)
public void teseCreateUser() {
try {
UserModel user = randomUser();
String username = user.getUserName();
long id = userDao.create(user);
String query = "select count(*) from public.usr where usr_name = '%s'";
long count = simpleJdbcTemplate.queryForLong(String.format(query, username));
Assert.assertEquals("User with username should be in the db", 1, count);
}
catch (Exception e) {
e.printStackTrace();
Assert.assertNull("testCreateUser: " + e.getMessage());
}
}
}
I think I was remiss by not adding the configuration files.
context-hibernate.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"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd>
<bean id="namingStrategy" class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">
<property name="staticField">
<value>org.hibernate.cfg.ImprovedNamingStrategy.INSTANCE</value>
</property>
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean" destroy-method="destroy" scope="singleton">
<property name="namingStrategy">
<ref bean="namingStrategy"/>
</property>
<property name="dataSource" ref="dataSource"/>
<property name="mappingResources">
<list>
<value>com/company/model/usr.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.use_sql_comments">true</prop>
<prop key="hibernate.query.substitutions">yes 'Y', no 'N'</prop>
<prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop>
<prop key="hibernate.cache.use_query_cache">true</prop>
<prop key="hibernate.cache.use_minimal_puts">false</prop>
<prop key="hibernate.cache.use_second_level_cache">true</prop>
<prop key="hibernate.current_session_context_class">thread</prop>
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
<property name="nestedTransactionAllowed" value="false" />
</bean>
<bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager">
<ref local="transactionManager"/>
</property>
<property name="transactionAttributes">
<props>
<prop key="create">PROPAGATION_REQUIRED</prop>
<prop key="delete">PROPAGATION_REQUIRED</prop>
<prop key="update">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_SUPPORTS,readOnly</prop>
</props>
</property>
</bean>
</beans>
context-dataSource.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"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="org.postgresql.Driver" />
<property name="jdbcUrl" value="jdbc\:postgresql\://localhost:5432/company_dev" />
<property name="user" value="postgres" />
<property name="password" value="postgres" />
</bean>
</beans>
context-daos.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"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="extendedFinderNamingStrategy" class="com.company.dao.finder.impl.ExtendedFinderNamingStrategy"/>
<bean id="finderIntroductionAdvisor" class="com.company.dao.finder.impl.FinderIntroductionAdvisor"/>
<bean id="abstractDaoTarget" class="com.company.dao.impl.GenericDaoHibernateImpl" abstract="true" depends-on="sessionFactory">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
</property>
<property name="namingStrategy">
<ref bean="extendedFinderNamingStrategy"/>
</property>
</bean>
<bean id="abstractDao" class="org.springframework.aop.framework.ProxyFactoryBean" abstract="true">
<property name="interceptorNames">
<list>
<value>transactionInterceptor</value>
<value>finderIntroductionAdvisor</value>
</list>
</property>
</bean>
<bean id="userDao" parent="abstractDao">
<property name="proxyInterfaces">
<value>com.company.dao.UserDao</value>
</property>
<property name="target">
<bean parent="abstractDaoTarget">
<constructor-arg>
<value>com.company.model.UserModel</value>
</constructor-arg>
</bean>
</property>
</bean>
</beans>
Some of this I've inherited from someone else. I wouldn't have used the proxying that is going on here because I'm not sure it's needed but this is what I'm working with.
Any help much appreciated.
IIRC, Hibernate (and other O/R mappers) delay the database INSERT and UPDATE statements until the transaction commit. The SELECT then does not see the data, as it is not yet written. Try explicitly requesting a session flush.
See also the documentation for an explanation and example: http://docs.jboss.org/hibernate/core/3.3/reference/en/html/objectstate.html#objectstate-flushing
Doesn't transactionInterceptor automatically commits?
http://www.docjar.com/html/api/org/springframework/transaction/interceptor/TransactionInterceptor.java.html
Line 117:
commitTransactionAfterReturning(txInfo);
By default the flush mode should be auto, so there should be an implicit flush when commit is performed. I have the same problem and had to solve it by manually calling flush. That is very unsatisfying because my existing code depends on auto-flush before migrating to Spring transation manager. I wish there is a way to tell org.springframework.orm.hibernate3.HibernateTransactionManager to flush after commit.
精彩评论