Transactions with Spring and JPA – 用Spring和JPA进行事务

最后修改: 2014年 12月 20日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

This tutorial will discuss the right way to configure Spring Transactions, how to use the @Transactional annotation and common pitfalls.

本教程将讨论配置Spring Transactions的正确方法,如何使用@Transactional注解以及常见的陷阱。

For a more in-depth discussion on the core persistence configuration, check out the Spring with JPA tutorial.

有关对核心持久性配置的更深入讨论,请查看Spring with JPA 教程

Basically, there are two distinct ways to configure Transactions, annotations and AOP, each with its own advantages. We’re going to discuss the more common annotation config here.

基本上,有两种不同的方式来配置Transaction,注解和AOP,每一种都有自己的优势。我们将在这里讨论更常见的注释配置。

2. Configure Transactions

2.配置事务

Spring 3.1 introduces the @EnableTransactionManagement annotation that we can use in a @Configuration class to enable transactional support:

Spring 3.1引入了@EnableTransactionManagement注解,我们可以在@Configuration类中使用它来启用事务性支持。

@Configuration
@EnableTransactionManagement
public class PersistenceJPAConfig{

   @Bean
   public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
       //...
   }

   @Bean
   public PlatformTransactionManager transactionManager() {
      JpaTransactionManager transactionManager = new JpaTransactionManager();
      transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
      return transactionManager;
   }
}

However, if we’re using a Spring Boot project and have a spring-data-* or spring-tx dependencies on the classpath, then transaction management will be enabled by default.

然而,如果我们使用的是Spring Boot项目,并且在classpath上有spring-data-*或spring-tx依赖项,那么事务管理将被默认启用

3. Configure Transactions With XML

3.用XML配置事务

For versions before 3.1, or if Java is not an option, here is the XML configuration using annotation-driven and namespace support:

对于3.1之前的版本,或者如果Java不是一个选项,这里是使用注解驱动和命名空间支持的XML配置。

<bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
   <property name="entityManagerFactory" ref="myEmf" />
</bean>
<tx:annotation-driven transaction-manager="txManager" />

4. The @Transactional Annotation

4.@Transactional 注释

With transactions configured, we can now annotate a bean with @Transactional either at the class or method level:

在配置了事务之后,我们现在可以在类或方法级别上用@Transactional来注释Bean。

@Service
@Transactional
public class FooService {
    //...
}

The annotation supports further configuration as well:

该注释也支持进一步配置

  • the Propagation Type of the transaction
  • the Isolation Level of the transaction
  • a Timeout for the operation wrapped by the transaction
  • a readOnly flag – a hint for the persistence provider that the transaction should be read only
  • the Rollback rules for the transaction

Note that by default, rollback happens for runtime, unchecked exceptions only. The checked exception does not trigger a rollback of the transaction. We can, of course, configure this behavior with the rollbackFor and noRollbackFor annotation parameters.

请注意,默认情况下,回滚只发生在运行时、未检查的异常。被检查的异常不会触发事务的回滚。当然,我们可以通过rollbackFornoRollbackFor注解参数来配置这种行为。

5. Potential Pitfalls

5.潜在的陷阱

5.1. Transactions and Proxies

5.1.事务和代理权

At a high level, Spring creates proxies for all the classes annotated with @Transactional, either on the class or on any of the methods. The proxy allows the framework to inject transactional logic before and after the running method, mainly for starting and committing the transaction.

在高层次上,Spring为所有被@Transactional注解的类创建代理,无论是在类上还是在任何方法上。代理允许框架在运行方法前后注入事务逻辑,主要用于启动和提交事务。

What’s important to keep in mind is that, if the transactional bean is implementing an interface, by default the proxy will be a Java Dynamic Proxy. This means that only external method calls that come in through the proxy will be intercepted. Any self-invocation calls will not start any transaction, even if the method has the @Transactional annotation.

需要记住的是,如果事务性Bean实现了一个接口,默认情况下,代理将是一个Java动态代理。这意味着只有通过代理进入的外部方法调用才会被拦截。任何自我调用将不会启动任何事务,即使该方法有@Transactional注解。

Another caveat of using proxies is that only public methods should be annotated with @Transactional. Methods of any other visibilities will simply ignore the annotation silently as these are not proxied.

使用代理的另一个注意事项是,只有公共方法应该用@Transactional.任何其他可见性的方法都会默默地忽略注释,因为这些方法没有被代理。

5.2. Changing the Isolation Level

5.2.改变隔离级别

courseDao.createWithRuntimeException(course);

We can also change the transaction isolation level:

我们还可以改变交易隔离级别。

@Transactional(isolation = Isolation.SERIALIZABLE)

Note that this has actually been introduced in Spring 4.1; if we run the above example before Spring 4.1, it will result in:

请注意,这实际上已经在Spring 4.1中被引入;如果我们在Spring 4.1之前运行上述例子,将导致。

