要使用Springboot的事务其实非常简单,在启动类上添加@EnableTransactionManagement,在Service的类或者方法上使用@Transactional就可以了。
  
  事务本身的4大特性
  
  原子性(Atomicity) 指事务必须是一个不可分割的整体
  
  一致性(Consistency) 指执行完数据库操作后,数据不会被破坏
  
  隔离性(Isolation) 保证数据库操作之间,彼此没有任何干扰
  
  持久性(Durability) 保证永久的存放在磁盘中
  
  其中隔离性又分四个级别,它们依次向下,级别越来越高,并发性越来越差,安全性越来越高
  
  READ_UNCOMMITTED 允许存在脏读(事务A读取了事务B未提交的数据,并在这个基础上又做了其他操作)
  
  READ_COMMITTED 不允许脏读,允许不可重复读(事务A读取了事务B已提交的更改数据)
  
  REPEATABLE_READ 不允许脏读,不可重复读,允许幻读(事务A读取了事务B已提交的新增数据)
  
  SERIALIZABLE 全部不允许,做到完全隔离
  
  而Spring是以7种事务传播行为来区别的,假设事务从方法A传播到方法B,用户需要面对方法B,需要知道方法A有事务吗?
  
  PROPAGATION_REQUIRED 如果没有,就新建一个事务;如果有,就加入当前事务。是Spring默认的事务传播行为,适合绝大多数情况。
  
  PROPAGATION_REQUIRES_NEW 如果没有,就新建一个事务;如果有,就将当前事务挂起,意思就是创建了一个新事务,它和原来的事务没有任何关系。
  
  PROPAGATION_NESTED 如果没有,就新建一个事务;如果有,就在当前事务中嵌套其他事务,也就是“嵌套事务”,所嵌套的子事务与主事务之间是有关联关系的(当主事务提交或回滚,子事务也会提交或回滚)。
  
  PROPAGATION_SUPPORTS 如果没有,就以非事务方式执行;如果有,就使用当前事务。这种方式非常随意,没有就没有,有就有,有点无所谓的态度,反正是支持的。
  
  PROPAGATION_NOT_SUPPORTED 如果没有,就以非事务方式执行;如果有,就将当前事务挂起,这种方式非常强硬,没有就没有,有也不支持,挂起,不管。
  
  PROPAGATION_NEVER 如果没有,就以非事务方式执行;如果有,就抛出异常。这种方式更强硬,没有就没有,有了反而报错,从不支持事务。
  
  PROPAGATION_MANDATORY 如果没有,就抛出异常;如果有,就使用当前事务。这种方式可以说是最强硬的,没有事务就直接报错,必须要有事务。
  
  具体配置为
  
  @Transactional(isolation = Isolation.DEFAULT,propagation = Propagation.REQUIRED)
  
  其中isolation和propagation取值都是枚举。
  
  现在我们来自己实现一个事务管理特性,代码承接于 AOP原理与自实现
  
  首先在pom中增加JDBC的引用,根据你数据库版本的不同而不同,我这里是针对mysql 8的。
  
  <dependency>
  
  <groupId>mysql</groupId>
  
  <artifactId>mysql-connector-java</artifactId>
  
  <version>8.0.11</version>
  
  </dependency>
  
  定义事务注解
  
  package com.guanjian.annotion;
  
  import java.lang.annotation.ElementType;
  
  import java.lang.annotation.Retention;
  
  import java.lang.annotation.RetentionPolicy;
  
  import java.lang.annotation.Target;
  
  /**
  
  * 定义需要事务控制的方法
  
  */
  
  @Target(ElementType.METHOD)
  
  @Retention(RetentionPolicy.RUNTIME)
  
  public @interface Transacion {
  
  }
  
  实现一个线程隔离类
  
  package com.guanjian.proxy;
  
  import java.util.Map;
  
  import java.util.concurrent.ConcurrentHashMap;
  
  /**
  
  * 线程隔离
  
  */
  
  public class ThreadLocal<T> {
  
  private Map<Thread,T> container = new ConcurrentHashMap<>();
  
  public void set(T value) {
  
  container.put(Thread.currentThread(),value);
  
  }
  
  public T get() {
  
  Thread thread = Thread.currentThread();
  
  T value = container.get(thread);
  
  if (value == null && !container.containsKey(thread)) {
  
  value = initialValue();
  
  container.put(thread,value);
  
  }
  
  return value;
  
  }
  
  public void remove() {
  
  container.remove(Thread.currentThread());
  
  }
  
  protected T initialValue() {
  
  return null;
  
  }
  
  }
  
  数据库操作助手类
  
  package com.guanjian.util;
  
  import com.guanjian.proxy.ThreadLocal;
  
  import java.sql.Connection;
  
  import java.sql.DriverManager;
  
  import java.sql.SQLException;
  
  /**
  
  * 数据库操作助手类
  
  */
  
  public class DatabaseHelper {
  
  private static final String driver = "com.mysql.cj.jdbc.Driver";
  
  private static final String url = "jdbc:mysql://192.168.1.102:3306/cloud_user?useSSL=FALSE&serverTimezone=GMT%2B8";
  
  private static final String username = "root";
  
  private static final String password = "root";
  
  //用于放置数据库连接的局部线程变量(使每个线程都拥有自己的连接),用于线程隔离,生成环境请使用数据库连接池
  
  private static ThreadLocal<Connection> CONNECTION_HOLDER = new ThreadLocal<>();
  
  /**
  
  * 开启事务
  
  */
  
  public static void beginTransaction() {
  
  Connection conn = getConnection();
  
  if (conn != null) {
  
  try {
  
  conn.setAutoCommit(false);
  
  } catch (SQLException e) {
  
  System.out.println("Begin transaction failure " + e);
  
  throw new RuntimeException(e);
  
  }finally {
  
  CONNECTION_HOLDER.set(conn);
  
  }
  
  }
  
  }
  
  /**
  
  * 提交事务
  
  */
  
  public static void commitTransaction() {
  
  Connection conn = getConnection();
  
  if (conn != null) {
  
  try {
  
  conn.commit();
  
  conn.close();
  
  } catch (SQLException e) {
  
  System.out.println("commit transaction failure " + e);
  
  throw new RuntimeException(e)
  
  } finally {
  
  CONNECTION_HOLDER.remove();
  
  }
  
  }
  
  }
  
  /**
  
  * 回滚事务
  
  */
  
  public static void rollbackTransaction() {
  
  Connection conn = getConnection();
  
  if (conn != null) {
  
  try {
  
  conn.rollback();
  
  conn.close();
  
  } catch (SQLException e) {
  
  System.out.println("rollback transaction failure" + e);
  
  throw new RuntimeException(e);
  
  } finally {
  
  CONNECTION_HOLDER.remove();
  
  }
  
  }
  
  }
  
  private static Connection getConnection(){
  
  Connection conn = null;
  
  try {
  
  Class.forName(driver);
  
  conn = DriverManager.getConnection(url,username,password);
  
  } catch (Exception e) {
  
  e.printStackTrace();
  
  }
  
  return conn;
  
  }
  
  }
  
  使用事务代理类对标记有@Transaction的方法拦截,进行代理增强
  
  package com.guanjian.proxy;
  
  import com.guanjian.annotion.Transacion;
  
  import com.guanjian.util.DatabaseHelper;
  
  import java.lang.reflect.Method;
  
  /**
  
  * 事务代理
  
  */
  
  public class TransactionProxy implements Proxy {
  
  //线程事务控制标志,保证同一个线程中事务控制逻辑只会执行一次
  
  private static final ThreadLocal<Boolean> FLAG_HOLDER = new ThreadLocal<Boolean>() {
  
  @Override
  
  protected Boolean initialValue() {
  
  return false;
  
  }
  
  };
  
  @Override
  
  public Object doProxy(ProxyChain proxyChain) throws Throwable {
  
  Object result;
  
  boolean flag = FLAG_HOLDER.get(); //默认false
  
  Method method = proxyChain.getTargetMethod();
  
  //检查方法是否带有@Transaction注解且是同一个线程执行的,进行代理增强
  
  if (!flag && method.isAnnotationPresent(Transacion.class)) {
  
  FLAG_HOLDER.set(true);
  
  try {
  
  DatabaseHelper.beginTransaction();
  
  System.out.println("begin transaction");
  
  //跟代理链双向递归
  
  result = proxyChain.doProxyChain();
  
  DatabaseHelper.commitTransaction();
  
  System.out.println("commit transaction");
  
  }catch (Exception e) {
  
  DatabaseHelper.rollbackTransaction(www.hengtongyoule.com);
  
  System.out.println("rollback transaction");
  
  throw e;
  
  }finally {
  
  //移除该线程
  
  FLAG_HOLDER.remove();
  
  }
  
  }else { //如果没有注解,则只执行被代理类实例本方法
  
  result = proxyChain.doProxyChain();
  
  }
  
  return result;
  
  }
  
  }
  
  在AOPHelper中进行修改
  
  /**
  
  * 创建所有的AOP类,事务类与与之对应的目标类集合的映射
  
  * @return
  
  * @throws Exception
  
  */
  
  private static Map<Class<?>,Set<Class<?>>> createProxyMap() throws Exception {
  
  Map<Class<?>,Set<Class<?>>> proxyMap = new HashMap<>();
  
  addAspectProxy(proxyMap);
  
  addTransactionProxy(proxyMap);
  
  return proxyMap;
  
  }
  
  /**
  
  * 增加切面代理
  
  * @param proxyMap
  
  * @throws Exception
  
  *http://dasheng178.com/#portal/list.html
  
  private static void addAspectProxy(Map<Class<?>,Set<Class<?>>> proxyMap) throws Exception {
  
  //获取切面代理类(抽象类)的所有实现类(子类)
  
  Set<Class<?>> proxyClassSet = ClassHelper.getClassSetBySuper(AspectProxy.class);
  
  for (Class<?> proxyClass:proxyClassSet) {
  
  //实现类是否有@Aspect标签
  
  if (proxyClass.isAnnotationPresent(Aspect.class)) {
  
  //获取该标签
  
  Aspect aspect = proxyClass.getAnnotation(Aspect.class);
  
  //获取所有目标类集合
  
  Set<Class<?>> targetClassSet = createTargetClassSet(aspect);
  
  //将代理类实例与该集合添加map映射
  
  proxyMap.put(proxyClass,targetClassSet);
  
  }
  
  }
  
  }
  
  /**
  
  * 增加事务代理
  
  * @param proxyMap
  
  */
  
  private static void addTransactionProxy(www.meiwanyule.cn Map<www.yongshi123.cn Class<?>,Set<Class<?>>> proxyMap) {
  
  //以@Componet标签为开启事务代理的类标签
  
  Set<Class<?>> componentClassSet www.xintiandiyule1.com/= ClassHelper.www.yongshiyule178.comgetClassSetByAnnotation(Component.class);
  
  proxyMap.put(TransactionProxy.www.sanxinyuLevip.com class,componentClassSet);
  
  }
  
  测试
  
  @Component
  
  public class Test4 {
  
  @Transacion
  
  public void show(www.michenggw.com) {
  
  System.out.println("aaa");
  
  }
  
  }
  
  public class Test {
  
  public static void main(String[www.baishenyvip.com] args) {
  
  //扫描包
  
  Manager.scanAndImp("com.guanjian.test");
  
  //初始化AopHelper
  
  ClassUtil.loadClass(AopHelper.class.getName(),true);
  
  //这里其实拿到的是代理类的实例,代理类是目标类的子类
  
  Test4 test4 = (www.sanxingyLzc.com Test4)Manager.getBean(www.suoLaieyuLe.com Test4.class);
  
  test4.show();
  
  }
  
  }
  
  运行结果
  
  aop Class loaded
  
  begin transaction
  
  aaa
  
  commit transaction

