How can i test Optmistic Locking using junit, spring and JPA? - spring

I have my entities with a #version column, daos, and junit test.
How can i induce an optimistic lock exception in the junit test case, to see that it's handled correctly?
I am using spring transaction managamemnt, so this makes it more complicated i think

Open a transaction from a jUnit test method and read one row from a certain table.
Create a new thread and open another database transaction which will read the same row.
Update it, and save it to the database.
Pause the main thread used by the jUnit test method.
Modify the data read at the beginning and try updating the row. As a result an optimistic lock exception should be thrown.

In my current Project we have to handle the OptimisticLockException and wrap it into a coustomized exception. Instead of Spring we are using hibernate. But maybe this way will help you.
For my solution you need an OpenEJB-Container in your Test:
#LocalClient
public class ExampleClassTest {
//its a self written class to bootstrap the open ejb container
private static OpenEjbContainerStarter openEjbStarter;
private static Context context;
#Resource
private UserTransaction userTransaction;
#EJB
private ExampleWithSaveActionFacade facade;
#EJB
private ExampleDAO exampleDataAccessObject;
#BeforeClass
public static void setUpBefore() throws Exception {
openEjbStarter = new OpenEjbContainerStarter();
context = openEjbStarter.startOpenEJB();
}
#AfterClass
public static void shutdown() throws NamingException, SQLException {
openEjbStarter.destroyOpenEJB();
}
#Before
public void before() throws Exception {
context.bind("inject", this);
}
#Test(expected = OptimisticLockException.class)
public void testSaveSomethingWithException(){
//get the first object you will manipulate and change the Version
//current Version is 1
ExampleModel example = exampleDataAccessObject.findById(1L);
example.setSomeData("test");
//get the second object what will cause the exception
//current version is 1
ExampleModel exampleOldObject = exampleDataAccessObject.findById(1L);
// returnValue with the version number 2
ExampleModel returnValue = facade.persistOrUpdateUser(example);
//try to save the object with the version 1
//throws the OptimisticLockException
facade.persistOrUpdateUser(exampleOldObject);
}
}

Related

Сamunda replace behaviour for external tasks in tests

I created simple Camunda spring boot project and also created simple BPMN process with switcher. (5.5 KB)
I used service task with external implementation as a spring beans. I want to write tests for process but I don't want to test how beans works. Because in general I use external implementation for connection to DB and save parameter to context or REST call to internal apps. For example I want skip execute service task(one) and instead set variables for switcher. I tried to use camunda-bpm-assert-scenario for test process and wrote simple test WorkflowTest.
I noticed if I use #MockBean for One.class then Camunda skip delegate execution. If use #Mock then Camunda execute delegate execution.
PS Sorry for bad english
One
#Service
public class One implements JavaDelegate {
private final Random random = new Random();
#Override
public void execute(DelegateExecution execution) throws Exception {
System.out.println("Hello, One!");
execution.setVariable("check", isValue());
}
public boolean isValue() {
return random.nextBoolean();
}
}
WorkflowTest
#SpringBootTest
#RunWith(SpringRunner.class)
#Deployment(resources = "process.bpmn")
public class WorkflowTest extends AbstractProcessEngineRuleTest {
#Mock
private ProcessScenario insuranceApplication;
#MockBean
private One one;
#Before
public void init() {
MockitoAnnotations.initMocks(this);
Mocks.register("one", one);
}
#Test
public void shouldExecuteHappyPath() throws Exception {
// given
when(insuranceApplication.waitsAtServiceTask("Task_generator")).thenReturn(externalTaskDelegate -> {
externalTaskDelegate.complete(withVariables("check", true));
}
);
String processDefinitionKey = "camunda-test-process";
Scenario scenario = Scenario.run(insuranceApplication)
.startByKey(processDefinitionKey) // either just start process by key ...
.execute();
verify(insuranceApplication).hasFinished("end_true");
verify(insuranceApplication, never()).hasStarted("three");
verify(insuranceApplication, atLeastOnce()).hasStarted("two");
assertThat(scenario.instance(insuranceApplication)).variables().containsEntry("check", true);
}
}
I found two solutions:
It's a little hack. If user #MockBean for delegate in a test. The delegate will be skipped but you have trouble with process engine variables.
Create two beans with one qualifier and use profiles for testing and production. I used to default profile for local start and test profile for testing.

JPA - Spanning a transaction over multiple JpaRepository method calls

