Spring事务控制

编程式事务控制相关对象

编程式事务控制三大对象

 PlatFormTransactionManager、TransactionDefinition、TransactionStatus

编程式事务控制含义 

手动代码API的方式控制事务   例如jdbc的connection:

connection.setAutoCommit(false) 、 connection.commit 、 connection.callBack

PlatFormTransactionManager接口:平台事务管理器

PlatFormTransactionManager是接口,不同的Dao层(jdbc,mybatis,hibernate等),有不同的实现类。是spring的平台事务管理器。常用方法有:

Transaction getTransaction(TransactionDefination defination)获取事务的状态信息。

void commit(TransactionStatus status)提交事务

void rollback(TransactionStatus status)回滚事务

TransactionDefinition:事务的定义对象,内部维护事务的相关参数。

int getIsolationLevel()获取事务的隔离级别

int getPropogationBehavior()获取事务的传播行为

int getTimeout()获取超时时间

boolean isReadOnly()是否只读

事务隔离级别

设置隔离级别,可解决事务并发产生的问题,如脏读、不可重复读和虚读。

ISOLATION_DEFAULT

ISOLATION_READ_UNCOMMITTED

ISOLATION_READ_COMMITTED解决脏读

ISOLATION_REPEATABLE_READ解决不可重复读

ISOLATION_SERIALIZABLE全能解决,性能较低

事务传播行为

解决业务方法在调用业务方法时,他们之间事务统一性问题:如:业务层A方法调用业务层B方法时,可能出现事务的统一或重复问题。

1、REQUIRED:如果当前没有事务,就新建一个事务, 如果已经存在一个事务中, 加入到这个事务中。一般的选择(默认值)。举例如:A业务方法调用B业务方法,B业务方法看A业务方法有没有事务,如果A没有,B就新建一个,如果A有,B就加入到这个事务中。

2、SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)。举例如:A业务方法调用B业务方法,B业务方法看A业务方法有没有事务,如果A有事务,B就一起用,如果A没有事务,B也不创建事务了,以非事务方式运行。

3、MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常。举例如:A业务方法调用B业务方法,B业务方法看A业务方法有没有事务,如果A有事务,B就使用当前事务,如果A没有事务,B就抛异常。

4、REQUERS_NEW:新建事务,如果当前在事务中,把当前事务挂起。

注:事务挂起的含义:Spring中事务挂起的含义是,需要新事务时,将现有的connection1保存起来(它还有尚未提交的事务),然后创建connection2,connection2提交、回滚、关闭完毕后,再把connection1取出来,完成提交、回滚、关闭等动作,保存connection1的动作称之为事务挂起。

5、NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

6、NEVER:以非事务方式运行,如果当前存在事务,抛出异常。

7、NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行REQUIRED类似的操作。
8、超时时间:默认值是-1,没有超时限制。可设置超时时间,如果有,以秒为单位进行设置。
9、是否只读:建议查询时设置为只读。查询可以只读,增删改就得写了。

TransactionStatus:事务状态信息对象

boolean hasSavepoint() 是否存储回滚点。

boolean isCompleted() 事务是否完成。

boolean isNewTransaction() 是否是新事物。

boolean isRollbackOnly() 事务是否回滚。

注:状态不需要配置。

PlatFormTransactionManager平台事务管理器+TransactionDefinition事务的定义对象=TransactionStatus事务状态信息对象。

基于XML的声明式事务控制

Spring的声明式事务顾名思义就是采用声明的方式来处理事务。这里所说的声明,就是指在配置文件中声明,用在Spring配置文件中声明式的处理事务来代替代码式的处理事务。

声明式事务处理的作用

事务管理不侵入开发的组件。具体来说,业务逻辑对象就不会意识到正在事务管理之中,事实上也应该如此,因为事务管理是属于系统层面的服务,而不是业务逻辑的一部分,如果想要改变事务管理策划的话,也只需要在定义文件中重新配置即可。

也就是解耦的。事务管理是属于系统层面的,不是业务逻辑的一部分。这样通过配置,业务逻辑和事务管理松耦合了,互不影响,仅在运行期间功能整合一起,是AOP思想的体现。切点是业务逻辑,通知是事务管理。

在不需要事务管理的时候,只要在设定文件上修改一下, 即可移去事务管理服务,无需改变代码重新编译,这样维护起来极其方便。

创建mavenWeb项目,pom文件:
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.5</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.0.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.0.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.2</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.8</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>
    </dependencies>
resource下创建jdbc.properties文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/jdbc
jdbc.username=root
jdbc.password=root123
 resource下applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation=
               "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
                http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
