Note that there are some explanatory texts on larger screens.

plurals
  1. POSpring + JUnit4 + JPA/Hibernate - Transaction isolation weirdness?
    primarykey
    data
    text
    <p>I am trying to test my Spring backed JPA/Hibernate DAO using JUnit and H2. I have a <code>@Before</code> annotated initialization method which loads a SQL file up and creates a base data set for each test. Transactions are setup so that after every test, it's rolled back and it starts again. So, this base data set is created for each individual test, then rolled back afterward.</p> <p>This all works great, except I see oddness with unique constraints. I am fairly new to all of these technologies, so maybe I'm just overlooking something. I'd like to be able to test that the unique constraints are working as expected on certain values.</p> <p>First, the <code>init()</code> method:</p> <pre><code>@Before public void init() throws IOException { // Setup default data Query query = em.createNativeQuery(getSqlFromFile()); query.executeUpdate(); } </code></pre> <p>Now a test method demonstrating issue:</p> <pre><code>public void testSaveExistingNonUniqueUsername() { // EXISTING_USER_ID added in @Before annotated init() method User existingUser = testDao.get(EXISTING_USER_ID); // Set to a non-unique username also added in @Before annotated init() method existingUser.setUsername(SECOND_EXISTING_USER); // Save. Here I would expect an exception because of the unique constraint violation. None. Save method simply calls EntityManager.persist() testDao.save(existingUser); Long count = testDao.countByUsername(SECOND_EXISTING_USER); // Count method still returns 1 assertEquals(Long.valueOf(1), count); // Re-load the user User savedUser = testDao.get(EXISTING_USER_ID); // Fails. Username is set to the non-unique value after re-loading, even though the count returned 1, it appears we have two with the same username assertEquals(savedUser.getUsername(), EXISTING_USER); } </code></pre> <p>I'm loading an existing user, changing the username to a non-unique value, and then saving.</p> <p>Here is my User entity:</p> <pre><code>@Entity @Table(name="users") public class User implements DomainObject { @Id @GeneratedValue private Long id; @Column(updatable = false, nullable = false, length=25, unique = true) private String username; @Column(nullable = false, length=50) private String password; @Column(nullable = false) private boolean enabled = true; @ManyToOne(optional = false) @JoinColumn(name="role", referencedColumnName="name") private UserRole role; @OneToOne(optional = false, orphanRemoval = true, cascade=CascadeType.ALL) @JoinColumn(name="contact_details_id") private UserContactDetail contactDetails; // .... getters and setters omitted ..... } </code></pre> <p>You'll note that I also have updatable and nullable set to false for Username, yet it still allows me to make the change.</p> <p>The Test class itself is annotated with the following:</p> <pre><code>@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = { "file:src/test/resources/spring/spring-test-master.xml" }) @TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true) @Transactional(propagation=Propagation.REQUIRED) @TestExecutionListeners( { DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class, TransactionalTestExecutionListener.class }) </code></pre> <p>It uses my production configuration, but I override the data source bean with an H2 datasource. It's completely vanilla, nothing fancy.</p> <p>The test method demonstrates that the data loaded in the <code>init()</code> method is accessible, as the IDs are present and the entities load. However, the unique constraints don't seem to be working within this transaction.</p> <p>However, in the following test, they do:</p> <pre><code>@Test(expected=DataIntegrityViolationException.class) public void testSaveExistingNonUniqueUsername() { // getValidTestUser() just creates a new User() and fills it in with valid data. No ID is set. User firstUser = getValidTestUser(); testDao.save(firstUser); // secondUser will be identical to the first user, but will have a different ID when saved User secondUser = getValidTestUser(); // This DOES throw an exception testDao.save(secondUser); } </code></pre> <p>I'm hoping I am just overlooking something simple. Any help or explanation as to why this might occur would be appreciated.</p> <p>DB Connection configuration:</p> <pre><code># configure h2 data source jdbc.url=jdbc\:h2\:mem\:junitTest;DB_CLOSE_DELAY\=-1 jdbc.user=sa jdbc.pass= jdbc.driver=org.h2.Driver # configure hibernate specific properties hibernate.hbm2ddl.auto=update hibernate.show_sql=true hibernate.cache.provider_class=net.sf.ehcache.hibernate.SingletonEhCacheProvider hibernate.dialect=org.hibernate.dialect.H2Dialect </code></pre> <p>Spring configuration:</p> <pre><code>&lt;bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" p:driverClassName="${jdbc.driver}" p:url="${jdbc.url}" p:username="${jdbc.user}" p:password="${jdbc.pass}"/&gt; </code></pre> <p> </p> <pre><code>&lt;bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" /&gt; &lt;util:properties id="jpaProperties"&gt; &lt;prop key="hibernate.dialect"&gt;${hibernate.dialect}&lt;/prop&gt; &lt;prop key="hibernate.hbm2ddl.auto"&gt;${hibernate.hbm2ddl.auto}&lt;/prop&gt; &lt;prop key="hibernate.show_sql"&gt;${hibernate.show_sql}&lt;/prop&gt; &lt;prop key="hibernate.cache.provider_class"&gt;${hibernate.cache.provider_class}&lt;/prop&gt; &lt;/util:properties&gt; &lt;bean id="defaultLobHandler" class="org.springframework.jdbc.support.lob.DefaultLobHandler" /&gt; &lt;bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" p:dataSource-ref="dataSource" p:persistenceUnitName="PersistenceUnit" p:jpaDialect-ref="jpaDialect" p:jpaVendorAdapter-ref="jpaVendorAdapter" p:jpaProperties-ref="jpaProperties" /&gt; &lt;bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager" p:entityManagerFactory-ref="entityManagerFactory"/&gt; &lt;tx:annotation-driven mode="aspectj" transaction-manager="transactionManager"/&gt; </code></pre> <p>Persistence.xml:</p> <pre><code>&lt;persistence-unit name="PersistenceUnit" transaction-type="RESOURCE_LOCAL"&gt; &lt;provider&gt;org.hibernate.ejb.HibernatePersistence&lt;/provider&gt; &lt;/persistence-unit&gt; </code></pre>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload