A text with your depth understanding of the principles of the transaction Spring

The basic principle Spring affairs

Spring is in fact the nature of the transaction database support for transactions, no transaction database support, spring is unable to provide transaction capabilities. For pure JDBC database operation, you want to use the services, you can follow these steps:

  1. Obtaining a connection Connection con = DriverManager.getConnection ()

  2. Open transaction con.setAutoCommit (true / false);

  3. Perform CRUD

  4. Commit the transaction / rollback transaction con.commit () / con.rollback ();

  5. Close the connection conn.Close ();

After using Spring's transaction management capabilities, we can no longer write code in step 2 and 4, but is done automatically by Spirng. So how Spring open after transaction before we write and CRUD and closing of the session it? To solve this problem, it can be understood Spring's transaction management principles to achieve a whole.

Following is a brief description, the annotation mode as an example:

  1. Open the configuration file annotation drive, on the relevant classes and methods @Transactional identifying annotations.

  2. spring when it starts to parse will generate relevant bean, this time will have to view the classes and methods related notes, and generate a proxy for these classes and methods, and configuration parameters based on the injection @Transaction, thus the agent for us to dispose of matters related to the (open normal commit the transaction, the transaction is rolled back abnormal).

  3. The real transaction commit and rollback database layer is achieved through the binlog or redo log.

Spring's transaction mechanism

All data access technologies have transaction processing, these techniques provides an API to enable the transaction, the transaction is committed to the completion of data manipulation, data or rolled back when the error occurred.

The Spring's transaction mechanism with a unified mechanism to handle transaction processing different data access technologies. Spring's transaction mechanism provides a PlatformTransactionManager interfaces, different transactions using different data access technology interface, as shown in Table.

Data Access Technology and Implementation

Data Access Technology achieve
JDBC DataSourceTransactionManager
JPA JapTransactionManager
Hibernate HibernateTransactionManager
Jdo JdoTransactionManager
Distributed Transaction JtaTransactionManager

Code defines the transaction manager in the program are as follows:

@Bean
public PlatformTransactionManager transactionManager() {

    JpaTransactionManager transactionManager = new JpaTransactionManager();
    transactionManager.setDataSource(dataSource());
    return transactionManager;
}

Declarative transaction

Spring declarative transaction support, users can select the method requires the use of a transaction using annotations, which uses @Transactional annotation on the method shows that this method requires transaction support. This is a realization of AOP-based operation.

@Transactional
public void saveSomething(Long  id, String name) {
    //数据库操作
}

Of particular note here is that this @Transactional comment from org.springframework.transaction.annotation package instead javax.transaction.

Two kinds of AOP proxy implementation:

  • jdk is the agent interface, private methods must not exist in the interface, it will not be intercepted;

  • cglib is a subclass, private method still does not appear in the subclass, it can not be intercepted.

Java dynamic proxy.

In particular the following four steps:

  1. InvocationHandler by implementing the interface to create your own call processor;

  2. To create a dynamic proxy class by specifying a set of interface objects and ClassLoader as Proxy class;

  3. Dynamic proxy class constructor is obtained by reflection, the only parameter type is a call processor interface type;

  4. By creating dynamic proxy class instance constructor object configured to call the handler is passed as a parameter.

GCLIB agent

cglib (Code Generation Library) is a powerful, high-performance, high-quality Code generation libraries. It can be extended at runtime Java classes and implement Java interfaces.

  • cglib encapsulates asm, dynamically generate a new class (subclass) at runtime.

  • cglib for AOP, jdk the proxy must be based interfaces, cglib do not have this limitation.

The principle of distinction:

java dynamic proxy using reflection to generate an anonymous proxy class that implements the interface call InvokeHandler before calling a specific method to process. The dynamic proxy cglib asm using open source packages, class files proxy object classes loaded in, processed by modifying a subclass bytecode.

  1. If the target object implements the interface, it will use JDK dynamic proxy AOP implementations default

  2. If the target object implements the interface, you can force the use of CGLIB achieve AOP

  3. If the target object does not implement the interfaces must be used CGLIB library, spring will automatically between JDK dynamic proxies and convert CGLIB

If the class is not a direct method to go inside agency, this time by maintaining its own instance of a proxy.

@Service
public class PersonServiceImpl implements PersonService {
    @Autowired
    PersonRepository personRepository;

    // 注入自身代理对象,在本类内部方法调用事务的传递性才会生效
    @Autowired
    PersonService selfProxyPersonService;

    /**
     * 测试事务的传递性
     *
     * @param person
     * @return
     */
    @Transactional
    public Person save(Person person) {
        Person p = personRepository.save(person);
        try {
            // 新开事务 独立回滚
            selfProxyPersonService.delete();
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            // 使用当前事务 全部回滚
            selfProxyPersonService.save2(person);
        } catch (Exception e) {
            e.printStackTrace();
        }
        personRepository.save(person);

        return p;
    }

