基于Python實(shí)現(xiàn)一個(gè)簡(jiǎn)單的銀行轉(zhuǎn)賬操作
前言
在進(jìn)行一個(gè)應(yīng)用系統(tǒng)的開(kāi)發(fā)過(guò)程中,從上到下一般需要四個(gè)構(gòu)件:客戶端-業(yè)務(wù)邏輯層-數(shù)據(jù)訪問(wèn)層-數(shù)據(jù)庫(kù),其中數(shù)據(jù)訪問(wèn)層是一個(gè)底層、核心的技術(shù)。而且在實(shí)際開(kāi)發(fā)中,數(shù)據(jù)庫(kù)的操作也就是說(shuō)數(shù)據(jù)訪問(wèn)層都是嵌套在其他語(yǔ)言中的,其是編程的核心。本文面向的是python語(yǔ)言,即通過(guò)python操作數(shù)據(jù)庫(kù)來(lái)實(shí)現(xiàn)簡(jiǎn)單的銀行轉(zhuǎn)賬操作。
工具
python提供了python DB API用來(lái)統(tǒng)一操作數(shù)據(jù)庫(kù),使訪問(wèn)數(shù)據(jù)庫(kù)的接口規(guī)范化,在沒(méi)有python DB API之前,接口程序十分混亂,不同的數(shù)據(jù)庫(kù)需要不同的操作接口,所以這個(gè)接口提供了極大的方便。在具體操作的時(shí)候,我們需要操作數(shù)據(jù)庫(kù)以及其他邏輯的python代碼,數(shù)據(jù)庫(kù)連接對(duì)象connection來(lái)建立連接,數(shù)據(jù)庫(kù)交互對(duì)象cursor來(lái)“運(yùn)送”數(shù)據(jù),一個(gè)健壯的系統(tǒng)必不可少的便是數(shù)據(jù)庫(kù)異常類Exceptions。整個(gè)訪問(wèn)數(shù)據(jù)庫(kù)流程如下圖:
接下來(lái)分別介紹下兩個(gè)主要對(duì)象:
connection:數(shù)據(jù)庫(kù)連接對(duì)象,建立python客戶端與數(shù)據(jù)庫(kù)的網(wǎng)絡(luò)連接。
創(chuàng)建方法:MySQLdb.connect(),包括的主要成員方法:
cursor():使用該連接創(chuàng)建并返回游標(biāo)
commit():提交當(dāng)前事務(wù)
rollback():回滾當(dāng)前事務(wù)
close()關(guān)閉連接
cursor:游標(biāo)對(duì)象,用于執(zhí)行查詢與獲取結(jié)果,cursor對(duì)象支持的主要方法如下:
execute():執(zhí)行SQL語(yǔ)句,將結(jié)果從數(shù)據(jù)庫(kù)獲取到客戶端
fetchone():取得結(jié)果集的下一行
fetchmany(size):獲取結(jié)果集的下size行
fetchall():獲取結(jié)果集中剩下的所有行
rowcount:最近一次execute返回?cái)?shù)據(jù)的行數(shù)
close():關(guān)閉游標(biāo)對(duì)象
在上面的方法中提到了一個(gè)關(guān)鍵名詞:事務(wù),什么是事務(wù)呢?他是訪問(wèn)和更新數(shù)據(jù)的一個(gè)程序執(zhí)行單元,很多操作的一個(gè)集合,有四個(gè)特點(diǎn):
原子性:事物中包括的諸操作要么都做,要么都不做
一致性:事務(wù)必須使數(shù)據(jù)庫(kù)從一致性狀態(tài)變到另一個(gè)一致性狀態(tài)
隔離型:一個(gè)事務(wù)的執(zhí)行不被其他事務(wù)干擾
持久性:事務(wù)一旦提交,它對(duì)數(shù)據(jù)庫(kù)的改變就是持久性的
事務(wù)的上述特點(diǎn)正是我們完成銀行轉(zhuǎn)賬操作的關(guān)鍵。
具體實(shí)現(xiàn)
在開(kāi)發(fā)中我們?cè)趺礃邮褂檬聞?wù)呢?
關(guān)閉自動(dòng)commit()
正常結(jié)束事務(wù):conn.commit(),
異常結(jié)束事務(wù):conn.rollback()
在銀行轉(zhuǎn)賬系統(tǒng)中,需要考慮如下需求:比如A給B轉(zhuǎn)賬,當(dāng)A賬戶上減少了M錢時(shí),必須在B賬戶上多了M錢,不能A減了B沒(méi)加,也不能B加了A還沒(méi)有減,當(dāng)然賬戶必須是有效的,M錢的金額肯定要大于A賬戶上的金額。所以在具體設(shè)計(jì)的時(shí)候,需要將A賬戶的金錢減少和B賬戶的金錢增加作為一個(gè)事務(wù),要么同時(shí)成功,要么一起失敗。按照這個(gè)需求,書(shū)寫(xiě)代碼,詳細(xì)代碼見(jiàn)github,代碼復(fù)制和數(shù)據(jù)庫(kù)如下,有兩個(gè)賬戶,分別擁有金錢110和10,在運(yùn)行代碼的時(shí)候在參數(shù)欄輸入1,2,100(source_acctid, target_acctid, tranfer_money)。
整個(gè)代碼的邏輯如下:首先連接數(shù)據(jù)庫(kù),之后執(zhí)行邏輯,然后斷開(kāi)數(shù)據(jù)庫(kù)連接,執(zhí)行的邏輯包括檢查轉(zhuǎn)賬雙方的賬戶是否有效,轉(zhuǎn)賬金額是否多于轉(zhuǎn)賬人的賬戶余額,分別給轉(zhuǎn)賬雙方的帳號(hào)金額發(fā)生變化。如果正常結(jié)束事務(wù),提交修改數(shù)據(jù)庫(kù),否則回滾。
#coding:utf-8 import sys import MySQLdb class TransferMoney(): def __init__(self, conn): self.conn = conn def transfer(self, src, target, money): try: self.check_acct_available(src) self.check_acct_available(target) self.has_enough_money(src, money) self.reduce_money(src, money) self.add_money(target, money) self.conn.commit() except Exception as e: print e self.conn.rollback() def reduce_money(self, src, money): cursor = self.conn.cursor() try: sql = "update account set money = money - %s where acctid = %s" %(money, src) cursor.execute(sql) print "reduce_money: " + sql #rs = cursor.fetchall() if cursor.rowcount != 1: raise Exception("the account reduce money fail") finally: cursor.close() def add_money(self, target, money): cursor = self.conn.cursor() try: sql = "update account set money = money + %s where acctid = %s" %(money, target) cursor.execute(sql) print "add_money: " + sql #rs = cursor.fetchall() if cursor.rowcount != 1: raise Exception("the account add money fail") finally: cursor.close() def check_acct_available(self, accit): cursor = self.conn.cursor() try: sql = "select * from account where acctid = %s" %accit cursor.execute(sql) print "check_acct_available: " + sql rs = cursor.fetchall() if len(rs) != 1: raise Exception("the account %s is not exist" %accit) finally: cursor.close() def has_enough_money(self, src, money): cursor = self.conn.cursor() try: sql = "select * from account where acctid = %s and money >= %s " %(src, money) cursor.execute(sql) print "has_enough_money: " + sql rs = cursor.fetchall() if len(rs) != 1: raise Exception("the account does not have enough money") finally: cursor.close() if __name__ == "__main__": source_acctid = sys.argv[1] target_acctid = sys.argv[2] money = sys.argv[3] conn = MySQLdb.connect( host = "127.0.0.1", user = '******', passwd = '******', port = 3306, db = '******' ) tr_money = TransferMoney(conn) try: tr_money.transfer(source_acctid, target_acctid, money) except Exception as e: print e finally: conn.close()
總結(jié)
通過(guò)對(duì)數(shù)據(jù)庫(kù)的操作就可以實(shí)現(xiàn)一個(gè)簡(jiǎn)單的銀行轉(zhuǎn)賬系統(tǒng),所以在系統(tǒng)開(kāi)發(fā)的時(shí)候,我們應(yīng)該盡最大的可能,讓整個(gè)系統(tǒng)不只是多個(gè)組件的拼接,應(yīng)該實(shí)現(xiàn)1+1>2。
相關(guān)文章
Python網(wǎng)絡(luò)爬蟲(chóng)中的同步與異步示例詳解
這篇文章主要給大家介紹了關(guān)于Python網(wǎng)絡(luò)爬蟲(chóng)中同步與異步的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2018-02-02python json.dumps中文亂碼問(wèn)題解決
這篇文章主要介紹了python json.dumps中文亂碼問(wèn)題解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04python正則表達(dá)式中匹配次數(shù)與貪心問(wèn)題詳解(+??*)
正則表達(dá)式是一個(gè)特殊的字符序列,它能幫助你方便的檢查一個(gè)字符串是否與某種模式匹配,下面這篇文章主要給大家介紹了關(guān)于python正則表達(dá)式中匹配次數(shù)與貪心問(wèn)題(+??*)的相關(guān)資料,需要的朋友可以參考下2022-10-10Python實(shí)現(xiàn)構(gòu)建一個(gè)儀表板的示例代碼
這篇文章主要為大家詳細(xì)介紹了Python如何實(shí)現(xiàn)構(gòu)建一個(gè)儀表板,文中的示例代碼講解詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴可以了解一下2023-03-03PyCharm在新窗口打開(kāi)項(xiàng)目的方法
今天小編就為大家分享一篇PyCharm在新窗口打開(kāi)項(xiàng)目的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-01-01Python切換pip源兩種方法(解決pip?install慢)
這篇文章主要給大家介紹了關(guān)于Python切換pip源兩種方法(解決pip?install慢),我總結(jié)的這幾種更換pip源的常用方式,希望可以幫助您成功配置國(guó)內(nèi)源,解決安裝Python包速度慢的問(wèn)題,需要的朋友可以參考下2023-11-11對(duì)python程序內(nèi)存泄漏調(diào)試的記錄
今天小編就為大家分享一篇對(duì)python程序內(nèi)存泄漏調(diào)試的記錄,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-06-06Python?seaborn?barplot畫(huà)圖案例
這篇文章主要介紹了Python?seaborn?barplot畫(huà)圖案例,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-07-07