要使用Springboot的事務其實很是簡單,在啓動類上添加@EnableTransactionManagement,在Service的類或者方法上使用@Transactional就能夠了。java
事務自己的4大特性mysql
其中隔離性又分四個級別,它們依次向下,級別愈來愈高,併發性愈來愈差,安全性愈來愈高sql
而Spring是以7種事務傳播行爲來區別的,假設事務從方法A傳播到方法B,用戶須要面對方法B,須要知道方法A有事務嗎?數據庫
具體配置爲安全
@Transactional(isolation = Isolation.DEFAULT,propagation = Propagation.REQUIRED)
其中isolation和propagation取值都是枚舉。併發
如今咱們來本身實現一個事務管理特性,代碼承接於 AOP原理與自實現ide
首先在pom中增長JDBC的引用,根據你數據庫版本的不一樣而不一樣,我這裏是針對mysql 8的。測試
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.11</version> </dependency>
定義事務註解url
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 { }
實現一個線程隔離類spa
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(); 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 */ 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(Map<Class<?>,Set<Class<?>>> proxyMap) { //以@Componet標籤爲開啓事務代理的類標籤 Set<Class<?>> componentClassSet = ClassHelper.getClassSetByAnnotation(Component.class); proxyMap.put(TransactionProxy.class,componentClassSet); }
測試
@Component public class Test4 { @Transacion public void show() { System.out.println("aaa"); } }
public class Test { public static void main(String[] args) { //掃描包 Manager.scanAndImp("com.guanjian.test"); //初始化AopHelper ClassUtil.loadClass(AopHelper.class.getName(),true); //這裏其實拿到的是代理類的實例,代理類是目標類的子類 Test4 test4 = (Test4)Manager.getBean(Test4.class); test4.show(); } }
運行結果
aop Class loaded
begin transaction aaa commit transaction