    @Transactional
    public void save2(Person person) {
        personRepository.save(person);
        throw new RuntimeException();
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void delete() {
        personRepository.delete(1L);
        throw new RuntimeException();
    }
}

Spring transaction propagation properties

The spread of so-called spring property transaction is defined when there are multiple simultaneous transactions, the spring should be how to deal with the behavior of these matters. These attributes define TransactionDefinition specific constants explained in the table below:

Constant Name Constant explained
PROPAGATION_REQUIRED Support the current transaction, if no transaction, we create a new business. This is the most common choice, but also the spread of Spring default transactions.
PROPAGATION_REQUIRES_NEW New Transaction, if the current transaction exists, the current transaction pending. The new transaction and the pending transaction has nothing to do, are two separate transactions, the outer transaction is rolled back after the failure, the results can not roll back the transaction execution inner, inner transaction fails throwing an exception, the outer transaction capture, processing may not be rollback
PROPAGATION_SUPPORTS Support the current transaction, if no transaction is executed in a non-transactional way.
PROPAGATION_MANDATORY Support the current transaction, if no transaction, throw an exception.
PROPAGATION_NOT_SUPPORTED Perform operations to a non-transactional way, if the current transaction exists, put the current transaction pending.
PROPAGATION_NEVER Performed in a non-transactional way, if the current transaction exists, an exception is thrown.
PROPAGATION_NESTED If there is an active transaction, run in a nested transaction. If there is no active transaction, press execute REQUIRED property. It uses a separate transaction, the transaction has multiple save points can be rolled back. Rollback internal affairs will not affect the external affairs. Only the onset of DataSourceTransactionManager transaction manager.

Database isolation level

Isolation Levels The value of the isolation level Problems caused
Read-Uncommitted 0 Lead to dirty read
Read-Committed 1 Prevent dirty reads, allows unrepeatable reads and phantom read
Repeatable-Read 2 Prevent dirty reads, non-repeatable read, allowing phantom read
Serializable 3 Reading serialized transaction can only be executed one by one, to avoid dirty reads, non-repeatable read, phantom read. Efficiency slow, use caution

Dirty read: a transaction data were additions and deletions, but did not submit, another transaction can read uncommitted data. At this time, if the first transaction is rolled back, then the second transaction to attend the dirty data.

Non-repeatable read: have been two read operations in a transaction, between a first read operation and a second operation, a further modified transaction data, this time the two read data are inconsistent.

Magic Reading: the first transaction for a range of data make bulk edits, add a second transaction data in this range, it will be lost when the first transaction to modify the new data.

to sum up:

The higher the isolation level, the more it can ensure the integrity and consistency of the data, but the impact on concurrent performance is also greater.

Most of the database default isolation level is Read Commited, such as SqlServer, Oracle

Few database default isolation level: Repeatable Read for example: MySQL InnoDB

Spring isolation level

constant Explanation
ISOLATION_DEFAULT This is a PlatfromTransactionManager default isolation level, use the default database transaction isolation level. Further JDBC isolation level and four, respectively.
ISOLATION_READ_UNCOMMITTED This is the lowest transaction isolation level, it promises another transaction charge and you can see this data uncommitted transactions. This isolation level will have dirty reads, non-repeatable reads and phantom reads.
ISOLATION_READ_COMMITTED After a transaction to ensure that the data submitted in order to be modified to read another transaction. Another transaction can not read the uncommitted transactions.
ISOLATION_REPEATABLE_READ This transaction isolation level prevents dirty reads, non-repeatable reads. But the phantom read may occur.
ISOLATION_SERIALIZABLE This is the most expensive, but the cost of the most reliable transaction isolation level. The transaction is processed as a sequential execution.

Nested Transactions

通过上面的理论知识的铺垫,我们大致知道了数据库事务和spring事务的一些属性和特点,接下来我们通过分析一些嵌套事务的场景,来深入理解spring事务传播的机制。

假设外层事务 Service A 的 Method A() 调用 内层Service B 的 Method B()

PROPAGATION_REQUIRED(spring 默认)

如果ServiceB.methodB() 的事务级别定义为 PROPAGATION_REQUIRED,那么执行 ServiceA.methodA() 的时候spring已经起了事务,这时调用 ServiceB.methodB(),ServiceB.methodB() 看到自己已经运行在 ServiceA.methodA() 的事务内部,就不再起新的事务。

假如 ServiceB.methodB() 运行的时候发现自己没有在事务中,他就会为自己分配一个事务。

这样,在 ServiceA.methodA() 或者在 ServiceB.methodB() 内的任何地方出现异常,事务都会被回滚。

PROPAGATION_REQUIRES_NEW

比如我们设计 ServiceA.methodA() 的事务级别为 PROPAGATION_REQUIRED,ServiceB.methodB() 的事务级别为 PROPAGATION_REQUIRES_NEW。

那么当执行到 ServiceB.methodB() 的时候,ServiceA.methodA() 所在的事务就会挂起,ServiceB.methodB() 会起一个新的事务,等待 ServiceB.methodB() 的事务完成以后,它才继续执行。

他与 PROPAGATION_REQUIRED 的事务区别在于事务的回滚程度了。因为 ServiceB.methodB() 是新起一个事务,那么就是存在两个不同的事务。如果 ServiceB.methodB() 已经提交,那么 ServiceA.methodA() 失败回滚,ServiceB.methodB() 是不会回滚的。如果 ServiceB.methodB() 失败回滚,如果他抛出的异常被 ServiceA.methodA() 捕获,ServiceA.methodA() 事务仍然可能提交(主要看B抛出的异常是不是A会回滚的异常)。

PROPAGATION_SUPPORTS

假设ServiceB.methodB() 的事务级别为 PROPAGATION_SUPPORTS,那么当执行到ServiceB.methodB()时,如果发现ServiceA.methodA()已经开启了一个事务,则加入当前的事务,如果发现ServiceA.methodA()没有开启事务,则自己也不开启事务。这种时候,内部方法的事务性完全依赖于最外层的事务。

PROPAGATION_NESTED

现在的情况就变得比较复杂了, ServiceB.methodB() 的事务属性被配置为 PROPAGATION_NESTED, 此时两者之间又将如何协作呢? ServiceB#methodB 如果 rollback, 那么内部事务(即 ServiceB#methodB) 将回滚到它执行前的 SavePoint 而外部事务(即 ServiceA#methodA) 可以有以下两种处理方式:

a、捕获异常,执行异常分支逻辑

void methodA() {

        try {

            ServiceB.methodB();

        } catch (SomeException) {

            // 执行其他业务, 如 ServiceC.methodC();

        }

    }

这种方式也是嵌套事务最有价值的地方, 它起到了分支执行的效果, 如果 ServiceB.methodB 失败, 那么执行 ServiceC.methodC(), 而 ServiceB.methodB 已经回滚到它执行之前的 SavePoint, 所以不会产生脏数据(相当于此方法从未执行过), 这种特性可以用在某些特殊的业务中, 而 PROPAGATION_REQUIRED 和 PROPAGATION_REQUIRES_NEW 都没有办法做到这一点。

b、 外部事务回滚/提交 代码不做任何修改, 那么如果内部事务(ServiceB#methodB) rollback, 那么首先 ServiceB.methodB 回滚到它执行之前的 SavePoint(在任何情况下都会如此), 外部事务(即 ServiceA#methodA) 将根据具体的配置决定自己是 commit 还是 rollback

另外三种事务传播属性基本用不到,在此不做分析。

总结

对于项目中需要使用到事务的地方,我建议开发者还是使用spring的TransactionCallback接口来实现事务,不要盲目使用spring事务注解,如果一定要使用注解,那么一定要对spring事务的传播机制和隔离级别有个详细的了解,否则很可能发生意想不到的效果。

Spring Boot 对事务的支持

通过org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration类。我们可以看出Spring Boot自动开启了对注解事务的支持 Spring

只读事务(@Transactional(readOnly = true))的一些概念

  • 概念:从这一点设置的时间点开始(时间点a)到这个事务结束的过程中,其他事务所提交的数据,该事务将看不见!(查询中不会出现别人在时间点a之后提交的数据)。

@Transcational(readOnly=true) 这个注解一般会写在业务类上,或者其方法上,用来对其添加事务控制。当括号中添加readOnly=true, 则会告诉底层数据源,这个是一个只读事务,对于JDBC而言,只读事务会有一定的速度优化。

而这样写的话,事务控制的其他配置则采用默认值,事务的隔离级别(isolation) 为DEFAULT,也就是跟随底层数据源的隔离级别,事务的传播行为(propagation)则是REQUIRED,所以还是会有事务存在,一代在代码中抛出RuntimeException,依然会导致事务回滚。

  • 应用场合:

  1. 如果你一次执行单条查询语句,则没有必要启用事务支持,数据库默认支持SQL执行期间的读一致性;

  2. 如果你一次执行多条查询语句,例如统计查询,报表查询,在这种场景下,多条查询SQL必须保证整体的读一致性,否则,在前条SQL查询之后,后条SQL查询之前,数据被其他用户改变,则该次整体的统计查询将会出现读数据不一致的状态,此时,应该启用事务支持。

【注意是一次执行多次查询来统计某些信息,这时为了保证数据整体的一致性,要用只读事务】

References:

  1. http://www.codeceo.com/article/spring-transactions.html

  2. http://www.cnblogs.com/fenglie/articles/4097759.html

  3. https://www.zhihu.com/question/39074428/answer/88581202

  4. http://blog.csdn.net/andyzhaojianhui/article/details/51984157

发布了50 篇原创文章 · 获赞 1706 · 访问量 222万+

Guess you like

Origin blog.csdn.net/zl1zl2zl3/article/details/105372909