Erledigt!
Hallo Forum,
ich setze gerade einen JUnit Test in Spring auf und sehe ein mir unerklärliches Verhalten. Ich erstelle in der @Before-Methode händisch eine SessionFactory und einen HibernateTransactionManager. Die Transaktion soll in der @After-Methode zurückgerollt werden. Das Zurückrollen funktioniert solange ich auf der sessionFactory.getCurrentSession() nur das saveOrUpdate() aufrufe. Sobald ich nach dem saveOrUpdate() ein sessionFactory.getCurrentSession().createQuery() aufrufe, das mir eines der vorher persistierten Objekte per ID liefert, wird die Transaktion anscheinend nicht mehr zurückgerollt. Die Objekte bleiben trotz eines Aufrufs von transactionManager.rollback(transactionStatus); in der Datenbank stehen.
Warum ist das so? Wird durch das sessionFactory.getCurrentSession().createQuery() eine eigene/unabhängige Transaktion aufgemacht die immer committed wird?
Hier der Code des Testfalls:
Code:
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.Properties;
import javax.sql.DataSource;
import org.hibernate.SessionFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.hibernate3.HibernateTransactionManager;
import org.springframework.orm.hibernate3.LocalSessionFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import de.kraemerit.msl.repository.HibernateArticleRepository;
import de.kraemerit.msl.repository.HibernateUnitRepository;
//from 15-mvc1
//Unit-Test only with Hibernate
public class HibernateArticleTest {
private HibernateUnitRepository unitRepository;
private HibernateArticleRepository articleRepository;
private PlatformTransactionManager transactionManager;
private TransactionStatus transactionStatus;
@Before
public void setUp() throws Exception {
// setup the repository to test
SessionFactory sessionFactory = createTestSessionFactory();
unitRepository = new HibernateUnitRepository(sessionFactory);
articleRepository = new HibernateArticleRepository(sessionFactory);
// begin a transaction
transactionManager = new HibernateTransactionManager(sessionFactory);
transactionStatus = transactionManager.getTransaction(new DefaultTransactionDefinition());
}
@Test
public void testUnit() {
Unit unit = new Unit();
unit.setDescription("mm");
unit.setFactor(1L);
unitRepository.update(unit);
Long id = unit.getId();
unit = new Unit();
unit.setDescription("m");
unit.setFactor(1000L);
unitRepository.update(unit);
System.out.println(transactionStatus.toString());
//getById() commits the transaction, rollback in tearDown doesn't work
//therefore property "hibernate.hbm2ddl.auto -> create"
unit = unitRepository.getById(id);
assertTrue(unit.getDescription().equals("mm"));
}
@After
public void tearDown() throws Exception {
// transactionManager.commit(transactionStatus);
// rollback the transaction to avoid corrupting other tests
transactionManager.rollback(transactionStatus);
}
private SessionFactory createTestSessionFactory() throws Exception {
// create a FactoryBean to help create a Hibernate SessionFactory
LocalSessionFactoryBean factoryBean = new LocalSessionFactoryBean();
factoryBean.setDataSource(createDataSource());
Resource[] mappingLocations = new ClassPathResource[] {
new ClassPathResource("Unit.hbm.xml", HibernateUnitRepository.class),
new ClassPathResource("Article.hbm.xml", HibernateUnitRepository.class)
};
factoryBean.setMappingLocations(mappingLocations);
factoryBean.setHibernateProperties(createHibernateProperties());
// initialize according to the Spring InitializingBean contract
factoryBean.afterPropertiesSet();
// get the created session factory
return (SessionFactory) factoryBean.getObject();
}
private DataSource createDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
// use the MySql JDBC driver
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/xxx?autoReconnect=true");
dataSource.setUsername("xxx");
dataSource.setPassword("xxx");
return dataSource;
}
private Properties createHibernateProperties() {
Properties properties = new Properties();
// turn on formatted SQL logging (very useful to verify Hibernate is
// issuing proper SQL)
properties.setProperty("hibernate.show_sql", "true");
properties.setProperty("hibernate.format_sql", "true");
// properties.setProperty("hibernate.hbm2ddl.auto", "create");
properties.setProperty("hibernate.hbm2ddl.auto", "update");
return properties;
}
}
Hier der Code des Repositories:
Code:
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import de.kraemerit.msl.domain.Unit;
public class HibernateUnitRepository implements UnitRepository {
private SessionFactory sessionFactory;
/**
* Creates a new Hibernate category repository.
* @param sessionFactory the Hibernate session factory
*/
public HibernateUnitRepository(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
@Override
public void update(Unit unit) {
getCurrentSession().saveOrUpdate(unit);
}
@Override
public Unit getById(Long id) {
return (Unit) getCurrentSession().createQuery("Select u From Unit u where u.id = ?").setLong(0, id).uniqueResult();
}
@Override
public Unit getByDescription(String description) {
return (Unit) getCurrentSession().createQuery("Select u From Unit u where u.description = ?").setString(0, description).uniqueResult();
}
/**
* Returns the transactional session
* @return the transactional session
*/
protected Session getCurrentSession() {
return sessionFactory.getCurrentSession();
}
}
Vorab vielen Dank!