在進(jìn)行一個(gè)應(yīng)用系統(tǒng)的開發(fā)過程中,從上到下一般需要四個(gè)構(gòu)件:客戶端-業(yè)務(wù)邏輯層-數(shù)據(jù)訪問層-數(shù)據(jù)庫,其中數(shù)據(jù)訪問層是一個(gè)底層、核心的技術(shù)。而且在實(shí)際開發(fā)中,數(shù)據(jù)庫的操作也就是說數(shù)據(jù)訪問層都是嵌套在其他語言中的,其是編程的核心。本文面向的是python語言,即通過python操作數(shù)據(jù)庫來實(shí)現(xiàn)簡(jiǎn)單的銀行轉(zhuǎn)賬操作。
工具
python提供了python DB API用來統(tǒng)一操作數(shù)據(jù)庫,使訪問數(shù)據(jù)庫的接口規(guī)范化,在沒有python DB API之前,接口程序十分混亂,不同的數(shù)據(jù)庫需要不同的操作接口,所以這個(gè)接口提供了極大的方便。在具體操作的時(shí)候,我們需要操作數(shù)據(jù)庫以及其他邏輯的python代碼,數(shù)據(jù)庫連接對(duì)象connection來建立連接,數(shù)據(jù)庫交互對(duì)象cursor來“運(yùn)送”數(shù)據(jù),一個(gè)健壯的系統(tǒng)必不可少的便是數(shù)據(jù)庫異常類Exceptions。整個(gè)訪問數(shù)據(jù)庫流程如下圖:
接下來分別介紹下兩個(gè)主要對(duì)象:
connection:數(shù)據(jù)庫連接對(duì)象,建立python客戶端與數(shù)據(jù)庫的網(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語句,將結(jié)果從數(shù)據(jù)庫獲取到客戶端
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ù)庫從一致性狀態(tài)變到另一個(gè)一致性狀態(tài)
隔離型:一個(gè)事務(wù)的執(zhí)行不被其他事務(wù)干擾
持久性:事務(wù)一旦提交,它對(duì)數(shù)據(jù)庫的改變就是持久性的
事務(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ù)庫如下,有兩個(gè)賬戶,分別擁有金錢110和10,在運(yùn)行代碼的時(shí)候在參數(shù)欄輸入1,2,100(source_acctid, target_acctid, tranfer_money)。
整個(gè)代碼的邏輯如下:首先連接數(shù)據(jù)庫,之后執(zhí)行邏輯,然后斷開數(shù)據(jù)庫連接,執(zhí)行的邏輯包括檢查轉(zhuǎn)賬雙方的賬戶是否有效,轉(zhuǎn)賬金額是否多于轉(zhuǎn)賬人的賬戶余額,分別給轉(zhuǎn)賬雙方的帳號(hào)金額發(fā)生變化。如果正常結(jié)束事務(wù),提交修改數(shù)據(jù)庫,否則回滾。
#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ù)庫的操作就可以實(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。
聲明:本網(wǎng)頁內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com