先看一下效果,带接口层的三层架构:

BL层:

  public class StudentBL : IStudentService
{
private ILogger mLogger;
private readonly IStudentDA mStudentDa;
private readonly IValueService mValueService; public StudentService(IStudentDA studentDa,IValueService valueService)
{
mLogger = LogManager.GetCurrentClassLogger();
mStudentDa = studentDa;
mValueService = valueService; } [TransactionCallHandler]
public IList<Student> GetStudentList(Hashtable paramsHash)
{
var list = mStudentDa.GetStudents(paramsHash);
var value = mValueService.FindAll();
return list;
}
}

假设GetStudentList方法里的mStudentDa.GetStudents和mValueService.FindAll不是查询操作,而是更新操作,当一个失败另一个需要回滚,就需要在同一个事务里,当一个出现异常就要回滚事务。

特性TransactionCallHandler就表明当前方法需要开启事务,并且当出现异常的时候回滚事务,方法执行完后提交事务。

DA层:

 public class StudentDA : IStudentDA
{ private SqlSugarClient db;
public StudentDA()
{
db = SugarManager.GetInstance().SqlSugarClient;
}
public IList<Student> GetStudents(Hashtable paramsHash)
{
return db.Queryable<Student>().AS("T_Student").With(SqlWith.NoLock).ToList();
}
}

SqlSugar做一下包装

 public class SugarManager
{
private static ConcurrentDictionary<string,SqlClient> _cache =
new ConcurrentDictionary<string, SqlClient>();
private static ThreadLocal<string> _threadLocal;
private static readonly string _connStr = @"Data Source=localhost;port=3306;Initial Catalog=thy;user id=root;password=xxxxxx;Charset=utf8";
static SugarManager()
{
_threadLocal = new ThreadLocal<string>();
} private static SqlSugarClient CreatInstance()
{
SqlSugarClient client = new SqlSugarClient(new ConnectionConfig()
{
ConnectionString = _connStr, //必填
DbType = DbType.MySql, //必填
IsAutoCloseConnection = true, //默认false
InitKeyType = InitKeyType.SystemTable
});
var key=Guid.NewGuid().ToString().Replace("-", "");
if (!_cache.ContainsKey(key))
{
_cache.TryAdd(key,new SqlClient(client));
_threadLocal.Value = key;
return client;
}
throw new Exception("创建SqlSugarClient失败");
}
public static SqlClient GetInstance()
{
var id= _threadLocal.Value;
if (string.IsNullOrEmpty(id)||!_cache.ContainsKey(id))
return new SqlClient(CreatInstance());
return _cache[id];
} public static void Release()
{
try
{
var id = GetId();
if (!_cache.ContainsKey(id))
return;
Remove(id);
}
catch (Exception e)
{
throw e;
}
}
private static bool Remove(string id)
{
if (!_cache.ContainsKey(id)) return false; SqlClient client; int index = 0;
bool result = false;
while (!(result = _cache.TryRemove(id, out client)))
{
index++;
Thread.Sleep(20);
if (index > 3) break;
}
return result;
}
private static string GetId()
{
var id = _threadLocal.Value;
if (string.IsNullOrEmpty(id))
{
throw new Exception("内部错误: SqlSugarClient已丢失.");
}
return id;
} public static void BeginTran()
{
var instance=GetInstance();
//开启事务
if (!instance.IsBeginTran)
{
instance.SqlSugarClient.Ado.BeginTran();
instance.IsBeginTran = true;
}
} public static void CommitTran()
{
var id = GetId();
if (!_cache.ContainsKey(id))
throw new Exception("内部错误: SqlSugarClient已丢失.");
if (_cache[id].TranCount == 0)
{
_cache[id].SqlSugarClient.Ado.CommitTran();
_cache[id].IsBeginTran = false;
}
} public static void RollbackTran()
{
var id = GetId();
if (!_cache.ContainsKey(id))
throw new Exception("内部错误: SqlSugarClient已丢失.");
_cache[id].SqlSugarClient.Ado.RollbackTran();
_cache[id].IsBeginTran = false;
_cache[id].TranCount = 0;
} public static void TranCountAddOne()
{
var id = GetId();
if (!_cache.ContainsKey(id))
throw new Exception("内部错误: SqlSugarClient已丢失.");
_cache[id].TranCount++;
}
public static void TranCountMunisOne()
{
var id = GetId();
if (!_cache.ContainsKey(id))
throw new Exception("内部错误: SqlSugarClient已丢失.");
_cache[id].TranCount--;
}
}

