2015-02-19 48 views
8

Tôi đã tạo một ứng dụng Khởi động mùa xuân bằng phiên bản 1.2.0 với spring-boot-starter-data-jpa và tôi đang sử dụng MySQL. Tôi đã định cấu hình các thuộc tính MySQL của mình trong tệp application.properties một cách chính xác.Khởi động mùa xuân + Dữ liệu mùa xuân JPA + Giao dịch không hoạt động đúng cách

Tôi có một JPA Entity đơn giản, Spring liệu JPA Repository và một dịch vụ như sau:

@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);   
     } 
    } 
} 

Tôi có thử nghiệm JUnit sau:

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); 
    } 

} 

Kỳ vọng ở đây là nó không nên chèn bất kỳ bản ghi nào vào bảng PERSON vì nó sẽ ném Ngoại lệ trong khi chèn đối tượng thứ 3. Nhưng nó không được quay trở lại, 2 hồ sơ đầu tiên được đưa vào và cam kết.

Sau đó, tôi đã nghĩ nhanh chóng thử với Trình quản lý thực thể JPA.

@PersistenceContext 
private EntityManager em; 

em.save(person); 

Sau đó, tôi nhận được javax.persistence.TransactionRequiredException: Không EntityManager giao dịch có sẵn ngoại lệ.

Sau khi googling đôi khi tôi gặp chủ đề JIRA này https://jira.spring.io/browse/SPR-11923 về cùng một chủ đề.

Sau đó, tôi đã cập nhật phiên bản Spring Boot thành 1.1.2 để nhận phiên bản Spring cũ hơn 4.0.6.

Sau đó, em.save (người) hoạt động như mong đợi và Giao dịch hoạt động tốt (nghĩa là khôi phục tất cả chèn db khi RuntimeException xảy ra).

Nhưng ngay cả với mùa xuân 4.0.5 + mùa xuân dữ liệu JPA 1.6.0 phiên bản giao dịch không hoạt động khi personRepository.save (người) được sử dụng thay vì em.persist (người).

Dường như kho lưu trữ dữ liệu Spring JPA đang thực hiện giao dịch.

Tôi đang thiếu gì? Cách đặt các chú thích dịch vụ @Transactional hoạt động?

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

+0

Đăng lớp ứng dụng của bạn và pom maven. Phiên bản Spring Boot bạn đang sử dụng nên sử dụng Spring 4.1.x và không phải cái gì cũ, nếu có một phiên bản khác trong đó bạn có thể có một số phụ thuộc lạ trong danh sách của bạn. –

+0

Đã thêm pom.xml và Application.java –

+4

Đặt 'PersonService'' public' của bạn cũng như phương thức bạn đang gọi. –

Trả lời

2

Thay đổi giao dịch chú thích để @Transactional (rollbackFor = Exception.class)

+1

Đó là hành vi mặc định của Spring, không cần chỉ định rõ ràng. –

+0

Tôi gặp phải vấn đề tương tự. Vì vậy, tôi giữ lớp ngoại lệ của tôi, Nó giải quyết – Abhinay

+0

Nếu nó được kiểm tra ngoại lệ thì chúng ta cần phải xác định rõ ràng. Nếu nó được bỏ chọn ngoại lệ như RuntimeException hoặc bất kỳ lớp con của nó thì mùa xuân sẽ tự động quay trở lại giao dịch. –

0

Tôi t nêu ra điều này với SpringBoot v1.2.0 và v1.5.2 và tất cả đều hoạt động như mong đợi, bạn không cần phải sử dụng trình quản lý thực thể không phải @Transactional (rollbackFor = Exception.class).

Tôi không thể nhìn thấy phần nào bạn cấu hình sai, tất cả có vẻ tốt đẹp ở cái nhìn đầu tiên, vì vậy tôi chỉ đăng tất cả các cấu hình của tôi như là tài liệu tham khảo với các chú thích cập nhật hơn và H2 nhúng DB bộ nhớ:

application.properties

# Embedded memory database 
spring.jpa.hibernate.ddl-auto=create 
spring.datasource.url=jdbc:h2:~/test;AUTO_SERVER=TRUE 

pom.xml

