I'am using org.springframework.data.repository.CrudRepository to save my entities.
Here is my entity:
@Entity
@Table (name="ThirdClass")
public abstract class ThirdClassBase
extends BaseDomainObject
implements Serializable, Cloneable{
/**
* Default serialID to prevent warning
*/
private static final long serialVersionUID = 1L;
/*
* A T T R I B U T E S
*/
@Id
private String pk;
@Version
private Integer version;
...
}
Here my spring-config:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd"
default-autowire="byName">
<!-- only components from this package can be wired by spring -->
<context:component-scan base-package="test.envmuster.*" />
<!-- Directory to scan for repository classes -->
<jpa:repositories base-package="test.domain.repository" />
<!-- jdbc.properties => used to put db-connection data to an own property-file -->
<bean id="domainPropertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="locations">
<list>
<value>classpath:jdbc.properties</value>
</list>
</property>
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${db.driver}" />
<property name="url" value="${db.url}" />
<property name="username" value="${db.username}" />
<property name="password" value="${db.password}" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager" >
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" >
<list>
<value>test.domain</value>
</list>
</property>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true" />
<property name="generateDdl" value="true" />
</bean>
</property>
</bean>
</beans>
The test:
@ContextConfiguration(locations = { "classpath:spring-test-config.xml" })
@TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = false)
@Transactional
public class OptimisticLockingTest extends AbstractTransactionalTestNGSpringContextTests {
@Autowired
private ThirdClassRepository thirdRepository;
@BeforeClass
public void setUpData(){
ThirdClass thirdClass = new ThirdClass(3L);
thirdClass.setPk("pk1");
thirdClass.setVersion(30);
thirdClass = thirdRepository.save(thirdClass);
Assert.assertEquals(thirdClass.getVersion(), new Integer(30));
}
@Test
public void optimisticLockingCheck(){
ThirdClass thirdClass = thirdRepository.findByPK("pk1");
Assert.assertEquals(thirdClass.getVersion(), new Integer(30));
thirdClass.setValue(5L);
thirdClass.setVersion(5);
thirdClass = thirdRepository.save(thirdClass);
}
@AfterClass
public void deleteData(){
thirdRepository.delete("pk1");
}
}
The test-result in the console:
Hibernate: select thirdclass0_.pk as pk2_8_3_, thirdclass0_.secondClass_pk as secondCl5_8_3_, thirdclass0_.value as value3_8_3_, thirdclass0_.version as version4_8_3_, secondclas1_.pk as pk2_2_0_, secondclas1_.id as id3_2_0_, secondclas1_.thirdClass_pk as thirdCla5_2_0_, secondclas1_.version as version4_2_0_, secondclas1_.wayone_pk as wayone_p6_2_0_, fourthclas2_.SecondClass_pk as SecondCl1_2_5_, fourthclas3_.pk as fourthCl2_3_5_, fourthclas3_.pk as pk2_0_1_, fourthclas3_.prop as prop3_0_1_, fourthclas3_.secondClass_pk as secondCl5_0_1_, fourthclas3_.version as version4_0_1_, thirdclass4_.pk as pk2_8_2_, thirdclass4_.secondClass_pk as secondCl5_8_2_, thirdclass4_.value as value3_8_2_, thirdclass4_.version as version4_8_2_ from ThirdClass thirdclass0_ left outer join SecondClass secondclas1_ on thirdclass0_.secondClass_pk=secondclas1_.pk left outer join SecondClass_FourthClass fourthclas2_ on secondclas1_.pk=fourthclas2_.SecondClass_pk left outer join FourthClass fourthclas3_ on fourthclas2_.fourthClass_pk=fourthclas3_.pk left outer join ThirdClass thirdclass4_ on secondclas1_.thirdClass_pk=thirdclass4_.pk where thirdclass0_.pk=? and thirdclass0_.DTYPE='ThirdClass'
Hibernate: insert into ThirdClass (secondClass_pk, value, version, DTYPE, pk) values (?, ?, ?, 'ThirdClass', ?)
Hibernate: select thirdclass0_.pk as pk2_8_3_, thirdclass0_.secondClass_pk as secondCl5_8_3_, thirdclass0_.value as value3_8_3_, thirdclass0_.version as version4_8_3_, secondclas1_.pk as pk2_2_0_, secondclas1_.id as id3_2_0_, secondclas1_.thirdClass_pk as thirdCla5_2_0_, secondclas1_.version as version4_2_0_, secondclas1_.wayone_pk as wayone_p6_2_0_, thirdclass2_.pk as pk2_8_1_, thirdclass2_.secondClass_pk as secondCl5_8_1_, thirdclass2_.value as value3_8_1_, thirdclass2_.version as version4_8_1_, myfirstcla3_.pk as pk2_4_2_, myfirstcla3_.flag as flag3_4_2_, myfirstcla3_.text as text4_4_2_, myfirstcla3_.version as version5_4_2_, myfirstcla3_.myID as myID6_4_2_, myfirstcla3_.property2 as property7_4_2_, myfirstcla3_.property3 as property8_4_2_ from ThirdClass thirdclass0_ left outer join SecondClass secondclas1_ on thirdclass0_.secondClass_pk=secondclas1_.pk left outer join ThirdClass thirdclass2_ on secondclas1_.thirdClass_pk=thirdclass2_.pk left outer join SuperClass myfirstcla3_ on secondclas1_.wayone_pk=myfirstcla3_.pk where thirdclass0_.pk=? and thirdclass0_.DTYPE='ThirdClass'
Hibernate: update ThirdClass set secondClass_pk=?, value=?, version=? where pk=? and version=?
Hibernate: select thirdclass0_.pk as pk2_8_3_, thirdclass0_.secondClass_pk as secondCl5_8_3_, thirdclass0_.value as value3_8_3_, thirdclass0_.version as version4_8_3_, secondclas1_.pk as pk2_2_0_, secondclas1_.id as id3_2_0_, secondclas1_.thirdClass_pk as thirdCla5_2_0_, secondclas1_.version as version4_2_0_, secondclas1_.wayone_pk as wayone_p6_2_0_, thirdclass2_.pk as pk2_8_1_, thirdclass2_.secondClass_pk as secondCl5_8_1_, thirdclass2_.value as value3_8_1_, thirdclass2_.version as version4_8_1_, myfirstcla3_.pk as pk2_4_2_, myfirstcla3_.flag as flag3_4_2_, myfirstcla3_.text as text4_4_2_, myfirstcla3_.version as version5_4_2_, myfirstcla3_.myID as myID6_4_2_, myfirstcla3_.property2 as property7_4_2_, myfirstcla3_.property3 as property8_4_2_ from ThirdClass thirdclass0_ left outer join SecondClass secondclas1_ on thirdclass0_.secondClass_pk=secondclas1_.pk left outer join ThirdClass thirdclass2_ on secondclas1_.thirdClass_pk=thirdclass2_.pk left outer join SuperClass myfirstcla3_ on secondclas1_.wayone_pk=myfirstcla3_.pk where thirdclass0_.pk=? and thirdclass0_.DTYPE='ThirdClass'
Hibernate: delete from ThirdClass_FourthClass where ThirdClass_pk=?
Hibernate: delete from ThirdClass where pk=? and version=?
PASSED: optimisticLockingCheck
I know i shouldn't set the version-property => but it isn't set automatic or at least not incremented. If i leave it null, it switches to 0 after the first save... that seems to be ok but the second save didn't increment it... so i tried to set the version manual.
Something seems to work because in the console-output i see at the update statement the "where pk =? and version=?" => but why is no exception thrown? It seems to be that the version field is used like a normal column...
Please help me, i have no idea what i did wrong. Thanks a lot in advance. BR
Dependencies (just in case):
<dependencies>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<scope>test</scope>
<version>6.8.7</version>
<exclusions>
<exclusion>
<artifactId>junit</artifactId>
<groupId>junit</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.179</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.0.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.6.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.0.6.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>4.2.8.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.0-api</artifactId>
<version>1.0.1.Final</version>
</dependency>
</dependencies>
Edit: Another for me unexpected behavior... i use CascadeType(REMOVE, MERGE) on OneToOne-Relations. After i have added the @Version annotation it doesn't work any more. I save 2 new entities (1:1) relation by one repository.save - call but the associated relation is not saved (but no exception occured).
Answer to the unexcepected behavior of the CascadeType: Adding of @Version seems to change the saving behavior... because when i changed the CascadeType.MERGE to CascadeType.PERSIST it works again.
Something more seems to change by adding the @Version: I receive all exception only after the transaction is committed. If i remove the @Version i receive the exception at the persist method.