Spring Data JPA transaction manager

1, the basic concept of the four characteristics of the transactions _

 Four properties (ACID) transactions in the database, claiming that if a database operation support services, then the database must have the following four characteristics:

 ⑴ atomicity (Atomicity)

  Atomicity, refers to all operations in the transaction included, either all succeed, or all fail rolled back. Therefore, the operation of the transaction if successful, it must be completely persisted to the database if the operation fails it can not have any impact on the database.

 consistency (Consistency)

  Consistency refers to the transaction must transform the database from one consistent state to another consistent state, that is to say before and after a transaction execution and implementation must be in a consistent state.

  For example: Suppose both user A and user B add up to a total of 10,000 money, no matter how transfers between A and B, turn several accounts, after the end of the transaction the money add up to two users should still be 10000, which namely is transactional consistency.

 ⑶ isolation (Isolation)

  Isolation, when a plurality of users concurrent access to the database, such as when operating with a table, a database for each user transaction open, operation not being disturbed by other transactions, for isolation between a plurality of concurrent transactions.

  That is, to achieve such an effect: for any two concurrent transactions T1 and T2, T1 appears in the transaction, T2 or T1 has ended before the start, or started after the end of T1, so each transaction does not feel there are other matters to performing concurrently.

       Prior to introduction of various isolation levels provided by the database, let's see if the transaction is not considered isolation, the types of problems occur:

       First, the dirty read

  Dirty read transaction refers to a process where the data is read uncommitted transactions in another.

  When a transaction is a data modified several times, but this many times in this transaction changes are not committed, then a concurrent transactions to access the data, it will cause inconsistencies two transactions resulting data. For example: A user 100 yuan transfer to the users B, corresponding to the SQL command is as follows

    update account set money=money+100 where name=’B’;  (此时A通知B)

    update account set money=money - 100 where name=’A’;

  When performing only the first SQL, A view account notification B, B found that indeed the money has been credited into account (which happened at this time the dirty read), but after the second SQL regardless of whether or not to perform, as long as the transaction is not submitted, all operations are rolled back, then when B after viewing the account again you will find the money, it did not turn.

       A second, non-repeatable read

  Non-repeatable read refers to a database of data within a transaction scope has repeatedly query returns a different data values, which is due in the query interval, changed and committed by another transaction.

  For example, a read data transaction T1, T2 and immediately modify the transaction data and the transaction submitted to the database, transaction T1 the data is read again obtained different results, the non-repeatable read transmission.

  Repeating the reading and non-reading difference is dirty, is a dirty read transaction reads dirty data from another uncommitted transaction, and can not repeat the reading of data is read before a transaction commits.

  In some cases, non-repeatable read is not a problem, such as we have repeatedly query a data query of course, to get the final result based. But there may occur in other cases the problem, for example, may be different for the same data A and B in turn queries, A and B can play up ......

       Third, the dummy read (phantom read)

  Magic Reading is a phenomenon that occurs when a transaction non-independent execution. For example, transaction T1 to a data item in a table of all the lines made from "1" to "2" in the operation, but this time the value of the table row is inserted into a data item, the data item and the transaction T2 or "1" and committed to the database. The operations of the transaction T1 users to see if we just modify the data, you will find there is a line not modify, in fact, this line is added from the transaction T2, like hallucinations, like, this is what has happened phantom reads.

  Phantom reads and non-repeatable read are read another transaction has been submitted (this is different to dirty read), the difference is non-repeatable read query data items are the same, but the magic of reading for a group overall data (such as the number of data)

  About database transaction isolation level provides a variety of isolation, mySQL database provides us with four isolation levels:

  ① Serializable (serialization): can prevent dirty reads, non-repeatable read, phantom read occurs.

  ② Repeatable read (Repeatable Read): can prevent dirty reads, unrepeatable reads occurring.

  ③ Read committed (Read Committed): can avoid dirty reads to occur.

  ④ Read uncommitted (read uncommitted): the lowest level, in any case can not be guaranteed.

     More than four isolation levels are Serializable highest level, the lowest Read uncommitted level, of course, the higher the level, the lower the efficiency. Such a level as Serializable, is the way to lock tables (similar to Java multi-thread lock) allows other threads can only wait outside the lock, so usually choose what isolation level should be based on the actual situation. The default in MySQL database isolation level is Repeatable read (repeatable read).

  In the MySQL database, support for the above four isolation levels, the default is Repeatable read (repeatable read); and in the Oracle database, the only support Serializable (serialized) level and Read committed (Read Committed) Both levels , where the default is committed level is Read.