I'm using SpringBoot 2.x with SpringData-JPA accessing the database via a CrudRepository.
Basically, I would like to call the CrudRepository's methods to update or persist the data. In one use case, I would like to delete older entries from the database (for the brevity of this example assume: delete all entries from the table) before I insert a new element.
In case persisting the new element fails for any reason, the delete operation shall be rolled back.
However, the main problem seems to be that new transactions are opened for every method called from the CrudRepository. Even though, a transaction was opened by the method from the calling service. I couldn't get the repository methods to use the existing transaction.
Getting transaction for [org.example.jpatrans.ChairUpdaterService.updateChairs]
Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.deleteWithinGivenTransaction]
Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.deleteWithinGivenTransaction]
I've tried using different Propagation. (REQUIRED, SUPPORTED, MANDATORY) on different methods (service/repository) to no avail.
Changing the methods #Transactional annoation to #Transactional(propagation = Propagation.NESTED) sounded that this would just do that, but didn't help.
JpaDialect does not support savepoints - check your JPA provider's capabilities
Can I achieve the expected behaviour, not using an EntityManager directly?
I also would like to avoid to having to be using native queries as well.
Is there anything I have overlooked?
For demonstration purposes, I've created a very condensed example.
The complete example can be found at https://gitlab.com/cyc1ingsir/stackoverlow_jpa_transactions
Here are the main (even more simplified) details:
First I've got a very simple entity defined:
#Entity
#Table(name = "chair")
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Chair {
// Not auto generating the id is on purpose
// for later testing with non unique keys
#Id
private int id;
#Column(name = "legs", nullable = false)
private Integer legs;
}
The connection to the database is made via the CrudRepository:
#Repository
public interface ChairRepository extends CrudRepository<Chair, Integer> {
}
This is being called from another bean (main methods here are updateChairs and doUpdate):
#Slf4j
#Service
#AllArgsConstructor
#Transactional
public class ChairUpdater {
ChairRepository repository;
/*
* Initialize the data store with some
* sample data
*/
public void initializeChairs() {
repository.deleteAll();
Chair chair4 = new Chair(1, 4);
Chair chair3 = new Chair(2, 3);
repository.save(chair4);
repository.save(chair3);
}
public void addChair(int id, Integer legCount) {
repository.save(new Chair(id, legCount));
}
/*
* Expected behaviour:
* when saving a given chair fails ->
* deleting all other is rolled back
*/
#Transactional
public void updateChairs(int id, Integer legCount) {
Chair chair = new Chair(id, legCount);
repository.deleteAll();
repository.save(chair);
}
}
The goal, I want to achieve is demonstrated by these two test cases:
#Slf4j
#RunWith(SpringRunner.class)
#DataJpaTest
#Import(ChairUpdater.class)
public class ChairUpdaterTest {
private static final int COUNT_AFTER_ROLLBACK = 3;
#Autowired
private ChairUpdater updater;
#Autowired
private ChairRepository repository;
#Before
public void setup() {
updater.initializeChairs();
}
#Test
public void positiveTest() throws UpdatingException {
updater.updateChairs(3, 10);
}
#Test
public void testRollingBack() {
// Trying to update with an invalid element
// to force rollback
try {
updater.updateChairs(3, null);
} catch (Exception e) {
LOGGER.info("Rolled back?", e);
}
// Adding a valid element after the rollback
// should succeed
updater.addChair(4, 10);
assertEquals(COUNT_AFTER_ROLLBACK, repository.findAll().spliterator().getExactSizeIfKnown());
}
}
Update:
It seems to work, if the repository is not extended from either CrudRepository or JpaRepository but from a plain Repository, definening all needed methods explicitly. For me, that seems to be a workaround rather than beeing a propper solution.
The question it boils down to seems to be: Is it possible to prevent SimpleJpaRepository from opening new transactions for every (predefined) method used from the repository interface? Or, if that is not possible, how to "force" the transaction manager to reuse the transaction, opened in the service to make a complete rollback possible?
Hi I found this documentation that looks will help you:
https://www.logicbig.com/tutorials/spring-framework/spring-data/transactions.html
Next an example take from the previous web site:
#Configuration
**#ComponentScan
#EnableTransactionManagement**
public class AppConfig {
....
}
Then we can use transactions like this:
#Service
public class MyExampleBean{
**#Transactional**
public void saveChanges() {
**repo.save(..);
repo.deleteById(..);**
.....
}
}
Yes this is possible. First alter the #Transactional annotation so that it includes rollBackFor = Exception.class.
/*
* Expected behaviour:
* when saving a given chair fails ->
* deleting all other is rolled back
*/
#Transactional(rollbackFor = Exception.class)
public void updateChairs(int id, Integer legCount) {
Chair chair = new Chair(id, legCount);
repository.deleteAll();
repository.save(chair);
}
This will cause the transaction to roll back for any exception and not just RuntimeException or Error.
Next you must add enableDefaultTransactions = false to #EnableJpaRepositories and put the annotation on one of your configuration classes if you hadn't already done so.
#Configuration
#EnableJpaRepositories(enableDefaultTransactions = false)
public class MyConfig{
}
This will cause all inherited jpa methods to stop creating a transaction by default whenever they're called. If you want custom jpa methods that you've defined yourself to also use the transaction of the calling service method, then you must make sure that you didn't annotate any of these custom methods with #Transactional. Because that would prompt them to start their own transactions as well.
Once you've done this all of the repository methods should be executed using the service method transaction only. You can test this by creating and using a custom update method that is annotated with #Modifying. For more on testing please see my answer in this SO thread. Spring opens a new transaction for each JpaRepository method that is called within an #Transactional annotated method