">
    <context:component-scan base-package="com.kdy"/><!--开启组件扫描-->

    <context:property-placeholder location="classpath:jdbc.properties"/><!--引入配置文件,使用el获取-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}"></property>
        <property name="jdbcUrl" value="${jdbc.url}"></property>
        <property name="user" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>
    <!--JdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--配置平台事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/><!--引入数据源,需要它的connection对象事务控制,提交和回滚-->
    </bean>
    <!--通知 事务的增强-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <!--isolation隔离级别、propagation传播行为、timeout超时时间、readonly是否只读-->
            <!--对目标类中的某些名称的方法进行事务处理的增强-->
            <tx:method name="*"/>
            <!--<tx:method name="*" isolation="DEFAULT" propagation="REQUIRED" timeout="-1" read-only="false"/>
            <tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false"/>
            <tx:method name="save" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false"/>
            <tx:method name="findAll" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="true"/>
            <tx:method name="update*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false"/>-->
            <!--以update开头的通配符的写法-->
        </tx:attributes>
    </tx:advice>
    <!--配置事务的aop织入,也可抽取切点点表达式出来后引用-->
    <aop:config>
        <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.kdy.service.impl.*.*(..))"></aop:advisor>
    </aop:config>

</beans>

配置中的tx:attributes: 

它是设置事务的属性信息的,相当于编程式事务控制中的TransactionDefinition事务的定义对象,内部维护事务的相关参数。
methods代表对哪些方法使用事务控制,这里是对所有的方法进行事务控制,这里还能配置事务定义对象的属性,比如隔离级别,超时处理,只读等。

com.kdy.domain中Account
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Account {
    private String name;
    private double money;
}
com.kdy.dao.AccountDaoImpl
@Repository
public class AccountDaoImpl implements AccountDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public void out(String outMan, double money) {
        jdbcTemplate.update("update account set money = money - ? where name = ?",money,outMan);
    }

    @Override
    public void in(String inMan, double money) {
        jdbcTemplate.update("update account set money = money + ? where name = ?",money,inMan);
    }
}
com.kdy.service中AccountServiceImpl
@Service
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;

    @Override
    public void transfer(String outMan, String inMan, double money) {
        /*
        * 传统解决方式,使用事务代码嵌入:
        * 在service业务层使用try-catch将两个事务包裹起来,
        * 在前面开启事务,当无异常就commit提交事务,当有异常就rollback回滚事务。
        * 这种方式比较繁琐,所有涉及到事务的地方都需一个个的处理。
        * */
        //开启事务
        accountDao.out(outMan,money);
        int i = 1/0;//制造异常错误
        accountDao.in(inMan,money);
        //提交事务
    }
}
test目录下com.kdy.test的AccountTest
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AccountTest {

    @Autowired
    private AccountService accountService;

    @Test
    public void test1(){
        accountService.transfer("tom","jerry",500);
    }
}

使用spirng-test就可用不用newClassPathXMLApplicaitonContext的方式创建app对象了。

运行测试用例即可。

基于注解的声明式事务控制

还是上面项目案例,删除applicationContext.xml中的事务增强<tx:advice>和织入<aop:config>
保留平台事务管理器的bean,并加上事务的注解驱动
修改后的applicationContext.xml如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation=
               "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
                http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
">
    <context:component-scan base-package="com.kdy"/><!--开启组件扫描-->

    <context:property-placeholder location="classpath:jdbc.properties"/><!--引入配置文件,使用el获取-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}"></property>
        <property name="jdbcUrl" value="${jdbc.url}"></property>
        <property name="user" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>
    <!--JdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--配置平台事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/><!--引入数据源,需要它的connection对象事务控制,提交和回滚-->
    </bean>

    <!--事务的注解驱动-->
    <tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
在业务类com.kdy.serviceAccountServiceImpl中加上@Transaction注解

注解使用在类上,那么该类下的所有方法都使用同一套注解参数配置。
使用在方法上,不同的方法可以采用不同的事务参数配置。 

@Service
@Transactional(isolation = Isolation.REPEATABLE_READ)//可在类上加上声明事务控制注解
public class AccountServiceImpl implements AccountService {
    @Autowired
    private AccountDao accountDao;

    @Override
    //可在方法上加上声明事务控制注解
    @Transactional(isolation = Isolation.READ_COMMITTED,propagation = Propagation.REQUIRED)
    public void transfer(String outMan, String inMan, double money) {
        /*
        * 传统解决方式,使用事务代码嵌入:
        * 在service业务层使用try-catch将两个事务包裹起来,
        * 在前面开启事务,当无异常就commit提交事务,当有异常就rollback回滚事务。
        * 这种方式比较繁琐,所有涉及到事务的地方都需一个个的处理。
        * */
        //开启事务
        accountDao.out(outMan,money);
        int i = 1/0;//制造异常错误
        accountDao.in(inMan,money);
        //提交事务
    }
}
再次运行测试用例即可。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值