View mysql database transaction isolation level of default

SELECT @@tx_isolation;

 

 persistent (Durability)

  Persistence means that once a transaction is committed, then changes to the data in the database is permanent, commit the transaction will not be lost even in the case of a database system experienced a failure of the operation.

  For example we use JDBC database operations, after the transaction is committed method, the user is prompted transaction is complete, when we complete the program execution until prompted, you can identify and correct the transaction submitted, even if this time the database there is a problem, it must To complete the full implementation of our business, otherwise it will cause us to see the prompt completion of the transaction, but because of the failure of major database without executing a transaction error.


2, spring-data-jpa transaction management

1) default transaction

   Spring Data provides a default transaction mode so that all queries were declared as read-only transactions. Single request process ensures consistency of data.

  For custom method, the transaction need to change the default way SpringData provided, can comment @Transactional statement Repository operations on multiple methods, they should be treated in the same transaction, in accordance with the layered architecture of thought, which part belongs to the business logic layer, therefore, call for the need to achieve more in the Service Repository layer, and a transaction statement on the appropriate method. 

    Repository concept: According to the original author of the presentation, which is the interface between a data link layer and the mapping domain, the domain acts as a collection of objects in memory. Client object to query some entities are combined, and submit them to the Repository. Object Repository can be removed from or added, like the objects in a line Collection object data manipulation, while layer may map the code corresponding to the respective data extracted from the database.

    Conceptually, Repository data is a data storage area to the object and into a collection package provides a set of these operations.

    We often say that can be understood as a broad sense of DAO

2) dao level code

@Modifying
@Query(value="UPDATE hr_employee_contract t SET t.deleteStatus=1 WHERE t.id=?1",nativeQuery = true)
void delete(Long id);

Description: @Modifying comment

① in @Query annotations, written JPQL achieve DELETE and UPDATE operations, you must add @modifying comment to notify the Spring Data This is an UPDATE or DELETE operation.

②UPDATE or DELETE operations require the use of transaction, need to define the Service layer, operating on the method of adding the transaction layer Service.

③ Note JPQL does not support INSERT operations. 

3) service level code

Use @Transactional manually open transaction management

@Transactional
@Override
public void delete(Long id) {
    employeeContractDao.delete(id);
}

@Transactional annotation support 9 setting properties, of which more use of three attributes: readOnly, propagation, isolation. Wherein the propagation of attributes used to enumerate the transaction propagation, isolation to set the transaction isolation level, readOnly read and write transaction control.

 

① readOnly

From this point of time to set the start (time point a) to the end of this process the transaction, the data submitted by other firms, the transaction will not see! (Data others after time a submission will not appear in the query)

Applications:

NO.1, if you execute a single query, it is not necessary to enable transaction support, database support for SQL default read consistency during execution; 
NO.2, if you once execute multiple queries, such as statistical queries, reports, queries, in this scenario, a number of SQL queries must ensure the overall consistency of reading, otherwise, after the previous one SQL query, before Article SQL queries, data changed by another user, the Comprehensive statistical queries will appear read data inconsistent state at this time, should enable transaction support.
[Note that perform multiple queries to a certain statistical information, this time in order to ensure overall consistency of the data, use read-only transactions]

How to set:

For read-only query, you can specify the type of transaction is readonly, ie read-only transactions.
Since the read-only transaction to modify the data does not exist, so the database will provide some optimization tools for read-only transactions, such as Oracle for read-only transaction, rollback does not start, do not record the rollback log.

(1) In JDBC, to specify read-only transactions approach is: connection.setReadOnly (true);

(2) In Hibernate, specify the read-only transaction approach is: session.setFlushMode (FlushMode.NEVER); 
In this case, Hibernate will provide some optimization methods Session respect for read-only transactions

