How to unit test pessimistic locking with Spring Boot + Spring Data JPA

Viewed 3544

I want to test whether the FooRepository.lock() works, after someone called the lock(), others calling it should be wait.

The following is my best try, it doesn't work. I believe the reason is both entityManger and fooRepository are participating in the same transaction.

How to call the lock() from another transaction? Or any suggestion for unit-testing the pessimistic lock? Thanks!!

FooRepositoryTest:

package com.example.demo;

import java.util.UUID;

import javax.persistence.LockModeType;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@DataJpaTest
public class FooRepositoryTest {

    @Autowired
    private TestEntityManager entityManager;    

    @Autowired
    private FooRepository fooRepository;

    @Test
    public void lockTest() {
        // given
        Foo foo = new Foo();
        foo.setName("foo-name");

        UUID fooId = fooRepository.save(foo).getFooId();
        entityManager.flush();
        entityManager.clear();

        // when
        Foo savedFoo = fooRepository.findOne(fooId);
        fooRepository.lock(savedFoo);

        // then
        // I want something like this to be lock wait,
        // something to confirm the above fooRepository.lock() work
        entityManager.getEntityManager().lock(savedFoo, LockModeType.PESSIMISTIC_WRITE);
    }

}

class Foo:

package com.example.demo;

import java.util.UUID;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class Foo {  
    @Id
    @GeneratedValue
    private UUID fooId;

    private String name;

    public UUID getFooId() {
        return fooId;
    }

    public void setFooId(UUID fooId) {
        this.fooId = fooId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

class FooApplication:

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class FooApplication {

    public static void main(String[] args) {
        SpringApplication.run(FooApplication.class, args);
    }
}

class FooRepository:

package com.example.demo;

import java.util.UUID;

import org.springframework.data.jpa.repository.JpaRepository;

public interface FooRepository extends JpaRepository<Foo, UUID>, FooRepositoryCustom {  
}

class FooRepositoryCustom:

package com.example.demo;

public interface FooRepositoryCustom {  
    public void lock(Foo foo);  
}

class FooRepositoryImpl:

package com.example.demo;

import javax.persistence.EntityManager;
import javax.persistence.LockModeType;

import org.springframework.beans.factory.annotation.Autowired;

public class FooRepositoryImpl implements FooRepositoryCustom {

    @Autowired
    private EntityManager entityManager;

    @Override
    public void lock(Foo foo) {
        entityManager.lock(foo, LockModeType.PESSIMISTIC_WRITE);
    }   
}
2 Answers

Testing pessimistic locking handling with an integration test is important (may I say mandatory?) for two reasons:

  • There is a good chance that the LockTimeout parameter is not correctly configured (by Oracle and PostgreSQL it is by default infinite time) and this could have a dramatic impact on the stability/performance of the system in production;
  • The JPA implementation for pessimistic locking by most RDBMS is very limited because the 6 internal LockTimeout parameters differ; Testing against an in-memory database only could be not enough and would require special considerations.

In the blog post Testing Pessimistic Locking Handling With SpringBoot and JPA you could:

  • Read more about pessimistic locking handling;
  • And you could find GitHub examples for testing against Oracle, PostgreSQL, MySQL and Apache Derby.

The Spring Framework is used only as an example. You could easly adapt the tests for every other framework.

Related