spring事物-----手写spring的事物框架

Posted qingruihappy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring事物-----手写spring的事物框架相关的知识,希望对你有一定的参考价值。

一,区别声明式事物和编程式事物

    所谓编程式事务指的是通过编码方式实现事务,即类似于JDBC编程实现事务管理。管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。对于编程式事务管理,spring推荐使用TransactionTemplate。

 

    声明式事物其实就是编程式事物+spring的AOP代理,在里面我们是见不到手动的begin commit  和rollback的。

管理建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过基于@Transactional注解的方式),便可以将事务规则应用到业务逻辑中。显然声明式事务管理要优于编程式事务管理,这正是spring倡导的非侵入式的开发方式。

声明式事务管理使业务代码不受污染,一个普通的POJO对象,只要加上注解就可以获得完全的事务支持。和编程式事务相比,声明式事务唯一不足地方是,后者的最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。但是即便有这样的需求,也存在很多变通的方法,比如,可以将需要进行事务管理的代码块独立为方法等等。

 

下面我们通过案例来看看。

二,案例

 2.1,spring事物自带的几种通知

package com.qingruihappy.service;

//user 服务层
public interface UserService {
    public void add();
}
package com.qingruihappy.service.impl;

import org.springframework.stereotype.Service;

import com.qingruihappy.service.UserService;
/**
 * 注意事物出异常的话一定不要吃掉(try cath ,而是要throw往外抛,否则事物是不起作用的。原因我们后面讲)
 * @author qingruihappy
 * @data   2018年11月19日 上午12:21:21
 * @说明:
 */
//user 服务层
@Service
public class UserServiceImpl implements UserService {
    // spring 事务封装呢? aop技术
    public void add() {
      System.out.println("往数据库添加数据.........");
    }

}

 

 

package com.qingruihappy.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

// 切面类
@Component//注入到spring的容器中来
@Aspect//表示是一个切面
public class AopLog {

    // aop 编程里面有几个通知: 前置通知 后置通知 运行通知 异常通知 环绕通知
    @Before("execution(* com.qingruihappy.service.UserService.add(..))")
    public void before() {
        System.out.println("前置通知 在方法之前执行...");
    }

    // 后置通知 在方法运行后执行
    @After("execution(* com.qingruihappy.service.UserService.add(..))")
    public void after() {
        System.out.println("前置通知 在方法之后执行...");
    }

    // 运行通知
    @AfterReturning("execution(* com.qingruihappy.service.UserService.add(..))")
    public void returning() {
        System.out.println("运行通知");
    }

    // 异常通知
    @AfterThrowing("execution(* com.qingruihappy.service.UserService.add(..))")
    public void afterThrowing() {
        System.out.println("异常通知");
    }

    // 环绕通知 在方法之前和之后处理事情
    @Around("execution(* com.qingruihappy.service.UserService.add(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {

        // 调用方法之前执行
        System.out.println("环绕通知 调用方法之前执行");
        proceedingJoinPoint.proceed();// 代理调用方法 注意点: 如果调用方法抛出溢出不会执行后面代码
        // 调用方法之后执行
        System.out.println("环绕通知 调用方法之后执行");
    }

}

 

 

package com.qingruihappy.test;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.qingruihappy.service.UserService;

public class Test001 {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        UserService userService = (UserService) applicationContext.getBean("userServiceImpl");
        userService.add();
    }

}

 

 

 结果:

前置通知 在方法之前执行...
环绕通知 调用方法之前执行
往数据库添加数据.........
前置通知 在方法之后执行...
运行通知
环绕通知 调用方法之后执行

 

2.2,一个不加事务的案例 

<?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:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    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/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
    <context:component-scan
        base-package="com"></context:component-scan>
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy> <!-- 开启事物注解 -->
    <!-- 1. 数据源对象: C3P0连接池 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"></property>
        <property name="user" value="root"></property>
        <property name="password" value="root"></property>
    </bean>


    <!-- 2. JdbcTemplate工具类实例 -->
    <bean id="jdbcTemplate"
        class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- 3.配置事务 -->
    <bean id="dataSourceTransactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

</beans>

 

这个spring的配置对下面的两个案例都其作用。

 