_cache保存SqlSugar实例,_threadLocal确保同一线程下取出的是同一个SqlSugar实例。

不知道SqlSugar判断当前实例是否已经开启事务,所以又将SqlSugar包了一层。

  public class SqlClient
{
public SqlSugarClient SqlSugarClient;
public bool IsBeginTran = false;
public int TranCount = 0; public SqlClient(SqlSugarClient sqlSugarClient)
{
this.SqlSugarClient = sqlSugarClient;
}
}

IsBeginTran标识当前SqlSugar实例是否已经开启事务,TranCount是一个避免事务嵌套的计数器。

一开始的例子

            [TransactionCallHandler]
public IList<Student> GetStudentList(Hashtable paramsHash)
{
var list = mStudentDa.GetStudents(paramsHash);
var value = mValueService.FindAll();
return list;
}

TransactionCallHandler表明该方法要开启事务,但是如果mValueService.FindAll也标识了TransactionCallHandler,又要开启一次事务?所以用TranCount做一个计数。

使用Castle.DynamicProxy

要实现标识了TransactionCallHandler的方法实现自动事务,使用Castle.DynamicProxy实现BL类的代理

Castle.DynamicProxy一般操作

  public class MyClass : IMyClass
{
public void MyMethod()
{
Console.WriteLine("My Mehod");
}
}
public class TestIntercept : IInterceptor
{
public void Intercept(IInvocation invocation)
{
Console.WriteLine("before");
invocation.Proceed();
Console.WriteLine("after");
}
} var proxyGenerate = new ProxyGenerator();
TestIntercept t=new TestIntercept();
var pg = proxyGenerate.CreateClassProxy<MyClass>(t);
pg.MyMethod();
//输出是
//before
//My Mehod
//after

before就是要开启事务的地方,after就是提交事务的地方

最后实现

  public class TransactionInterceptor : IInterceptor
{
private readonly ILogger logger;
public TransactionInterceptor()
{
logger = LogManager.GetCurrentClassLogger();
}
public void Intercept(IInvocation invocation)
{
MethodInfo methodInfo = invocation.MethodInvocationTarget;
if (methodInfo == null)
{
methodInfo = invocation.Method;
} TransactionCallHandlerAttribute transaction =
methodInfo.GetCustomAttributes<TransactionCallHandlerAttribute>(true).FirstOrDefault();
if (transaction != null)
{
SugarManager.BeginTran();
try
{
SugarManager.TranCountAddOne();
invocation.Proceed();
SugarManager.TranCountMunisOne();
SugarManager.CommitTran();
}
catch (Exception e)
{
SugarManager.RollbackTran();
logger.Error(e);
throw e;
} }
else
{
invocation.Proceed();
}
}
}
     [AttributeUsage(AttributeTargets.Method, Inherited = true)]
public class TransactionCallHandlerAttribute : Attribute
{
public TransactionCallHandlerAttribute()
{ }
}

Autofac与Castle.DynamicProxy结合使用

创建代理的时候一个BL类就要一次操作

 proxyGenerate.CreateClassProxy<MyClass>(t);

而且项目里BL类的实例化是交给IOC容器控制的,我用的是Autofac。当然Autofac和Castle.DynamicProxy是可以结合使用的

using System.Reflection;
using Autofac;
using Autofac.Extras.DynamicProxy;
using Module = Autofac.Module;
public class BusinessModule : Module
{
protected override void Load(ContainerBuilder builder)
{
var business = Assembly.Load("FTY.Business");
builder.RegisterAssemblyTypes(business)
.AsImplementedInterfaces().InterceptedBy(typeof(TransactionInterceptor)).EnableInterfaceInterceptors();
builder.RegisterType<TransactionInterceptor>();
}
}