org.springframework.transaction.InvalidIsolationLevelException: Standard JPA does not support custom isolation levels – use a special JpaDialect for your JPA implementation

5.3. Read-Only Transactions

5.3.只读事务

The readOnly flag usually generates confusion, especially when working with JPA. From the Javadoc:

readOnly标志通常会产生混淆,尤其是在与JPA一起工作时。来自Javadoc。

This just serves as a hint for the actual transaction subsystem; it will not necessarily cause failure of write access attempts. A transaction manager which cannot interpret the read-only hint will not throw an exception when asked for a read-only transaction.

The fact is that we can’t be sure that an insert or update won’t occur when the readOnly flag is set. This behavior is vendor-dependent, whereas JPA is vendor agnostic.

事实上,我们无法确定在设置只读标志时不会发生插入或更新。这种行为是依赖于供应商的,而JPA是不依赖于供应商的。

It’s also important to understand that the readOnly flag is only relevant inside a transaction. If an operation occurs outside of a transactional context, the flag is simply ignored. A simple example of that would call a method annotated with:

同样重要的是要理解只读标志只在事务内部相关。如果一个操作发生在事务上下文之外,该标志会被简单地忽略掉。一个简单的例子是,调用一个注解有以下内容的方法。

@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)

From a non-transactional context, a transaction will not be created and the readOnly flag will be ignored.

从一个非交易性的上下文来看,一个交易将不会被创建,只读标志将被忽略。

5.4. Transaction Logging

5.4.事务记录

A helpful method to understand transactional-related issues is fine-tuning logging in the transactional packages. The relevant package in Spring is “org.springframework.transaction”, which should be configured with a logging level of TRACE.

了解交易相关问题的一个有用方法是微调交易包的日志。Spring中的相关包是”org.springframework.transaction”,它应该被配置为TRACE的日志级别。

5.5. Transaction Rollback

5.5 事务回滚

The @Transactional annotation is the metadata that specifies the semantics of the transactions on a method. We have two ways to rollback a transaction: declarative and programmatic.

@Transactional注解是指定方法上事务语义的元数据。我们有两种方法来回滚一个事务:声明式和程序式。

In the declarative approach, we annotate the methods with the @Transactional annotation. The @Transactional annotation makes use of the attributes rollbackFor or rollbackForClassName to rollback the transactions, and the attributes noRollbackFor or noRollbackForClassName to avoid rollback on listed exceptions.

声明式方法中,我们用@Transactional注解来注释方法。@Transactional注解利用属性rollbackForrollbackForClassName来回滚事务,并利用属性noRollbackFornoRollbackForClassName来避免在列出的异常中回滚。

The default rollback behavior in the declarative approach will rollback on runtime exceptions.

声明式方法中的默认回滚行为将在运行时异常时回滚。

Let’s see a simple example using the declarative approach to rollback a transaction for runtime exceptions or errors:

让我们看一个简单的例子,使用声明式方法来回滚一个运行时异常或错误的事务。

@Transactional
public void createCourseDeclarativeWithRuntimeException(Course course) {
    courseDao.create(course);
    throw new DataIntegrityViolationException("Throwing exception for demoing Rollback!!!");
}

Next, we’ll use the declarative approach to rollback a transaction for the listed checked exceptions. The rollback in our example is on SQLException:

接下来,我们将使用声明式的方法来回滚一个列出的被检查的异常的事务。回滚我们的例子中的SQLException

@Transactional(rollbackFor = { SQLException.class })
public void createCourseDeclarativeWithCheckedException(Course course) throws SQLException {
    courseDao.create(course);
    throw new SQLException("Throwing exception for demoing rollback");
}

Let’s see a simple use of attribute noRollbackFor in the declarative approach to prevent rollback of the transaction for the listed exception:

让我们看看声明式方法中属性noRollbackFor的简单使用,以防止列出的异常的事务回滚。

@Transactional(noRollbackFor = { SQLException.class })
public void createCourseDeclarativeWithNoRollBack(Course course) throws SQLException {
    courseDao.create(course);
    throw new SQLException("Throwing exception for demoing rollback");
}

In the programmatic approach, we rollback the transactions using TransactionAspectSupport:

编程方法中,我们使用TransactionAspectSupport回滚事务。

public void createCourseDefaultRatingProgramatic(Course course) {
    try {
       courseDao.create(course);
    } catch (Exception e) {
       TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }
}
The declarative rollback strategy should be favored over the programmatic rollback strategy.

6. Conclusion

6.结论

In this article, we covered the basic configuration of transactional semantics using both Java and XML. We also learned how to use @Transactional, and the best practices of a Transactional Strategy.

在这篇文章中,我们介绍了使用Java和XML的交易语义的基本配置。我们还学习了如何使用@Transactional,以及事务性策略的最佳实践。

As always, the code presented in this article is available over on Github.

一如既往,本文介绍的代码可在Github上获得over。