利用AOP实现SqlSugar自动事务

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

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.GetStudentsmValueService.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()
         {
         }
     }

AutofacCastle.DynamicProxy结合使用

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

 proxyGenerate.CreateClassProxy<MyClass>(t);

而且项目里BL类的实例化是交给IOC容器控制的,我用的是Autofac。当然AutofacCastle.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>();
        }
    }
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 Spring Boot 中,可以使用 AOP(面向切面编程)实现事务隔离级别。通过定义切面和通知,在方法执行前后进行事务管理。 下面是一个使用 AOP 实现事务隔离级别的示例代码: 1. 首先,需要在 Spring Boot 项目中引入相关依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> ``` 2. 创建一个切面类,用于定义通知和切点: ```java @Aspect @Component public class TransactionAspect { @Autowired private PlatformTransactionManager transactionManager; @Pointcut("@annotation(org.springframework.transaction.annotation.Transactional)") public void transactionalMethod() {} @Around("transactionalMethod()") public Object manageTransaction(ProceedingJoinPoint joinPoint) throws Throwable { // 获取注解上的事务属性 MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); Transactional transactional = method.getAnnotation(Transactional.class); // 创建事务定义 DefaultTransactionDefinition definition = new DefaultTransactionDefinition(); definition.setPropagationBehavior(transactional.propagation().value()); definition.setIsolationLevel(transactional.isolation().value()); // 获取事务状态 TransactionStatus status = transactionManager.getTransaction(definition); try { // 执行目标方法 Object result = joinPoint.proceed(); // 提交事务 transactionManager.commit(status); return result; } catch (Throwable ex) { // 回滚事务 transactionManager.rollback(status); throw ex; } } } ``` 在上面的代码中,`transactionalMethod` 方法用于定义切点,指定切入带有 `@Transactional` 注解的方法。`manageTransaction` 方法则是通知,在目标方法执行前后进行事务管理。在该方法中,我们根据注解上的属性值创建事务定义,并利用事务管理器执行相应的事务操作。 3. 使用 `@Transactional` 注解来标记需要进行事务管理的方法: ```java @Service public class MyService { @Transactional(isolation = Isolation.READ_COMMITTED) public void myMethod() { // 代码逻辑 } } ``` 在上面的示例中,`myMethod` 方法使用了 `@Transactional` 注解,指定了事务隔离级别为 `READ_COMMITTED`。 需要注意的是,AOP 实现事务管理是基于方法级别的,因此需要确保切面类和被切方法位于不同的类中。此外,确保切面类被 Spring Boot 扫描到,并且事务管理器已经正确配置。 通过以上步骤,你可以使用 AOP 实现事务隔离级别。当调用被 `@Transactional` 注解标记的方法时,AOP 将会拦截并根据注解中指定的隔离级别进行事务管理。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值