package com.qingruihappy1.dao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository
public class UserDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    public void add(String name, Integer age) {
        String sql = "INSERT INTO t_users(NAME, age) VALUES(?,?);";
        int updateResult = jdbcTemplate.update(sql, name, age);
        System.out.println("updateResult:" + updateResult);
    }

}

 

package com.qingruihappy1.service;

//user 服务层
public interface UserService {
    public void add();
}

 

 

package com.qingruihappy1.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.qingruihappy1.service.UserService;
import com.qingruihappy2.dao.UserDaob;

//user 服务层
@Service
public class UserServiceImpla implements UserService {
    @Autowired
    private UserDaob userDao;
    public void add() {
        // 注意在这里没有事物的话,执行完userDao.add("test001", 20);就会把数据插入到数据库中去的。
            userDao.add("test001", 20);
            int a=1/0;
            //因为这里没有事物所以当报错的时候tes001会插入到数据库中,而test002不会插入到数据库中。
            System.out.println("################");
            userDao.add("test002", 21);
    }

}

 

 

 

 

package com.qingruihappy1.test;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.qingruihappy1.service.UserService;


public class Test001 {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        UserService userService = (UserService) applicationContext.getBean("userServiceImpla");
        userService.add();
    }

}

 

 

结果:

Exception in thread "main" updateResult:1
java.lang.ArithmeticException: / by zero
    at com.qingruihappy1.service.impl.UserServiceImpla.add(UserServiceImpla.java:17)
    at com.qingruihappy1.test.Test001.main(Test001.java:13)

 

数据库:

技术分享图片

 

 

 

 这是因为在没有事物的情况下,执行完service的userDao.add("test001", 20);这一行代码的时候,数据库里面立马就有数值了,也不存在所谓的回滚了。

 

2.3,手写编程式事物

package com.qingruihappy2.dao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository
public class UserDaob {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    public void add(String name, Integer age) {
        String sql = "INSERT INTO t_users(NAME, age) VALUES(?,?);";
        int updateResult = jdbcTemplate.update(sql, name, age);
        System.out.println("updateResult:" + updateResult);
    }

}

 

 

package com.qingruihappy2.service;

//user 服务层
public interface UserService {
    public void add();
}
package com.qingruihappy2.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;

import com.qingruihappy2.dao.UserDaob;
import com.qingruihappy2.service.UserService;
import com.qingruihappy2.transaction.TransactionUtils;

//user 服务层
@Service
public class UserServiceImplb implements UserService {
    @Autowired
    private UserDaob userDao;
    @Autowired
    private TransactionUtils transactionUtils;

    // spring 事务封装呢? aop技术
    public void add() {
        TransactionStatus transactionStatus = null;
        try {
            // 开启事务
            transactionStatus = transactionUtils.begin();
            userDao.add("test001", 20);
            System.out.println("开始报错啦[email protected]!!");
             int i = 1 / 0;
            System.out.println("################");
            userDao.add("test002", 21);
            // 提交事务
            //有错的话就会回滚的,这个时候当执行完userDao.add("test001", 20);,并不会插入到数据库中,只有确保commit的时候才会提交到数据库中去的
            //出错的话就会rollback的。
            if (transactionStatus != null)
                transactionUtils.commit(transactionStatus);
        } catch (Exception e) {
            e.getMessage();
            // 回滚事务
            if (transactionStatus != null)
                transactionUtils.rollback(transactionStatus);
        }
    }

}

 

package com.qingruihappy2.transaction;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.interceptor.DefaultTransactionAttribute;

//编程事务(需要手动begin 手动回滚  手都提交)
@Component
public class TransactionUtils {

    // 获取事务源
    @Autowired
    private DataSourceTransactionManager dataSourceTransactionManager;

    // 开启事务
    public TransactionStatus begin() {
        TransactionStatus transaction = dataSourceTransactionManager.getTransaction(new DefaultTransactionAttribute());
        return transaction;
    }

    // 提交事务
    public void commit(TransactionStatus transaction) {
        dataSourceTransactionManager.commit(transaction);
    }

    // 回滚事务
    public void rollback(TransactionStatus transaction) {
        dataSourceTransactionManager.rollback(transaction);
    }

}
package com.qingruihappy2.test;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.qingruihappy2.service.UserService;


public class Test001 {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        UserService userService = (UserService) applicationContext.getBean("userServiceImplb");
        userService.add();
    }

}

 

 