利用AOP实现SqlSugar自动事务的更多相关文章

  1. Aop实现SqlSugar自动事务

    http://www.cnblogs.com/jaycewu/p/7733114.html

  2. Hibernate自动事务揪出的编码不规范

    最近重构的项目(Java初学中),Service层一个获取通知记录报错: org.springframework.dao.InvalidDataAccessResourceUsageException ...

  3. [Spring-AOP-XML] 利用Spirng中的AOP和XML进行事务管理

    Spring中的AOP进行事务管理有三种方式 A.自定义事务切面 利用AspectJ来编写事务,我们一般把这个切面作用在service层中.其他代码在下面 编写一个Transaction实现类,通过S ...

  4. Spring AOP实现声明式事务代码分析

    众所周知,Spring的声明式事务是利用AOP手段实现的,所谓"深入一点,你会更快乐",本文试图给出相关代码分析. AOP联盟为增强定义了org.aopalliance.aop.A ...

  5. AOP基本概念、AOP底层实现原理、AOP经典应用【事务管理、异常日志处理、方法审计】

    1 什么是AOP AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软件 ...

  6. 利用AOP与ToStringBuilder简化日志记录

    刚学spring的时候书上就强调spring的核心就是ioc和aop blablabla...... IOC到处都能看到...AOP么刚开始接触的时候使用在声明式事务上面..当时书上还提到一个用到ao ...

  7. SpringBoot31 整合SpringJDBC、整合MyBatis、利用AOP实现多数据源

    一.整合SpringJDBC 1  JDBC JDBC(Java Data Base Connectivity,Java 数据库连接)是一种用于执行 SQL 语句的 Java API,可以为多种关系数 ...

  8. SpringCloud或SpringBoot+Mybatis-Plus利用AOP+mybatis插件实现数据操作记录及更新对比

    引文 本文主要介绍如何使用Spring AOP + mybatis插件实现拦截数据库操作并根据不同需求进行数据对比分析,主要适用于系统中需要对数据操作进行记录.在更新数据时准确记录更新字段 核心:AO ...

  9. SpringBoot2.x整合Email并利用AOP做一个项目异常通知功能

    因为不知aop能干嘛,因此用aop做个小功能,再结合最近学的springboot-Email做了个系统异常自动邮件通知的功能, 感觉满满的成就感. AOP不懂的可以看上一篇:https://www.c ...

随机推荐

  1. php 运行linux命令 与 linux下命令行执行php

    1.php运行linux命令 exec函数:string exec(string command, string [array], int [return_var]);  执行函数后不输出结果,返回最 ...

  2. 【Visual C++】游戏编程学习笔记之三:游戏循环的使用

     本系列文章由@二货梦想家张程 所写,转载请注明出处. 本文章链接:http://blog.csdn.net/terence1212/article/details/44208419 作者:Zee ...

  3. Android Studio相关资料链接

     AndroidStudio中文社区:http://www.android-studio.org/index.php Android studio删除工程项目:http://www.linuxid ...

  4. LeetCode之“散列表”:Single Number

    题目链接 题目要求: Given an array of integers, every element appears twice except for one. Find that single ...

  5. 色彩转换——RGB & HSI

    RGB to HSI I=(R+G+B)/3; S=1-3*min(R,G,B)/(R+G+B); H = cos^(-1)((0.5*((R-G)+(R-B))) / ((R-G)^2 + (R-B ...

  6. 【Android 应用开发】Android游戏音效实现

    1. 游戏音效SoundPool 游戏中会根据不同的动作 , 产生各种音效 , 这些音效的特点是短暂(叫声,爆炸声可能持续不到一秒) , 重复(一个文件不断重复播放) , 并且同时播放(比如打怪时怪的 ...

  7. GNU中的处理目标文件的若干工具

    AR 创建静态库,插入.删除.列出和提取成员: SRING 列出目标文件中的字符串: SIRIP 从目标文件中删除符号表信息: NM 列出目标文件符号表中定义的符号: SIZE 列出目标文件中节的名字 ...

  8. M1卡区块控制位详解

    M1卡区块控制位详解 Mifare 1S50/Mifare 1S70 每个扇区的密码和存取控制都是独立的,可以根据实际需要设定各自的密码及存取 控制.存取控制为4个字节,共32位,扇区中的每个块(包括 ...

  9. JAVA调用数据库存储过程

    下面将举出JAVA对ORACLE数据库存储过程的调用          ConnUtils连接工具类:用来获取连接.释放资源 复制代码 package com.ljq.test; import jav ...

  10. Storm 提交多个流例程

    1.拓扑(Topology): builder.setBolt(TRANSFORM_BOLT, new TransformationBolt(), 1).shuffleGrouping(MY_SPOU ...