(3) in the Spring Hibernate package, designated as read-only transactions ways: bean configuration file, the attribute prop increase "readOnly"
or a annotation mode @Transactional (readOnly = true)
{if the transaction is marked as read- only , the sET at the Spring Hibernate by Will flush the Session's the mODE to FLUSH_NEVER, 
and by Will transaction to the sET at the JDBC] that set the Read-only read-only transactions in Spring is to use two methods above

 

② propagation

// support the current transaction, if no transaction, we create a new business. Spring default transaction level.

int PROPAGATION_REQUIRED = 0;  

// support the current transaction, if no transaction is executed in a non-transactional way.

int PROPAGATION_SUPPORTS = 1;  

// support the current transaction, if no transaction, throw an exception.

int PROPAGATION_MANDATORY = 2;  

// New Transaction, if the current transaction exists, the current transaction pending. After the implementation of the new transaction, and then activate the current transaction.

int PROPAGATION_REQUIRES_NEW = 3;  

// perform operations to a non-transactional way, if the current transaction exists, put the current transaction pending.

int PROPAGATION_NOT_SUPPORTED = 4;  

// to perform non-transactional way, if the current transaction exists, an exception is thrown.

int PROPAGATION_NEVER = 5;

// If the current transaction exists, it is executed within nested transactions. If no transaction is carried out with PROPAGATION_REQUIRED similar operations.

// When nesting is determined by an external transaction, whether the transaction is a child commit or rollback.

// External transactions using generally try {} catch (Method nested transactions) {} is encoded.

int PROPAGATION_NESTED = 6; 
 

Case Study 1:

@Service
class A{
    @Autowired
    B b;
 
    @Transactional(propagation = Propagation.REQUIRED)    
    void call(){
        try{
            b.call();
        } catch(Exception e){
            //doSomething....
            //不抛异常,则A无法提交
        }
        //doSomething....
    }
}
 
@Service
class B{
    @Transactional(propagation = Propagation.REQUIRED)
    void call(){}
}


A and B share transaction, if B exception. A .. Unused a try..catch capture, AB rollback together.

If B exception. A capture, but did not throw. A can not submit the final, because the transaction B has been set to the rollback-only.

Case Study 2:

@Service
class A{
    @Autowired
    B b;
 
    @Transactional(propagation = Propagation.REQUIRED)    
    void call(){
        try{
            b.call();
        } catch(Exception e){
            throw e; //或者不抛
        }
        //doSomething....
    }
}
 
@Service
class B{
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    void call(){}
}


Performing b.call () A transaction suspension B at this time if the abnormality. A is captured, if an exception is thrown, then AB roll back; if not throw an exception capture A, then A continues not rolled back.

Performing b.call () A pending transaction, then B is normally performed if the abnormality occurs in A. Then B is not rolled back, A rollback.

Case Study 3:

@Service
class A{
    @Autowired
    B b;
 
    @Transactional(propagation = Propagation.REQUIRED)    
    void call(){
        try{
            b.call();
        } catch(Exception e){
            throw e; //或者不抛
        }
        //doSomething....
    }
}
 
@Service
class B{
    @Transactional(propagation = Propagation.NESTED)
    void call(){}
}


Performing b.call () A pending transaction, B and set a new transaction from SavePoint. If B is normally performed, A is abnormal, the rollback AB together.

If B fails exception this time but did not throw if the capture A follow-up A normal execution, then, can be submitted A and B have been rolled back.

If B fails exception is thrown if the capture case A, the rollback AB together.

Above stories, we can conclude that the difference between the first type 1 and type 3 modes, in the first three kinds of nesting mode, you can perform other operations under abnormal and outside the regular filing within, but not so the first one kind of operation.

 

③ isolation 

    @Override
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public void test() {
        User user = userMapper.getOne(1L);
        System.out.println(user.getName());
 
        userMapper.updateUser();
        try {
            Thread.sleep(10000); // 10 s
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("end");
    }

 @Transactional

Defaults

 


3, spring Detailed affairs

    Spring provides a feature-rich support for transaction management.

    Spring transaction management into coded and declarative in two ways. Programmatic transaction refers to a transaction implemented by coding; statement transaction based AOP, and the specific business transaction processing logic decoupling. Statement transaction management logic makes service code is not contaminated, and therefore in practical use declarative transaction with more.

    Declarative transaction in two ways: one is to make a statement related to the business rules configuration file (xml) in the other is based on @Transactional annotation mode . Comments configuration is a popular use, re-introduced here @Transactional annotation-based transaction management.

1)@Transactional 注解管理事务的实现步骤

