目录
前言
我之前的一篇博文讲述了一个基于Flask框架,Web后端项目重构的内容,最后的服务层级事务控制一部分没有讲,在这里做一下补充,包括思想的运用和代码的展示。
出门左转另一篇博文,了解需求背景:
正文
因为之前是用java那一套的,使用springBoot框架,那个框架相对来说比较全面,包含了各种组件,以及方便的事务控制注解组件
但是,重构的项目是Flask搭建的,比较轻量级,没有那么全面的功能。
我在重构的时候也发现了这个问题,所以,我就使用Python装饰器实现了一个类似java中springBoot框架的事务注解的组件
服务层事务控制的前提:
flask进行更修数据等操作,全部使用flush()来写入缓存
db.session.flush()
而不要使用commit()直接提交到数据库中,这样在服务出现异常的时候就可以实现事务的回滚
db.session.commit()
服务层级事务控制器
"""
事务处理控制
功能:
服务层统一事务控制
"""
import traceback
from src.const.extensions import db
from src.dto.ResultInfo import ResultInfo
class Transactional:
"""装饰器实体函数"""
@staticmethod
def transRollBack(originalFunction):
def newFunction(*args, **kwargs):
result = ResultInfo()
try:
result = originalFunction(*args, **kwargs)
except Exception as e:
err_info = traceback.format_exc()
print(err_info)
print("transactional rollback")
db.session.rollback()
return result.fail(msg="服务异常")
else:
db.session.commit()
return result
return newFunction
"""
类装饰器包装
查找所有需要事务控制的服务方法
"""
@staticmethod
def transactional(Cls):
class NewCls:
def __init__(self, *args, **kwargs):
self.oInstance = Cls(*args, **kwargs)
# 调用服务的时候会被调用,以实现面向运行时(切面)编程
def __getattribute__(self, name):
try:
# 默认处理方式
x = super(NewCls, self).__getattribute__(name)
except AttributeError as e:
pass
else:
return x
x = self.oInstance.__getattribute__(name)
if type(x) == type(self.__init__):
return Transactional.transRollBack(x)
else:
return x
return NewCls
用法
-
导入注解模块
-
在服务类上加上如下注解即可
from src.transactional.Transactional import Transactional
# 地区服务
@Transactional.transactional
class CityService:
def getAll(self):
resultInfo = ResultInfo()
results = CityDao().getAll()
if len(results):
resultInfo.success(
msg="查找记录成功",
data=results,
)
else:
resultInfo.fail(msg="未找到记录",data=[])
return resultInfo
实现的功能和效果
在所有已经加了事务注解的服务类的任何方法被调用的时候,都会进入事务的控制范围,当一个服务中的任何一步持久化操作出现异常,都会出发事务的回滚,从而实现服务层级的事务控制。(只有在服务被调用,才会针对被调用的服务进行事务控制,相当于是在运行时来控制程序的进行,类似于切面编程的思想)
疑问
可能看到这里,有些人会有些疑问?
问:为什么要实现服务层级的事务控制?
答:首先看这样一个例子:线上转账服务,用户甲,将自己的5k转账给乙,对数据库的操作分为两个步骤,1.甲的账户-5k,2.乙的账户+5k,才算这个转账服务真正完成!,如果第一步完成,并提交数据库,第二步操作因为网络或者一些其它原因造成失败,那么甲就白白损失了5k,是不是不合理?
问:我对数据库的操作一步,进行一步提交到缓存,将1.2.两步代码使用异常捕获,发生异常则回滚数据库。未发生异常则刷新到物理数据库。不也可以吗?
答:当然可以!但是首先有几个问题要明白:有多少个方法要进行事务控制,是所有的更新操作!难道所有的方法里面都要写重复的代码,如果项目比较大,有200个方法呢?2000个呢?累傻小子呢叭,这个时候,面向切面编程的思想就展现出了它的优越性!在java中的相关实现是反射(动态代理),而在python中的相关实现就是装饰器,可以在业务的执行前后进行一些操作,而不用更改原有的代码,甚至你可以完全不用了解原来的代码里面是怎么写的,你就可以实现如此强大的功能。所以,我们要做的就是在所有需要事务控制的方法上加上我们事务处理的注解即可。但是!200个方法也是个不小的工作量,加200行代码进去,这时候就可以考虑在服务层级加事务注解了,只要在服务类上加了注解,那么它的所有的方法都会收到事务的控制。工作量就要小很多,并且也完全符合逻辑,在微服务横行的当下,也不会过时!!!