事务用于将某些操作的多个SQL作为原子性操作,一旦有某一个出现错误,即可回滚到原来的状态,从而保证数据库数据完整性。
举例:有这样一张表
从表里可以看出张三的资金里有850元,李四的资金有632元
假如张三向李四划款20元,那么张三的资金应该减20,李四的资金应该加20
UPDATE usr SET zij = zij - 20 WHERE yhm = '张三'; UPDATE usr SET zij = zij + 20 WHERE yhm = '李四';
可以看到张三的资金以减20,李四的资金以加20
但是如果在李四资金加的时候SQL语句出错,那么就会导致张三减少20成功,李四加上20失败,张三的资金减少了,李四资金没加上,钱就失踪了
UPDATE usr SET zij = zij - 20 WHERE yhm = '张三'; UPDATE usr2 SET zij = zij + 20 WHERE yhm = '李四';
定义一个存储过程来执行事务(回滚)
TRANSACTION表示事务
delimiter $$ DROP PROCEDURE IF EXISTS p1; create PROCEDURE p1( -- 创建存储过程 OUT p_return_code tinyint -- out类型参数,用于返回值 ) BEGIN DECLARE exit handler for sqlexception -- 捕捉错误,如果是sql错误就执行里面的 BEGIN -- ERROR set p_return_code = 1; -- 返回值1,说明sql错误 rollback; -- 回滚数据 END; DECLARE exit handler for sqlwarning -- 捕捉错误,如果是sql警告就执行里面的 BEGIN -- WARNING set p_return_code = 2; -- 返回值2,说明出现sql警告 rollback; -- 回滚数据 END; START TRANSACTION; -- 开始事务 -- 执行sql语句 UPDATE usr SET zij = zij - 20 WHERE yhm = '张三'; UPDATE usr2 SET zij = zij + 20 WHERE yhm = '李四'; COMMIT; -- 提交 -- SUCCESS set p_return_code = 0; -- 返回0说明成功 END $$ delimiter ;
执行存储过程,执行事务
CALL p1(@u); -- 执行存储过程。传参接收返回值 SELECT @u; -- 查看返回值
可以看到返回1,说明sql错误,事务里的sql语句一旦有出现错误,将进行所有数据回滚,也就是无论成功的还是失败的语句都数据不变
pymysql模块,默认开启了事务(回滚),也就是说pymysql模块自动实现了事务(回滚)【重点】
还是上面的列子,将一个sql语句写错
#!/usr/bin/env python #coding:utf-8 import tornado.ioloop import tornado.web #导入tornado模块下的web文件 import pymysql #导入数据库模块 class khdHandler(tornado.web.RequestHandler): def get(self): #连接数据库 conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='279819', db='cshi',charset='utf8') # 创建游标 cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) #获取存储过程(函数)的返回值 effect_row = cursor.execute("UPDATE usr SET zij = zij - 20 WHERE yhm = '张三'") effect_row = cursor.execute("UPDATE usr2 SET zij = zij + 20 WHERE yhm = '李四'") fhuizhi = cursor.fetchone() print(fhuizhi) # 提交,不然无法保存新建或者修改的数据 conn.commit() # 关闭游标 cursor.close() # 关闭连接 conn.close() self.write("欢迎访问") settings = { #html文件归类配置,设置一个字典 "template_path":"views", #键为template_path固定的,值为要存放HTML的文件夹名称 "static_path":"statics", #键为static_path固定的,值为要存放js和css的文件夹名称 } #路由映射 application = tornado.web.Application([ #创建一个变量等于tornado.web下的Application方法 (r"/khd", khdHandler), ],**settings) #将html文件归类配置字典,写在路由映射的第二个参数里 if __name__ == "__main__": #内部socket运行起来 application.listen(8002) #设置端口 tornado.ioloop.IOLoop.instance().start()
当执行时可以看到,提示第二个语句表名称是错误的,但是第一个应该是执行成功的
在这种情况下,可以看到pymysql模块自动实现了事务(回滚),数据库数据没有改变