使用@Transactional 注解管理事务的实现步骤分为两步。

第一步,配置

在 xml 配置类或配置文件中添加如的事务配置信息。除了用配置文件的方式,@EnableTransactionManagement 注解也可以启用事务管理功能。这里以简单的 DataSourceTransactionManager 为例。

1、配置类


package com.myfutech.market.service.provider.config;

import com.myfutech.common.spring.jpa.base.impl.BaseJpaRepositoryImpl;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;
import java.util.Properties;

@Configuration
@ConditionalOnClass(HikariDataSource.class)
@EnableConfigurationProperties(ConnectionPoolProperties.class)
@EnableJpaRepositories(entityManagerFactoryRef = "marketServiceEntityManagerFactory",
		transactionManagerRef = "marketServiceTransactionManager", basePackages="com.myfutech.market.service.provider.dao",
		repositoryBaseClass = BaseJpaRepositoryImpl.class)
public class JpaConfig {
	
	@Value("${marketService.url}")
	private String url;
	@Value("${marketService.username}")
	private String username;
	@Value("${marketService.password}")
	private String password;
	
    @Autowired
    private ConnectionPoolProperties properties;
	

	@Bean("marketServiceTransactionManager")
	PlatformTransactionManager marketServiceTransactionManager() {
		return new JpaTransactionManager(marketServiceEntityManagerFactory().getObject());
	}

	@Bean("marketServiceEntityManagerFactory")
	LocalContainerEntityManagerFactoryBean marketServiceEntityManagerFactory() {

		HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
		vendorAdapter.setGenerateDdl(true);

		LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
		Properties properties = new Properties();
		properties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLDialect");
		properties.setProperty("hibernate.jdbc.batch_size", "20");
		properties.setProperty("current_session_context_class", "jpa");
		properties.setProperty("hibernate.ejb.entitymanager_factory_name", "marketServiceEntityManagerFactory");
		properties.setProperty("hibernate.hbm2ddl.auto", "none");
		
		factoryBean.setJpaProperties(properties);
		factoryBean.setDataSource(marketServiceDataSource());
		factoryBean.setJpaVendorAdapter(vendorAdapter);
		factoryBean.setPackagesToScan("com.myfutech.market.service.provider.model");
		return factoryBean;
	}

	@Bean
	JdbcTemplate initJdbcTemplate(){
		return new JdbcTemplate(marketServiceDataSource());
	}

	@Bean("marketServiceDataSource")
	DataSource marketServiceDataSource() {
		HikariDataSource dataSource = new HikariDataSource();
		dataSource.setUsername(username);
        dataSource.setPassword(password);
        dataSource.setJdbcUrl(url);
        dataSource.setDriverClassName(properties.getDriverClass());
	    dataSource.addDataSourceProperty("cachePrepStmts", properties.isCachePrepStmts());
        dataSource.addDataSourceProperty("prepStmtCacheSize", properties.getPrepStmtCacheSize());
        dataSource.addDataSourceProperty("prepStmtCacheSqlLimit", properties.getPrepStmtCacheSqlLimit());
        dataSource.addDataSourceProperty("useServerPrepStmts", properties.isUseServerPrepStmts());
        dataSource.addDataSourceProperty("useLocalSessionState", properties.isUseLocalSessionState());
        dataSource.addDataSourceProperty("rewriteBatchedStatements", properties.isRewriteBatchedStatements());
        dataSource.addDataSourceProperty("cacheResultSetMetadata", properties.isCacheResultSetMetadata());
        dataSource.addDataSourceProperty("cacheServerConfiguration", properties.isCacheServerConfiguration());
        dataSource.addDataSourceProperty("elideSetAutoCommits", properties.isElideSetAutoCommits());
        dataSource.addDataSourceProperty("maintainTimeStats", properties.isMaintainTimeStats());
        return dataSource;
	}
}

2、或者在 xml 配置中的事务配置信息 

清单1

