Spring Data JPA - return Future - spring

I tried to save my entity asynchronous and return future of the result, but I had following problems:
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.Repository;
import org.springframework.scheduling.annotation.Async;
//import org.springframework.stereotype.Repository;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
// I must extend Repository because when I extended JpaRepository an argument
// of save is ambigous when I try save(countryEntity) - compiler shows me
// save (CountryEntity) in AsyncCountryRepository and save (S) in CrudRepository
public interface AsyncCountryRepository extends Repository<CountryEntity, Long> {
// I don't know why I cannot return Future<CountryEntity>
#Async
<S extends CountryEntity> Future<S> save(CountryEntity countryEntity);
// I cannot test it - test is below
#Async
CompletableFuture<CountryEntity> findByCode(String code);
}
#RunWith(SpringRunner.class)
#DataJpaTest
public class CountryRepositoryTest {
#Autowired
private TestEntityManager entityManager;
#Autowired
private AsyncCountryRepository countryRepository;
#Test
public void findByCodeTest() throws Exception {
// given
final CountryEntity countryEntity = new CountryEntity("EN");
final CountryEntity persisted = entityManager.persist(countryEntity);
entityManager.flush();
// when
final Future<CountryEntity> byCode = countryRepository.findByCode(persisted.getCode());
// then
assertThat(byCode.get())
.isEqualTo(persisted);
}
Test returns Failed and I get expected:<CountryEntity(id=1, code=EN)> but was:<null>
How can I implement my repository as asynchronous repository and how to test it?
Which approach has better performance:
1. return Futures from repository
2. run repository methods as Callable in ExecutorService
?

If you use #Async the repository code runs in a separate thread within it's own transaction. Since your original transaction isn't committed, it won't see the changes (i.e. the inserted entity) hence the null result.
So in order to make it work, you need to commit the first transaction.
Regarding the second question about the performance (Please make separate questions next time): Performance of multithreaded code depends on so many things that really the only useful way to answer it: try it. But I would be surprised if the way how you schedule the execution has more effect on the performance than things like thread pool and connection pool size.

Related

Micronaut-Data JDBC - Multiple Dialects for Test and Production

The Mirconaut docs on JDBC repositories clearly tells us we have to create a test repository to test against another dialect. I think this will be manageable (e.g. Postgres for production and H2 for test).
The problem is I have to repeat my methods (e.g. find()) in the test repository. I have a book repository and a test repository:
#JdbcRepository(dialect = Dialect.POSTGRES)
interface BookRepository extends CrudRepository<Book, Long> {
Optional<Book> find(String title);
}
#JdbcRepository(dialect = Dialect.H2)
#Replaces(bean = BookRepository)
#Requires(env = ["test"])
interface TestBookRepository extends BookRepository {
// Optional<Book> find(String title);
// Required to make the find() method appear in the TestBookRepository
}
To make the find() method available in the TestBookRepository, I had to repeat the method (see commented line above).
Is there a better way to avoid repeating myself? The methods from the CrudRepository interface are available in the TestBookRepository without problems. Why is the find() method not treated the same?
BTW, I don't want to mock the test repository. I want to test the repository 'logic' injected by Micronaut-Data against an SQL database.
This is for Micronaut Data 1.0.0.M5, using Groovy for the source.
To make the find() method available in the TestBookRepository, I had
to repeat the method (see commented line above).
I cannot reproduce that behavior. In order for that to be the case I think the java compiler would need to have a bug in it that caused that.
See the project at https://github.com/jeffbrown/mikehoustonrepository.
https://github.com/jeffbrown/mikehoustonrepository/blob/82b8af568042c762a86cef9965e52fdc61053421/src/main/java/mikehoustonrepository/BookRepository.java
// src/main/java/mikehoustonrepository/BookRepository.java
package mikehoustonrepository;
import io.micronaut.data.jdbc.annotation.JdbcRepository;
import io.micronaut.data.model.query.builder.sql.Dialect;
import io.micronaut.data.repository.CrudRepository;
import java.util.Optional;
#JdbcRepository(dialect = Dialect.POSTGRES)
public interface BookRepository extends CrudRepository<Book, Long> {
Optional<Book> find(String title);
}
https://github.com/jeffbrown/mikehoustonrepository/blob/82b8af568042c762a86cef9965e52fdc61053421/src/test/java/mikehoustonrepository/TestBookRepository.java
// src/test/java/mikehoustonrepository/TestBookRepository.java
package mikehoustonrepository;
import io.micronaut.context.annotation.Replaces;
import io.micronaut.data.jdbc.annotation.JdbcRepository;
import io.micronaut.data.model.query.builder.sql.Dialect;
#JdbcRepository(dialect = Dialect.H2)
#Replaces(BookRepository.class)
public interface TestBookRepository extends BookRepository{}
https://github.com/jeffbrown/mikehoustonrepository/blob/82b8af568042c762a86cef9965e52fdc61053421/src/main/java/mikehoustonrepository/BookController.java
package mikehoustonrepository;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.Post;
import java.util.Optional;
#Controller("/books")
public class BookController {
private final BookRepository bookRepository;
public BookController(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
#Get("/")
public Iterable<Book> index() {
return bookRepository.findAll();
}
#Post("/{title}/{author}")
public Book create(String title, String author) {
return bookRepository.save(new Book(title, author));
}
#Get("/find/{title}")
public Optional<Book> findByTitle(String title) {
return bookRepository.find(title);
}
}
https://github.com/jeffbrown/mikehoustonrepository/blob/82b8af568042c762a86cef9965e52fdc61053421/src/test/java/mikehoustonrepository/BookControllerTest.java
package mikehoustonrepository;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.Post;
import io.micronaut.http.client.annotation.Client;
import io.micronaut.test.annotation.MicronautTest;
import org.junit.jupiter.api.Test;
import javax.inject.Inject;
import java.util.List;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.*;
#MicronautTest
public class BookControllerTest {
#Inject
BookClient bookClient;
#Test
public void testFind() throws Exception {
Optional<Book> book = bookClient.find("The Nature Of Necessity");
assertFalse(book.isPresent());
bookClient.create("The Nature Of Necessity", "Alvin Plantinga");
book = bookClient.find("The Nature Of Necessity");
assertTrue(book.isPresent());
}
}
#Client(value="/", path = "/books")
interface BookClient {
#Post("/{title}/{author}")
Book create(String title, String author);
#Get("/")
List<Book> list();
#Get("/find/{title}")
Optional<Book> find(String title);
}
That test passes.
You can see that a different repository is being used for test (TestBookRepository) that is used for other environments (BookRepository).
I hope that helps.
You can utilise Micronaut environments to create different environment configuration for test and production
and configure respective datasource configuration in application-test.yml
and use that datasource for tests
Micronaut Environments from docs
After some more work, I found another way to solve the original problem. You can define a base interface class that just has the methods you need. Then implement concrete classes for the dialect(s) you need. This allows one type of DB for test and one for production.
interface OrderRepository extends BaseRepository, CrudRepository<Order, UUID> {
#Join(value = "product", type = Join.Type.LEFT_FETCH)
Optional<Order> findById(UUID uuid)
}
#JdbcRepository(dialect = Dialect.H2)
#Requires(env = ["test"])
interface OrderRepositoryH2 extends OrderRepository, CrudRepository<Order, UUID> {
}
#JdbcRepository(dialect = Dialect.POSTGRES)
#Requires(env = ["dev"])
interface OrderRepositoryPostgres extends OrderRepository, CrudRepository<Order, UUID> {
}
No methods are needed in the OrderRepositoryH2 interface. Micronaut-data uses the methods from the parent interface fine. The trick is to not use the #JdbcRepository annotation in the parent interface.
You can create any other dialects needed, but you have to make sure the #Requires annotation results in only one bean for any given mode.
I plan to use H2 for testing, with an option to use the Postgres dialect for special test runs when needed.
Sorry for any confusion on the question and comments.
(I decided to mark this as the answer since it solves the original problem).

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

Why does OpenEntityManagerInViewFilter change #Transactional propagation REQUIRES_NEW behavior?

Using Spring 4.3.12, Spring Data JPA 1.11.8 and Hibernate 5.2.12.
We use the OpenEntityManagerInViewFilter to ensure our entity relationships do not throw LazyInitializationException after an entity has been loaded. Often in our controllers we use a #ModelAttribute annotated method to load an entity by id and make that loaded entity available to a controller's request mapping handler method.
In some cases like auditing we have entity modifications that we want to commit even when some other transaction may error and rollback. Therefore we annotate our audit work with #Transactional(propagation = Propagation.REQUIRES_NEW) to ensure this transaction will commit successfully regardless of any other (if any) transactions which may or may not complete successfully.
What I've seen in practice using the OpenEntityManagerInviewFilter, is that when Propagation.REQUIRES_NEW transactions attempt to commit changes which occurred outside the scope of the new transaction causing work which should always result in successful commits to the database to instead rollback.
Example
Given this Spring Data JPA powered repository (the EmployeeRepository is similarly defined):
import org.springframework.data.jpa.repository.JpaRepository;
public interface MethodAuditRepository extends JpaRepository<MethodAudit,Long> {
}
This service:
#Service
public class MethodAuditorImpl implements MethodAuditor {
private final MethodAuditRepository methodAuditRepository;
public MethodAuditorImpl(MethodAuditRepository methodAuditRepository) {
this.methodAuditRepository = methodAuditRepository;
}
#Override #Transactional(propagation = Propagation.REQUIRES_NEW)
public void auditMethod(String methodName) {
MethodAudit audit = new MethodAudit();
audit.setMethodName(methodName);
audit.setInvocationTime(LocalDateTime.now());
methodAuditRepository.save(audit);
}
}
And this controller:
#Controller
public class StackOverflowQuestionController {
private final EmployeeRepository employeeRepository;
private final MethodAuditor methodAuditor;
public StackOverflowQuestionController(EmployeeRepository employeeRepository, MethodAuditor methodAuditor) {
this.employeeRepository = employeeRepository;
this.methodAuditor = methodAuditor;
}
#ModelAttribute
public Employee loadEmployee(#RequestParam Long id) {
return employeeRepository.findOne(id);
}
#GetMapping("/updateEmployee")
// #Transactional // <-- When uncommented, transactions work as expected (using OpenEntityManagerInViewFilter or not)
public String updateEmployee(#ModelAttribute Employee employee, RedirectAttributes ra) {
// method auditor performs work in new transaction
methodAuditor.auditMethod("updateEmployee"); // <-- at close of this method, employee update occurrs trigging rollback
// No code after this point executes
System.out.println(employee.getPin());
employeeRepository.save(employee);
return "redirect:/";
}
}
When the updateEmployee method is exercised with an invalid pin number updateEmployee?id=1&pin=12345 (pin number is limited in the database to 4 characters), then no audit is inserted into the database.
Why is this? Shouldn't the current transaction be suspended when the MethodAuditor is invoked? Why is the modified employee flushing when this Propagation.REQUIRES_NEW transaction commits?
If I wrap the updateEmployee method in a transaction by annotating it as #Transactional, however, audits will persist as desired. And this will work as expected whether or not the OpenEntityManagerInViewFilter is used.
While your application (server) tries to make two separate transactions you are still using a single EntityManager and single Datasource so at any given time JPA and the database see just one transaction. So if you want those things to be separated you need to setup two Datasources and two EntityManagers

Behaviour of Hibernate merge() when #Transactional method throws an exception

I would like to ask the reasoning of this behaviour as it seems I do not fully understand the differences between persist() and merge() in Hibernate when running into Spring #Transactional methods/classes.
I have the following code which is supposed to rollback the DB operation but it doesn't (the whole class is annotated as #Transactional):
#Override
public MyBean assignNewFoo(Integer id, Integer idNewFoo) {
MyBean bean = myBeanRepository.findOne(id);
bean = myBeanRepository.save(bean);
bean.setNewFoo(
fooManagement.findById(idNewFoo)
);
if (true) throw new RuntimeException();
return bean;
}
The following code does rollback as expected when an exception is thrown:
#Override
public MyBean assignNewFoo(Integer id, Integer idNewFoo) {
MyBean bean = myBeanRepository.findOne(id);
myBeanRepository.save(bean);
bean.setNewFoo(
fooManagement.findById(idNewFoo)
);
if (true) throw new RuntimeException();
return bean;
}
The save() method comes from the class org.springframework.data.jpa.repository.support.SimpleJpaRepository, so its code is:
#Transactional
public <S extends T> S save(S entity) {
if (entityInformation.isNew(entity)) {
em.persist(entity);
return entity;
} else {
return em.merge(entity);
}
}
The entity is an existing one, so I understand it's doing a merge(). As per JPA specification:
The find method (provided it is invoked without a lock or invoked with
LockModeType.NONE) and the getReference method are not required to be
invoked within a transaction context. If an entity manager with
transaction-scoped persistence context is in use, the resulting
entities will be detached; if an entity manager with an extended
persistence context is used, they will be managed.
The merge operation allows for the propagation of state from detached
entities onto persistent entities managed by the entity manager. The
semantics of the merge operation applied to an entity X are as
follows:
If X is a detached entity, the state of X is copied onto a pre-existing managed entity instance X' of the same identity or a new
managed copy X' of X is created.
If X is a new entity instance, a new managed entity instance X' is created and the state of X is copied into the new managed entity
instance X'.
If X is a removed entity instance, an IllegalArgumentException will be thrown by the merge operation (or the transaction commit will
fail).
If X is a managed entity, it is ignored by the merge operation, however, the merge operation is cascaded to entities referenced by
relationships from X if these relationships have been annotated with
the cascade element value cascade=MERGE or cascade=ALL annotation.
For all entities Y referenced by relationships from X having the cascade element value cascade=MERGE or cascade=ALL, Y is merged
recursively as Y'. For all such Y referenced by X, X' is set to
reference Y'. (Note that if X is managed then X is the same object as
X'.)
If X is an entity merged to X', with a reference to another entity Y, where cascade=MERGE or cascade=ALL is not specified, then
navigation of the same association from X' yields a reference to a
managed object Y' with the same persistent identity as Y.
If the returned copy by merge() is supposedly the managed entity, why are changes stored in the DB when I use the detached one? (unless there is an exception. This is the behaviour I want)
Why changes are committed anyway if I modify the new managed entity but an exception is thrown?
EDIT As requested by #alan-hay:
package org.customer.somefoos.service.impl;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.customer.somefoos.entity.MyBean;
import org.customer.somefoos.repository.MyBeanRepository;
import org.customer.somefoos.service.MyBeanManagement;
import org.customer.somefoos.service.FooManagement;
#Service
#Transactional
public class MyBeanManagementImpl implements MyBeanManagement {
#Resource
private MyBeanRepository myBeanRepository;
#Resource
private FooManagement fooManagement;
#Override
public List<MyBean> findAll() {
return myBeanRepository.findAll();
}
#Override
public MyBean findById(Integer id) {
return myBeanRepository.findOne(id);
}
#Override
public void delete(Integer id) {
myBeanRepository.delete(id);
}
#Override
public MyBean save(MyBean myBean) {
return myBeanRepository.save(myBean);
}
#Override
public MyBean assignNewFoo(Integer id, Integer idNewFoo) {
MyBean bean = myBeanRepository.findOne(id);
myBeanRepository.save(bean);
bean.setNewFoo(
fooManagement.findById(idNewFoo)
);
if (true) throw new RuntimeException();
return bean;
}
}
It seems that you misunderstand merge semantics and container-managed transactions behaviour. Your assignNewFoo method is transactional and your 'bean' instance is loaded from the repository. Because of this, 'bean' instance keeps being managed until transaction ends (or until you remove in from a persistence context manually). This means that myBeanRepository.save(bean); call does nothing as 'bean' is already a JPA-managed entity. myBeanRepository.save(bean) == bean will be true as long as saving is performed in same transaction 'findOne' has been issued in. Merge is used to apply changes made to a non-managed instance of an entity to a managed one. This code illustrates the case merge is being used for:
MyBean bean = repo.findOne(id);
MyBean anotherInstance = new MyBean();
anotherInstance.setId(id);
anotherInstance.setNewFoo("100");
MyBean managed = repo.save(anotherInstance);
// And now we take a look:
managed == bean; // => true
anotherInstance == managed; // => false
bean.getNewFoo(); // => "100"
// An anotherInstance is still detached while save() call has
// returned us a managed instance ('bean')
As per JPA Spec entry you reference to: it's inapplicable here. It says about non-transactional searches, but your search is performed within transaction started by assignNewFoo invocation.
From all the stuff written above: two of your code samples provided to demonstrate no-rollback behaviour are, in fact, identical. There are some reasons you may face the issue you complain about:
You're calling assignNewFoo from a #Transactional method and you peform transaction application checks in this outer #Transactional method. Since your propagation level is 'REQUIRED' and RuntimeException is not caught inside assignNewFoo invocation, the transaction will be marked for rollback once assignNewFoo invocation is finished, but actual rollback will be performed upon completion of method your transaction has been propagated from.
If you're 100 % sure you've done everything right, this might be a Spring/Provider/DBMS issue. I was unable to reproduce this bug on latest Spring Boot + Hibernate 4 + HSQLDB, it may worth checking if you're out of options.

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