I recently added the JpaTransactionManager to my Config and tried using Spring transactions. It seems to work correctly, except for one test which I have added below.
Why am I not getting my expected IllegalStateException
? I expect this to fail, because CascadeType
is set to NONE
on the parentCategory
field.
If I add em.flush();
inside my repository class, I do get the expected exception.
As I understand it, adding @Transaction
in my tests only adds the begin, commit and rollback transaction methods. So it should fail at the commit method, but it doesnt.
According to the Hibernate User Guide the hibernate.transaction.flush_before_completion
is false by default. Is this the reason why I don't get my expected exception?
There is a similair post where a user states that the commit will cause a flush. This is also the reason why I decided to ask this question.
This is my entity
@Entity
@Table(name = "category")
public class Category {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "category_id", nullable = false, insertable = false, updatable = false)
private Long id;
@Column(name = "category", nullable = false, unique = true)
private String categoryName;
@ManyToOne // Default is CascadeType.NONE
@JoinColumn(name = "parent_category_id", referencedColumnName = "category_id")
private Category parentCategory;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "parentCategory")
private List<Category> childCategories = new ArrayList<>();
public Category(String categoryName, Category parentCategory) {
this.categoryName = categoryName;
this.parentCategory = parentCategory;
}
// default constructor, getters and setters ...
}
This is my repository
@Repository
public class CategoryDaoImpl {
@PersistenceContext protected EntityManager em;
@Override
public E persist(E entity) {
em.persist(entity);
// em.flush(); // Why doesnt Spring transactions automatically add the em.flush() inside the transaction?
return entity;
}
}
This is my test
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = DbConfig.class)
@Transactional
public class CategoryDaoTest {
@Autowired private CategoryDao categoryDao;
@Test(expected = IllegalStateException.class)
public void createTwoCategoriesPersistChildAndGetIllegalStateException() {
Category childCategory = new Category("firstChild", new Category("rootCategory", null));
categoryDao.persist(childCategory);
}
}
This is my config
@Configuration
@ComponentScan(basePackages = "nl.yoshuan.pricecomparer")
@EnableTransactionManagement
public class TestConfig {
@Bean
public DataSource dataSource() {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
return builder.setType(EmbeddedDatabaseType.HSQL).build();
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
factory.setPackagesToScan("nl.yoshuan.pricecomparer.entities");
factory.setDataSource(dataSource());
Map<String, Object> jpaProperties = new HashMap<>();
jpaProperties.put("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");
jpaProperties.put("hibernate.show_sql", true);
jpaProperties.put("hibernate.format_sql", true);
jpaProperties.put("hibernate.use_sql_comments", true);
jpaProperties.put("hibernate.hbm2ddl.auto", "create-drop");
factory.setJpaPropertyMap(jpaProperties);
return factory;
}
@Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory().getObject());
return txManager;
}
}
This is my console output
2017-05-29 15:43:14 INFO TransactionContext:101 - Began transaction (1) for test context [DefaultTestContext@971e903 testClass = CategoryDaoUTest, testInstance = nl.yoshuan.pricecomparer.dao.CategoryDaoUTest@2b2f5fcf, testMethod = addTwoCategoriesPersistChildAndGetIllegalStateException@CategoryDaoUTest, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@108531c2 testClass = CategoryDaoUTest, locations = '{}', classes = '{class nl.yoshuan.pricecomparer.config.TestConfig}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextCustomizers = set[[empty]], contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]]; transaction manager [org.springframework.orm.jpa.JpaTransactionManager@7661b5a]; rollback [true]
Hibernate:
/* insert nl.yoshuan.pricecomparer.entities.Category
*/ insert
into
category
(category_id, category, parent_category_id)
values
(default, ?, ?)
2017-05-29 15:43:14 INFO TransactionContext:136 - Rolled back transaction for test context [DefaultTestContext@971e903 testClass = CategoryDaoUTest, testInstance = nl.yoshuan.pricecomparer.dao.CategoryDaoUTest@2b2f5fcf, testMethod = addTwoCategoriesPersistChildAndGetIllegalStateException@CategoryDaoUTest, testException = java.lang.AssertionError: Expected exception: java.lang.IllegalStateException, mergedContextConfiguration = [MergedContextConfiguration@108531c2 testClass = CategoryDaoUTest, locations = '{}', classes = '{class nl.yoshuan.pricecomparer.config.TestConfig}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextCustomizers = set[[empty]], contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]].
2017-05-29 15:43:14 INFO GenericApplicationContext:987 - Closing org.springframework.context.support.GenericApplicationContext@49206065: startup date [Mon May 29 15:43:14 CEST 2017]; root of context hierarchy
2017-05-29 15:43:14 INFO LocalContainerEntityManagerFactoryBean:548 - Closing JPA EntityManagerFactory for persistence unit 'default'
2017-05-29 15:43:14 INFO SchemaDropperImpl$DelayedDropActionImpl:524 - HHH000477: Starting delayed drop of schema as part of SessionFactory shut-down'
Hibernate:
alter table category
drop constraint FKs2ride9gvilxy2tcuv7witnxc
Hibernate:
drop table category if exists
2017-05-29 15:43:14 INFO EmbeddedDatabaseFactory:217 - Shutting down embedded database: url='jdbc:hsqldb:mem:testdb'
java.lang.AssertionError: Expected exception: java.lang.IllegalStateException
at org.junit.internal.runners.statements.ExpectException.evaluate(ExpectException.java:32)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:237)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)