<tx:annotation-driven />

<bean id="transactionManager"

class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

<property name="dataSource" ref="dataSource" />

</bean>

第二步,添加

将@Transactional 注解添加到合适的方法上,并设置合适的属性信息。@Transactional 注解的属性信息如表 1 展示。

表 1. @Transactional 注解的属性信息

属性名 说明
name 当在配置文件中有多个 TransactionManager , 可以用该属性指定选择哪个事务管理器。
propagation 事务的传播行为,默认值为 REQUIRED。
isolation 事务的隔离度,默认值采用 DEFAULT。
timeout 事务的超时时间,默认值为-1。如果超过该时间限制但事务还没有完成,则自动回滚事务。
read-only 指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。
rollback-for 用于指定能够触发事务回滚的异常类型,如果有多个异常类型需要指定,各类型之间可以通过逗号分隔。
no-rollback- for 抛出 no-rollback-for 指定的异常类型,不回滚事务。

此外,

@Transactional 注解也可添加在类级别上。当把@Transactional 注解放在类级别时,表示所有该类的公共方法都配置相同的事务属性信息。如下面类,EmployeeService 的所有方法都支持事务并且是只读。

当类级别配置了@Transactional,方法级别也配置了@Transactional,应用程序会以方法级别的事务属性信息来管理事务,换言之,方法级别的事务属性信息会覆盖类级别的相关配置信息。

@Transactional 注解的类级别支持

清单 2

@Transactional(propagation= Propagation.SUPPORTS,readOnly=true)
@Service(value ="employeeService")
public class EmployeeService

        到此,您会发觉使用@Transactional 注解管理事务的实现步骤很简单。但是如果对 Spring 中的 @transaction 注解的事务管理理解的不够透彻,就很容易出现错误,比如事务应该回滚(rollback)而没有回滚事务的问题。接下来,将首先分析 Spring 的注解方式的事务实现机制,然后列出相关的注意事项,以最终达到帮助开发人员准确而熟练的使用 Spring 的事务的目的。

2)Spring 的注解方式的事务实现机制

        在应用系统调用声明@Transactional 的目标方法时,Spring Framework 默认使用 AOP 代理,在代码运行时生成一个代理对象,根据@Transactional 的属性配置信息,这个代理对象决定该声明@Transactional 的目标方法是否由拦截器 TransactionInterceptor 来使用拦截,在 TransactionInterceptor 拦截时,会在在目标方法开始执行之前创建并加入事务,并执行目标方法的逻辑, 最后根据执行情况是否出现异常,利用抽象事务管理器(图 2 有相关介绍)AbstractPlatformTransactionManager 操作数据源 DataSource 提交或回滚事务, 如图 1 所示。

                                                               图 1. Spring 事务实现机制

 

        Spring AOP 代理有 CglibAopProxy JdkDynamicAopProxy 两种,图 1 是以 CglibAopProxy 为例,对于 CglibAopProxy,需要调用其内部类的 DynamicAdvisedInterceptor 的 intercept 方法。对于 JdkDynamicAopProxy,需要调用其 invoke 方法。

        正如上文提到的,事务管理的框架是由抽象事务管理器 AbstractPlatformTransactionManager 来提供的,而具体的底层事务处理实现,由 PlatformTransactionManager 的具体实现类来实现,如事务管理器 DataSourceTransactionManager。不同的事务管理器管理不同的数据资源 DataSource,比如 DataSourceTransactionManager 管理 JDBC 的 Connection。

PlatformTransactionManager,AbstractPlatformTransactionManager 及具体实现类关系如图 2 所示。

                                                            图 2. TransactionManager 类结构

 

3)注解方式的事务使用注意事项

    当您对 Spring 的基于注解方式的实现步骤和事务内在实现机制有较好的理解之后,就会更好的使用注解方式的事务管理,避免当系统抛出异常,数据不能回滚的问题。

正确的设置@Transactional 的 propagation 属性

需要注意下面三种 propagation 可以不启动事务。本来期望目标方法进行事务管理,但若是错误的配置这三种 propagation,事务将不会发生回滚。

  1. TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
  2. TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
  3. TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。

正确的设置@Transactional 的 rollbackFor 属性

