I created a Spring boot application using version 1.2.0 with spring-boot-starter-data-jpa and I use MySQL. I configured my MySQL properties correctly in the application.properties file.
I have a simple JPA Entity, Spring Data JPA Repository and a service as follows:
@Entity class Person { @Id @GeneratedValue(strategy=GenerationType.AUTO) private Integer id; private String name; //setters & getters } @Repository public interface PersonRepository extends JpaRepository<Person, Integer>{ } import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service @Transactional class PersonService { @Autowired PersonRepository personRepository; @Transactional void save(List<Person> persons){ for (Person person : persons) { if("xxx".equals(person.getName())){ throw new RuntimeException("boooom!!!"); } personRepository.save(person); } } }
I have the following JUnit test:
import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = Application.class) public class ApplicationTests { @Autowired PersonService personService; @Test public void test_logging() { List<Person> persons = new ArrayList<Person>(); persons.add(new Person(null,"abcd")); persons.add(new Person(null,"xyz")); persons.add(new Person(null,"xxx")); persons.add(new Person(null,"pqr")); personService.save(persons); } }
The expectation here is that it should not insert records into the PERSON table, since it will throw an exception when inserting a third party object. But he does not roll back, the first 2 entries are inserted and fixed.
Then I thought of a quick try using JPA EntityManager.
@PersistenceContext private EntityManager em; em.save(person);
Then I get javax.persistence.TransactionRequiredException: Missing Transactional EntityManager .
After I ever ran into a search, I came across this JIRA thread https://jira.spring.io/browse/SPR-11923 in the same thread.
Then I upgraded the Spring boot version to 1.1.2 to get Spring version older than 4.0.6 .
Then em.save (person) works as expected, and the transaction works fine (meaning that it rolls back all db inserts when a RuntimeException occurs).
But even with Spring 4.0.5 + Spring Data JPA 1.6.0 transactions, transactions do not work when personRepository.save (person) is used instead of em.persist (person) .
It seems that Spring JPA Data Warehouses are committing transactions.
What am I missing? How to annotate @Transactional service?
PS:
Maven pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.sivalabs</groupId> <artifactId>springboot-data-jpa</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <name>springboot-data-jpa</name> <description>Spring Boot Hello World</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.2.0.RELEASE</version> <relativePath /> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <start-class>com.sivalabs.springboot.Application</start-class> <java.version>1.7</java.version> </properties> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> </dependencies> </project>
Application.java
@EnableAutoConfiguration @Configuration @ComponentScan public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }