关于Java:JPA事务-CrudRepository #save(List)不会回滚

JPA Transaction - CrudRepository #save(List) not rolled back

我正在将Spring Boot 1.5.9与spring-boot-starter-data-jpa和Camel 2.20.1一起使用。
作为输入,我得到一个带有一系列元素的XML文件,我将其与JAXB解组,然后将它们聚合到元素列表中。

为此,我定义了一个Element类,在其中我为JAXB关联了一个根元素,并将其注释为JPA实体。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Entity
@Table (name="tblElement", schema="comp")
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Element implements Serializable {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    @Column (name="name")
    @XmlElement(required = true)
    private String name;

    public void Element(){}

    //getter and setter methods
    ...
}

因此我的骆驼路线处理了要存储到MySQL数据库(版本:5.1.73)的条目列表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Component
public class CompRoute extends RouteBuilder {

    @Autowired
    private ElementDao elementDao;

    @Override
    public void configure() throws Exception {
        DataFormat jaxbDataFormat = new JaxbDataFormat("com.comp.beans");

        from(file:src/test/resources/input)
                .convertBodyTo(byte[].class,"UTF-8")
                .split().tokenizeXML("element")
                .unmarshal(jaxbDataFormat)
                .setProperty("SplitSize", simple("${header.CamelSplitSize}"))

                .aggregate(constant(true), new ArrayListAggregationStrategy())
                    .completionSize(simple("${property.SplitSize}"))

                .bean(elementDao,"insertElementList");
    }
}

我对JPA和事务管理器不太熟悉,因此我根据以下文档进行了配置:

https://spring.io/guides/gs/accessing-data-jpa/

https://docs.spring.io/spring-data/jpa/docs/1.5.0.RELEASE/reference/html/jpa.repositories.html

application.properties

1
2
3
4
spring.datasource.url=jdbc:mysql://compserver:3306/comp
spring.datasource.username=user
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

JpaConfig.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(basePackageClasses = CompApplication.class)
class JpaConfig {
    @Autowired
    DataSource dataSource;

    @Bean
    public EntityManagerFactory entityManagerFactory() {
        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        vendorAdapter.setGenerateDdl(false);

        LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
        factory.setJpaVendorAdapter(vendorAdapter);

        String entities = ClassUtils.getPackageName(CompApplication.class);
        factory.setPackagesToScan(entities);
        factory.setDataSource(dataSource);
        factory.afterPropertiesSet();

        return factory.getObject();
    }

    @Bean
    @Qualifier (value ="jpaTransactionManager")
    public PlatformTransactionManager transactionManager() {
        JpaTransactionManager txManager = new JpaTransactionManager();
        txManager.setEntityManagerFactory(entityManagerFactory());
        return txManager;
    }
}

CompApplication.java

1
2
3
4
5
6
7
@SpringBootApplication
public class CompApplication {
  public static void main(String[] args) {
          SpringApplication app = new SpringApplication(CompApplication.class);
          app.setBannerMode(Banner.Mode.OFF);
          app.run(args);
  }

} ??

ElementDao.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Service
public class ElementDao {
    @Autowired
    ElementRepository elementRepository;

    @Transactional (transactionManager ="jpaTransactionManager", readOnly = false, propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    public void insertElementList(Exchange exchange) {
        if(exchange.getIn().getBody() instanceof List) {
             List<Element> elements= convertListToElementList(exchange.getIn().getBody(List.class));
             if (elements != null) {
                 elementRepository.save(elements);
             }
        }
    }
}

ElementRepository.java

1
2
3
4
@Repository
public interface ElementRepository extends CrudRepository<Element, Long> {

}

但是我的交易配置无法正常工作。因为如果发生错误,例如在存储第5个元素时,不会回滚整个事务。它不应插入任何元素。但是仍然存储并提交了前4个元素。

我不理解这种行为?如何在数据库存储过程中发生异常时将服务#insertElementList设置为事务性,以便整个操作回滚?

pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
<?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.company</groupId>
     company</artifactId>
     <version>0.0.1</version>
     <parent>
         <groupId>org.springframework.boot</groupId>
         spring-boot-starter-parent</artifactId>
         <version>1.5.9.RELEASE</version>
     </parent>
     <properties>
       <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
       <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
       <java.version>1.8</java.version>
       <camel.version>2.20.1</camel.version>
     </properties>
     <dependencies>
         <dependency>
             <groupId>org.springframework.boot</groupId>
             spring-boot-starter-data-jpa</artifactId>
         </dependency>
         <dependency>
             <groupId>org.apache.camel</groupId>
             camel-spring-boot-starter</artifactId>
             <version>${camel.version}</version>
         </dependency>
         <dependency>
             <groupId>org.apache.camel</groupId>
             camel-jdbc</artifactId>
             <version>${camel.version}</version>
         </dependency>
         <dependency>
             <groupId>mysql</groupId>
             mysql-connector-java</artifactId>
             <version>5.1.45</version>
         </dependency>
         <dependency>
             <groupId>org.apache.camel</groupId>
             camel-jaxb</artifactId>
             <version>2.20.0</version>
         </dependency>    
         <dependency>
             <groupId>org.springframework.boot</groupId>
             spring-boot-starter-artemis</artifactId>
         </dependency>  
         <dependency>
             <groupId>org.apache.camel</groupId>
             camel-jms</artifactId>
             <version>${camel.version}</version>
         </dependency>  
         <dependency>
             <groupId>com.ibm</groupId>
             com.ibm.mq.allclient</artifactId>
             <version>9.0.0.1</version>
         </dependency>
          <dependency>
             <groupId>net.logstash.logback</groupId>
             logstash-logback-encoder</artifactId>
             <version>4.8</version>
         </dependency>    
         <dependency>
             <groupId>ch.qos.logback</groupId>
             logback-core</artifactId>
             <version>1.1.8</version>
         </dependency>    
         <dependency>
             <groupId>org.springframework.boot</groupId>
             spring-boot-starter-test</artifactId>
             <scope>test</scope>
         </dependency>  
         <dependency>
             <groupId>commons-io</groupId>
             commons-io</artifactId>
             <version>2.5</version>
         </dependency>
     </dependencies>
 </project>


我找到了解决方案。问题是数据库而不是我的配置。 MySql表的类型为MyISAM,它不支持事务并回滚。因此,我将表转换为InnoDB,现在它可以工作-当事务失败时,所有表都将回滚。


根据Spring文档。

@Transactional在定义了相同应用程序上下文中的bean上。这意味着,如果在WebApplicationContext中为DispatcherServlet放置注释,则它将仅在控制器中检查@Transactional bean,而不在服务中进行检查。 >

因此,您的保存方法不是事务性的,请在其中包含该软件包
保存方法已定义。