默认情况下,如果在事务中抛出了未检查异常(继承自 RuntimeException 的异常)或者 Error,则 Spring 将回滚事务;除此之外,Spring 不会回滚事务。

如果在事务中抛出其他类型的异常,并期望 Spring 能够回滚事务,可以指定 rollbackFor。例:

@Transactional(propagation= Propagation.REQUIRED,rollbackFor= MyException.class)

通过分析 Spring 源码可以知道,若在目标方法中抛出的异常是 rollbackFor 指定的异常的子类,事务同样会回滚。

清单 3. RollbackRuleAttribute 的 getDepth 方法

private int getDepth(Class<?> exceptionClass, int depth) {

        if (exceptionClass.getName().contains(this.exceptionName)) {

            // Found it!

            return depth;

}

        // If we've gone as far as we can go and haven't found it...

        if (exceptionClass == Throwable.class) {

            return -1;

}

return getDepth(exceptionClass.getSuperclass(), depth + 1);

}

@Transactional 只能应用到 public 方法才有效

只有@Transactional 注解应用到 public 方法,才能进行事务管理。这是因为在使用 Spring AOP 代理时,Spring 在调用在图 1 中的 TransactionInterceptor 在目标方法执行前后进行拦截之前,DynamicAdvisedInterceptor(CglibAopProxy 的内部类)的的 intercept 方法或 JdkDynamicAopProxy 的 invoke 方法会间接调用 AbstractFallbackTransactionAttributeSource(Spring 通过这个类获取表 1. @Transactional 注解的事务属性配置属性信息)的 computeTransactionAttribute 方法。

清单 4. AbstractFallbackTransactionAttributeSource

protected TransactionAttribute computeTransactionAttribute(Method method,

    Class<?> targetClass) {

        // Don't allow no-public methods as required.

        if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {

return null;}

这个方法会检查目标方法的修饰符是不是 public,若不是 public,就不会获取@Transactional 的属性配置信息,最终会造成不会用 TransactionInterceptor 来拦截该目标方法进行事务管理。

避免 Spring 的 AOP 的自调用问题

在 Spring 的 AOP 代理下,只有目标方法由外部调用,目标方法才由 Spring 生成的代理对象来管理,这会造成自调用问题。若同一类中的其他没有@Transactional 注解的方法内部调用有@Transactional 注解的方法,有@Transactional 注解的方法的事务被忽略,不会发生回滚。见清单 5 举例代码展示。

清单 5.自调用问题举例

    @Service

    public class OrderService {

        private void insert() {
    
        insertOrder();

    }

    @Transactional

    public void insertOrder() {

        //insert log info

        //insertOrder

        //updateAccount

       }

}

insertOrder 尽管有@Transactional 注解,但它被内部方法 insert 调用,事务被忽略,出现异常事务不会发生回滚。

上面的两个问题@Transactional 注解只应用到 public 方法和自调用问题,是由于使用 Spring AOP 代理造成的。为解决这两个问题,使用 AspectJ 取代 Spring AOP 代理。

需要将下面的 AspectJ 信息添加到 xml 配置信息中。

清单 6. AspectJ 的 xml 配置信息

<tx:annotation-driven mode="aspectj" />

<bean id="transactionManager" 
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
</bean>

<bean class="org.springframework.transaction.aspectj.AnnotationTransactionAspect" factory-method="aspectOf">
    <property name="transactionManager" ref="transactionManager" />
</bean>

同时在 Maven 的 pom 文件中加入 spring-aspects 和 aspectjrt 的 dependency 以及 aspectj-maven-plugin。

清单 7. AspectJ 的 pom 配置信息

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>4.3.2.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.8.9</version>
</dependency>
<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>aspectj-maven-plugin</artifactId>
    <version>1.9</version>
    <configuration>
        <showWeaveInfo>true</showWeaveInfo>
        <aspectLibraries>
            <aspectLibrary>
                <groupId>org.springframework</groupId>
                <artifactId>spring-aspects</artifactId>
            </aspectLibrary>
        </aspectLibraries>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>compile</goal>
                <goal>test-compile</goal>
            </goals>
        </execution>
    </executions>
</plugin>

 

Guess you like

Origin blog.csdn.net/jiahao1186/article/details/90604813