基于Python實(shí)現(xiàn)一個(gè)簡(jiǎn)單的銀行轉(zhuǎn)賬操作
前言
在進(jìn)行一個(gè)應(yīng)用系統(tǒng)的開發(fā)過程中,從上到下一般需要四個(gè)構(gòu)件:客戶端-業(yè)務(wù)邏輯層-數(shù)據(jù)訪問層-數(shù)據(jù)庫(kù),其中數(shù)據(jù)訪問層是一個(gè)底層、核心的技術(shù)。而且在實(shí)際開發(fā)中,數(shù)據(jù)庫(kù)的操作也就是說數(shù)據(jù)訪問層都是嵌套在其他語(yǔ)言中的,其是編程的核心。本文面向的是python語(yǔ)言,即通過python操作數(shù)據(jù)庫(kù)來(lái)實(shí)現(xiàn)簡(jiǎn)單的銀行轉(zhuǎn)賬操作。
工具
python提供了python DB API用來(lái)統(tǒng)一操作數(shù)據(jù)庫(kù),使訪問數(shù)據(jù)庫(kù)的接口規(guī)范化,在沒有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è)訪問數(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ù)呢?他是訪問和更新數(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)
在開發(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沒加,也不能B加了A還沒有減,當(dāng)然賬戶必須是有效的,M錢的金額肯定要大于A賬戶上的金額。所以在具體設(shè)計(jì)的時(shí)候,需要將A賬戶的金錢減少和B賬戶的金錢增加作為一個(gè)事務(wù),要么同時(shí)成功,要么一起失敗。按照這個(gè)需求,書寫代碼,詳細(xì)代碼見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í)行邏輯,然后斷開數(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é)
通過對(duì)數(shù)據(jù)庫(kù)的操作就可以實(shí)現(xiàn)一個(gè)簡(jiǎn)單的銀行轉(zhuǎn)賬系統(tǒng),所以在系統(tǒng)開發(fā)的時(shí)候,我們應(yīng)該盡最大的可能,讓整個(gè)系統(tǒng)不只是多個(gè)組件的拼接,應(yīng)該實(shí)現(xiàn)1+1>2。
相關(guān)文章
Python網(wǎng)絡(luò)爬蟲中的同步與異步示例詳解
這篇文章主要給大家介紹了關(guān)于Python網(wǎng)絡(luò)爬蟲中同步與異步的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2018-02-02
python正則表達(dá)式中匹配次數(shù)與貪心問題詳解(+??*)
正則表達(dá)式是一個(gè)特殊的字符序列,它能幫助你方便的檢查一個(gè)字符串是否與某種模式匹配,下面這篇文章主要給大家介紹了關(guān)于python正則表達(dá)式中匹配次數(shù)與貪心問題(+??*)的相關(guān)資料,需要的朋友可以參考下2022-10-10
Python實(shí)現(xiàn)構(gòu)建一個(gè)儀表板的示例代碼
這篇文章主要為大家詳細(xì)介紹了Python如何實(shí)現(xiàn)構(gòu)建一個(gè)儀表板,文中的示例代碼講解詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴可以了解一下2023-03-03
Python切換pip源兩種方法(解決pip?install慢)
這篇文章主要給大家介紹了關(guān)于Python切換pip源兩種方法(解決pip?install慢),我總結(jié)的這幾種更換pip源的常用方式,希望可以幫助您成功配置國(guó)內(nèi)源,解決安裝Python包速度慢的問題,需要的朋友可以參考下2023-11-11
對(duì)python程序內(nèi)存泄漏調(diào)試的記錄
今天小編就為大家分享一篇對(duì)python程序內(nèi)存泄漏調(diào)試的記錄,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來(lái)看看吧2018-06-06

