银行转账时,本质就是对数据库中的数据进行加减法,但是当我们在转账的中途突然断电了怎么办,这里就需要用到数据库的执行事务。
事务机制可以保证数据的一致性。
失误应该具有4个属性:原子性、一致性、隔离性、持久性。这四个数次那个通常称为ACID特性。
*原子性(atomicity)。一个事物是一个可分割的工作单位,事物中包括的诸操作要么都做,要么都不做。
*一致性(consistency)。事物必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。
*隔离性(isolation)。一个事物的执行不能被其他事物干扰。即一个事物内部的操作及使用的数据对并发的其他事物是隔
离的,并发执行的各个事物之间不能相互干扰
*持久性(durability)。持续性也称为持久性,只一个事务一旦提交,他对数据库中数据的改变就应该是永久性的。接下来
的其他操作或故障不应该对其有任何影响。
Python DB API 2.0 的事务提供了两个方法 commit 或 rollback。
对于支持事务的数据库, 在Python数据库编程中,当游标建立之时,就自动开始了一个隐形的数据库事务。commit()方法游标的所有更新操作,rollback()方法回滚当前游标的所有操作。每一个方法都开始了一个新的事务。
import sys import pymysql class TransferMoney(object): def __init__(self, conn): self.conn = conn def transfer(self, source_acctid, target_acctid, money): """转账函数""" try: # 判断账户是否存在? self.check_acct_available(source_acctid) self.check_acct_available(target_acctid) self.has_enough_money(source_acctid, money) # 001: 1000 002: 500 # 001: 1000 002: 500 self.reduce_money(source_acctid, money) self.add_money(target_acctid, money) self.conn.commit() except Exception as e: # 撤销数据库的执行 self.conn.rollback() raise e def check_acct_available(self, acctid): """检测账户是否存在?""" cur = self.conn.cursor() try: # 查询账户名为acctid的详细信息 select_sql = 'select * from account where accid=%s;' % (acctid) cur.execute(select_sql) print("execute sql:%s" % (select_sql)) res = cur.fetchall() if len(res) != 1: raise Exception("帐号%s不存在" % (acctid)) finally: cur.close() def has_enough_money(self, acctid, money): """检测账户是否有足够的钱""" cur = self.conn.cursor() try: select_sql = 'select money from account where accid=%s' \ %(acctid) cur.execute(select_sql) print('has_enough_money sql: %s' %(select_sql)) res = cur.fetchall() if res: now_money = int(res[0][0]) if int(now_money) < int(money): raise Exception("账户%s没有足够的钱,目前金额为 %d" %(acctid, now_money)) finally: cur.close() def reduce_money(self, acctid, money): """给其他账户进行转账操作""" cur = conn.cursor() try: update_sqli = 'update account set money=money-%s where accid=%s' %(money, acctid) cur.execute(update_sqli) print('reduce_money sql:%s' %(update_sqli)) except Exception as e: print("reduce money failed:",e) finally: cur.close() def add_money(self, acctid, money): cur = conn.cursor() try: update_sqli = 'update account set money=money+%s where accid=%s' % (money, acctid) cur.execute(update_sqli) print('add_money sql:%s' % (update_sqli)) except Exception as e: print("add money failed:",e) finally: cur.close() if __name__ == "__main__": conn = pymysql.connect( host='localhost', user='root', passwd='yutao', db='yutao', charset='utf8') # sys.argv 变量存储[脚本名, 传递的第一个参数,传递的第2个参数, ] source_acctid = sys.argv[1] target_acctid = sys.argv[2] money = sys.argv[3] trans_money = TransferMoney(conn) try: trans_money.transfer(source_acctid, target_acctid, money) except Exception as e: print("转账出现问题:", e) finally: conn.close()