<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.demo</groupId> 
    <artifactId>jpaDemo</artifactId> 
    <version>1.0-SNAPSHOT</version> 

    <properties> 
     <maven.compiler.source>1.8</maven.compiler.source> 
     <maven.compiler.target>1.8</maven.compiler.target> 
    </properties> 

    <parent> 
     <groupId>org.springframework.boot</groupId> 
     <artifactId>spring-boot-starter-parent</artifactId> 
     <version>1.5.2.RELEASE</version> 
    </parent> 

    <dependencies> 
     <dependency> 
      <groupId>org.springframework.boot</groupId> 
      <artifactId>spring-boot-starter-data-jpa</artifactId> 
     </dependency> 
     <dependency> 
      <groupId>com.h2database</groupId> 
      <artifactId>h2</artifactId> 
     </dependency> 
     <dependency> 
      <groupId>org.springframework.boot</groupId> 
      <artifactId>spring-boot-starter-test</artifactId> 
      <scope>test</scope> 
     </dependency> 
     <dependency> 
      <groupId>junit</groupId> 
      <artifactId>junit</artifactId> 
     </dependency> 
    </dependencies> 
</project> 

Person.java

@Entity 
public class Person 
{ 
    @Id 
    @GeneratedValue(strategy= GenerationType.AUTO) 
    private Integer id; 
    private String name; 

    public Person() {} 
    public Person(String name) {this.name = name;} 
    public Integer getId() {return id;} 
    public void setId(Integer id) {this.id = id;} 
    public String getName() {return name;} 
    public void setName(String name) {this.name = name;} 
} 

PersonRepository.java

@Repository 
public interface PersonRepository extends JpaRepository<Person, Integer> {} 

PersonService.java

@Service 
public class PersonService 
{ 
    @Autowired 
    PersonRepository personRepository; 

    @Transactional 
    public void saveTransactional(List<Person> persons){ 
     savePersons(persons); 
    } 

    public void saveNonTransactional(List<Person> persons){ 
     savePersons(persons); 
    } 

    private void savePersons(List<Person> persons){ 
     for (Person person : persons) { 
      if("xxx".equals(person.getName())){ 
       throw new RuntimeException("boooom!!!"); 
      } 
      personRepository.save(person); 
     } 
    } 
} 

Application.java

0.123.
@SpringBootApplication 
public class Application { 
    public static void main(String[] args) { 
     SpringApplication.run(Application.class, args); 
    } 
} 

PersonServiceTest.java

@RunWith(SpringRunner.class) 
@SpringBootTest 
public class PersonServiceTest { 

    @Autowired 
    PersonService personService; 

    @Autowired 
    PersonRepository personRepository; 

    @Test 
    public void person_table_size_1() { 
     List<Person> persons = getPersons(); 
     try {personService.saveNonTransactional(persons);} 
     catch (RuntimeException e) {} 

     List<Person> personsOnDB = personRepository.findAll(); 
     assertEquals(1, personsOnDB.size()); 
    } 

    @Test 
    public void person_table_size_0() { 
     List<Person> persons = getPersons(); 
     try {personService.saveTransactional(persons);} 
     catch (RuntimeException e) {} 

     List<Person> personsOnDB = personRepository.findAll(); 
     assertEquals(0, personsOnDB.size()); 
    } 

    public List<Person> getPersons(){ 
     List<Person> persons = new ArrayList() {{ 
      add(new Person("aaa")); 
      add(new Person("xxx")); 
      add(new Person("sss")); 
     }}; 

     return persons; 
    } 
} 

* Cơ sở dữ liệu sẽ bị xóa và tái khởi tạo cho mỗi bài kiểm tra để chúng ta luôn luôn xác nhận đối với một quốc gia nổi tiếng

1

Từ comment bởi @ m-deinum :

Làm cho PersonService public của bạn cũng như phương pháp bạn đang gọi.

Điều này dường như đã thực hiện thủ thuật cho một số người dùng. Điều tương tự cũng được bao phủ trong this answer, trích dẫn manual saying:

Khi sử dụng proxy, bạn nên áp dụng các chú thích @Transactional chỉ với các phương pháp với tầm nhìn công cộng. Nếu bạn đã bảo vệ chú thích, các phương thức riêng hoặc gói có thể nhìn thấy bằng chú thích @Transactional, không có lỗi nào được nêu ra, nhưng phương pháp được chú thích không hiển thị các cài đặt giao dịch được định cấu hình . Hãy xem xét việc sử dụng AspectJ (xem bên dưới) nếu bạn cần chú thích các phương pháp phi công cộng.

Các vấn đề liên quan