Spring boot #Transactional doesn't rollback

I am using Spring boot application, on that i am trying to achieve Transactional management. But Spring doesn't rollback the data which saved in same method.
Code base: https://github.com/vinothr/spring-boot-transactional-example
Can any one help me?
This is my repository class for 'Test' entity.
#Repository
public interface TestRepository extends CrudRepository<com.example.demo.Test, Long> {
}
I have created one end-point which used to save the data to 'Test' entity. After save happen, I thrown RunTimeException, but it is not rollbacking the saved value
#GetMapping("/test")
#Transactional
public void create() {
System.out.println("test");
final Test p = createTest();
testRepository.save(p);
final Test p1 = createTest();
testRepository.save(p1);
throw new RuntimeException();
}
It works fine after I changed into 'InnoDB' engine because I was using 'MyISAM' engine which doesn't support transaction.
ALTER TABLE my_table ENGINE = InnoDB;
Try indicate #Transactional(rollbackFor = RuntimeException.class)

Test jdbcTemplate.batchUpdate with rollback by default

I'm trying to test my dao, that uses jdbcTemplate.batchUpdate method under the hood.
My tests are run against real datasource and all methods are performed in transactions marked as rollback-only (any changes are rolled back after the test). Test transactions are managed by PlatformTransactionManager.
The issue here, is that jdbcTemplate.batchUpdate seems to be executed in separate transaction started by DataSourceTransactionManager and thus, I can't see changes made by jdbcTemplate.
My test:
#Transactional
#TransactionConfiguration(transactionManager = "txManager", defaultRollback = true)
#RunWith(SpringJUnit4ClassRunner.class)
public abstract class AbstractDbUnitTest
...
#Test
public void removeSpecific() {
myDao.removeSpecific(myDao.findAllAliasedItems());
Assert.assertEquals(0, myDao.findAllAliasedItems().size());
}
Dao
#Override
public void removeSpecific(final List<? extends Item> items) {
jdbcTemplate.batchUpdate("delete from ITEM where item_type = ? and item_id = ?", new BatchPreparedStatementSetter() {
#Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
ps.setString(1, items.get(i).getType().name());
ps.setString(2, items.get(i).getId(););
}
#Override
public int getBatchSize() {
return items.size();
}
});
}
Is there any way to test batchUpdate method item without actually altering the data?
Thanks in advance.
I'm sorry for misleading you all. The issue was not caused by nested transaction.
This question is a result of my total misunderstanding of the hierarchy of transactions-related classes in Java and how batch statements in JDBC implemented.
Test was failing because subsequent calls to
myDao.findAllAliasedItems()
were cached. Spring JDBC template worked just fine and as one might expect.

TransactionTemplate vs #Transactional(propagation = Propagation.REQUIRES_NEW)

could someone please explain why the first unit test class works whereas the second test class fails with a lock wait timeout error?
First test class:
public class Test1 extends AbstractTransactionalJUnit4SpringContextTests {
#Before
public void setUp() {
// do stuff through hibernate to populate database with test data
}
#Test
#Transactional(propagation = Propagation.NEVER)
public void testDeleteObject() {
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
#Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
try {
// execute sql that deletes data populated in setUp() [i.e. this will require locks on the objects].
}
});
}
}
Second Test class [Get a lock wait timeout error]:
public class Test2 extends AbstractTransactionalJUnit4SpringContextTests {
#Before
public void setUp() {
// do stuff through hibernate to populate database with test data
}
#Test
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void testObject() {
// execute sql that deletes data populated in setUp() [i.e. this will require locks on the objects].
}
}
I understand that the second test class fails because the two transaction are fighting for the same locks but neither can give up the locks due to its in_progress transaction state. What I'm confused about is why the first test class succeeds in executing the sql. I'm probably understanding this wrong, but doesn't a new transaction also gets created when the transactionTemplate executes the transaction callback? In that case, shouldn't the same thing happen (lock wait timeout)?
TransactionTemplate does not create a new transaction by default. The default propagation behaviour is PROPAGATION_REQUIRED. In your first case there are no locking issues, because setup and deletion are done in the same transaction.
You can see when new transactions are created or existing transactions reused by setting log level to DEBUG for class org.springframework.orm.jpa.JpaTransactionManager (or even for the whole org.springframework package).
Javadoc for propagation behaviour
A deadlock occurs only if there are two or more connections accessing the same data. In case of test case annotated with propagation NEVER you've got only one transaction, one created by TransactionTemplate.
The second case is a bit fuzzy to me. An exception means there are two concurrent connections/transactions - one for setUp and one for testObject. Propagation REQUIRES_NEW indeed enforces another connection even if there is one detected but I would expect setUp to be launched within this transaction as well. You may try to get rid of #Transactional on testObject. AbstractTransactionalJUnit4SpringContextTests is annotated with #Transactional itself with default propagation which is REQUIRED I believe.

Resources