2.4,手写声明式事务

package com.qingruihappy3.dao;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository
public class UserDaoc {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    public void add(String name, Integer age) {
        String sql = "INSERT INTO t_users(NAME, age) VALUES(?,?);";
        int updateResult = jdbcTemplate.update(sql, name, age);
        System.out.println("updateResult:" + updateResult);
    }

}

 

package com.qingruihappy3.service;

//user 服务层
public interface UserService {
    public void add();
}
package com.qingruihappy3.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import com.qingruihappy1.dao.UserDao;
import com.qingruihappy3.service.UserService;
import com.qingruihappy3.transaction.TransactionUtilsc;

//user 服务层
@Service
public class UserServiceImplc implements UserService {
    @Autowired
    private UserDao userDao;

    public void add() {
        // 注意事项: 在使用spring事务的时候,service 不要try 最将异常抛出给外层aop 异常通知接受回滚
        try {
            userDao.add("test001", 20);
            int i = 1 / 0;
            System.out.println("################");
            userDao.add("test002", 21);
        } catch (Exception e) {
            e.printStackTrace();
            //TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        //在spring的事物中我们一般不要trycatch 因为一旦吃掉异常的话就不会走到反射类的AopTransaction的afterThrowing()异常方法中去了
        //这个时候假如我们的业务场景必须的try的话,我们在catch的里面加上手动回滚的代码TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        //记住必须每个catch都加的。
/*        userDao.add("test001", 20);
        int i = 1 / 0;
        System.out.println("################");
        userDao.add("test002", 21);*/
    }

}

 

package com.qingruihappy3.transaction;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.interceptor.DefaultTransactionAttribute;

//编程事务(需要手动begin 手动回滚  手都提交)
@Component
public class TransactionUtilsc {

    // 获取事务源
    @Autowired
    private DataSourceTransactionManager dataSourceTransactionManager;

    // 开启事务
    public TransactionStatus begin() {
        TransactionStatus transaction = dataSourceTransactionManager.getTransaction(new DefaultTransactionAttribute());
        return transaction;
    }

    // 提交事务
    public void commit(TransactionStatus transaction) {
        dataSourceTransactionManager.commit(transaction);
    }

    // 回滚事务
    public void rollback(TransactionStatus transaction) {
        dataSourceTransactionManager.rollback(transaction);
    }

}
package com.qingruihappy3.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import com.qingruihappy3.transaction.TransactionUtilsc;


//切面类  基于手手动事务封装
@Component
@Aspect
public class AopTransaction {
    @Autowired
    private TransactionUtilsc transactionUtils;

    // TransactionUtils 不要实现为单例子: 如果为单例子的话可能会发生线程安全问题
    // // 异常通知
    @AfterThrowing("execution(* com.qingruihappy3.service.UserService.*(..))")
    public void afterThrowing() {
        System.out.println("回滚事务");
        // 获取当前事务 直接回滚
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }

    // 环绕通知 在方法之前和之后处理事情
    @Around("execution(* com.qingruihappy3.service.UserService.*(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {

        // 调用方法之前执行
        System.out.println("开启事务");
        TransactionStatus transactionStatus = transactionUtils.begin();
        proceedingJoinPoint.proceed();// 代理调用方法 注意点: 如果调用方法抛出溢出不会执行后面代码
        // 调用方法之后执行
        System.out.println("提交事务");
        transactionUtils.commit(transactionStatus);
    }
}
package com.qingruihappy3;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.qingruihappy3.service.UserService;


public class Test001 {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        UserService userService = (UserService) applicationContext.getBean("userServiceImplc");
        userService.add();
    }

}

 

事务是程序运行如果没有错误,会自动提交事物,如果程序运行发生异常,则会自动回滚。

如果使用了try捕获异常时.一定要在catch里面手动回滚。

事务手动回滚代码

TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

 

以上是关于spring事物-----手写spring的事物框架的主要内容,如果未能解决你的问题,请参考以下文章

spring详解——事物管理

Spring的事物管理

Spring事物三千问Spring配置多数据源 vs 给多个数据源添加事物管理

spring全注解事务管理中怎么手动回滚事物

spring事物源码分析篇三:业务代码的执行--事物增强器

spring事務