Optimistic Locking doesn't work - no version increment, no exception

2.6k views Asked by At

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).

1

There are 1 answers

1
user3227576 On BEST ANSWER

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.