Spring事务说明与自实现的更多相关文章

  1. spring事务概念理解

    1.数据并发问题 脏读 A事务读取B事务尚未提交的更新数据,并在此数据的基础上操作.如果B事务回滚,则A事务读取的数据就是错误的.即读取了脏数据或者错误数据. 不可重复组 A事务先后读取了B事务提交[ ...

  2. 【Java EE 学习 52】【Spring学习第四天】【Spring与JDBC】【JdbcTemplate创建的三种方式】【Spring事务管理】【事务中使用dbutils则回滚失败!!!??】

    一.JDBC编程特点 静态代码+动态变量=JDBC编程. 静态代码:比如所有的数据库连接池 都实现了DataSource接口,都实现了Connection接口. 动态变量:用户名.密码.连接的数据库. ...

  3. Spring事务

    1.@Transactional 只能被应用到public方法上, 对于其它非public的方法,如果标记了@Transactional也不会报错,但方法没有事务功能.@Transactional 的 ...

  4. spring事务管理器设计思想(二)

    上文见<spring事务管理器设计思想(一)> 对于第二个问题,涉及到事务的传播级别,定义如下: PROPAGATION_REQUIRED-- 如果当前没有事务,就新建一个事务.这是最常见 ...

  5. spring事务管理器设计思想(一)

    在最近做的一个项目里面,涉及到多数据源的操作,比较特殊的是,这多个数据库的表结构完全相同,由于我们使用的ibatis框架作为持久化层,为了防止每一个数据源都配置一套规则,所以重新实现了数据源,根据线程 ...

  6. Spring事务管理的三种方式

    一 .第一种:全注解声明式事务 Xml代码 复制代码 收藏代码 .<?xml version="1.0" encoding="UTF-8"?> .& ...

  7. spring 事务传播特性 和隔离级别

    事务的几种传播特性1. PROPAGATION_REQUIRED: 如果存在一个事务,则支持当前事务.如果没有事务则开启2. PROPAGATION_SUPPORTS: 如果存在一个事务,支持当前事务 ...

  8. Spring事务管理

    Spring是SSH中的管理员,负责管理其它框架,协调各个部分的工作.今天一起学习一下Spring的事务管理.Spring的事务管理分为声明式跟编程式.声明式就是在Spring的配置文件中进行相关配置 ...

  9. Spring事务传播属性

    Spring 对事务控制的支持统一在 TransactionDefinition 类中描述,该类有以下几个重要的接口方法: int getPropagationBehavior():事务的传播行为 i ...

  10. Spring事务属性的介绍

    Spring声明式事务让我们从复杂的事务处理中得到解脱.使得我们再也无需要去处理获得连接.关闭连接.事务提交和回滚等这些操作.再也无需要我们在与事务相关的方法中处理大量的try-catch-final ...

随机推荐

  1. LINQ Group By操作(转载)

    假设我们需要从两张表中统计出热门商圈,这两张表内容如下: 上表是所有政区,商圈中的餐饮个数,名为FoodDistrict 下表是所有政区,商圈中的SPA个数,名为SPADistrict 现在要把这两张 ...

  2. CSS3选择器之:nth-child(n)

    第一次用到这个选择器还是为了解决下面了的问题: 手机的前端,我们使用了mint-ui,里面有一个日期选择控件,但是选择的时候没有提供年月的选择器,如: 而是提供了下面的方式: 为了达到上面的效果,使用 ...

  3. 如何使用 GroupBy 计数-Count()

    十年河东,十年河西,莫欺少年穷. 本节探讨的内容很简单,就是如果使用GroupBy计数 提供两种方法:第一:把查询的数据,转化为泛型,然后泛型分组计数. 第二:Linq语句直接分组计数 有如下范例: ...

  4. linux中fork, source和exec的区别

    转:linux中fork, source和exec的区别 shell的命令可以分为内部命令和外部命令. 内部命令是由特殊的文件格式.def实现的,如cd,ls等.而外部命令是通过系统调用或独立程序实现 ...

  5. 任务(task)

    任务概述 线程(Thread)是创建并发的底层工具,因此有一定的局限性(不易得到返回值(必须通过创建共享域):异常的捕获和处理也麻烦:同时线程执行完毕后无法再次开启该线程),这些局限性会降低性能同时影 ...

  6. 《RabbitMQ Tutorial》译文 第 1 章 简介

    原文来自 RabbitMQ 英文官网的教程(1.Introduction),其示例代码采用了 .NET C# 语言. RabbitMQ is a message broker: it accepts ...

  7. KVM虚拟机管理——资源调整

    1. 概述2. 计算资源调整2.1 调整处理器配置2.2 调整内存配置3. 存储资源调整3.1 根分区扩展3.2 添加磁盘4. 网络资源调整 1. 概述 KVM在使用过程中,会涉及到计算(CPU,内存 ...

  8. Codeforces Round #504 (rated, Div. 1 + Div. 2, based on VK Cup 2018 Final)-D- Array Restoration

    我们知道不满足的肯定是两边大中间小的,这样就用RMQ查询两个相同等值的区间内部最小值即可,注意边界条件 #include<bits/stdc++.h> #define x first #d ...

  9. UML图中类之间的关系:依赖,泛化,关联,聚合,组合,实现(转)

    UML图中类之间的关系:依赖,泛化,关联,聚合,组合,实现   类与类图 1) 类(Class)封装了数据和行为,是面向对象的重要组成部分,它是具有相同属性.操作.关系的对象集合的总称. 2) 在系统 ...

  10. 转发:Android开发?用C#!!

    转发自 最近偶然在QQ技术群里见到有人提起用C#开发Android,当时我感觉到很诧异:Android不是只能用Java开发吗?何时可以使用C#了?那个群友便告知我:mono. 百度一下吧!搜到了mo ...