Python 如何优雅的操作 PyMySQL

一、PyMysql

在使用Python操作MySQL数据过的过程中,基本的增删改查操作如何更加高效优雅的执行。这里将以PyMySQL为例,介绍一下如何使用Python操作数据库。 Python对MySQL数据库进行操作,基本思路是先连接数据库 Connection 对象,建立游标 Cursor 对象,然后执行SQL语句对数据库进行操作,获取执行结果,最终断开连接。大致过程是这样,在对其进行介绍之前,先介绍一些基本的概念。

Connection

Connection 对象即为数据库连接对象,在python中可以使用pymysql.connect()方法创建Connection对象,该方法的常用参数如下:

host:IP地址,字符串类型
user:用户名, 字符串类型
passwd:无默认值;字符串类
db:数据库名称,无默认值;字符串类型(可以不传但是SQL中必须体现)
port:端口, 默认为3306, 整型
charset:设置utf8, 字符串类型
close:关闭当前连接对象

Cursor

Cursor对象即为游标对象,用于执行查询和获取结果,在python中可以使用connect.cursor()创建

execute():执行数据库单个查询或命令,将结果从数据库获取
executemany(): 对一个查询运行多个数据,其返回是:受影响的行数(如果有的话)
close():关闭当前游标对象

Transaction

1.事务是数据库理论中一个比较重要的概念,指访问和更新数据库的一个程序执行单元,具有ACID特性:

原子性(Atomic):事务中的各项操作要么全都做,要么全都不做,任何一项操作的失败都会导致整个事务的失败
一致性(Consistent):事务必须使数据库从一个一致性状态变到另一个一致性状态
隔离性(Isolated):并发执行的事务彼此无法看到对方的中间状态,一个事务的执行不能被其他事务干扰
持久性(Durable):事务一旦提交,它对数据库的改变就是永久性的,可以通过日志和同步备份在故障发生后重建数据。

2.常用事务方法

Connection.commit():正常事务提交
Connection.rollback():事务异常回滚
Connection.autocommit():事务自动提交机制,默认TRUE,设置FALSE则关闭。

二、Python操作MySQL

1.安装

$ pip3 install PyMySQL

2.数据库连接
        import pymysql
 
        # 打开数据库连接
        db = pymysql.connect(host='127.0.0.1',
                             user='user',
                             password='123456',
                             database='demo',
                             port=3306,
                             charset='utf8')
         
        # 使用 cursor() 方法创建一个游标对象 cursor
        cursor = db.cursor()
         
        # 使用 execute()  方法执行 SQL 查询 
        cursor.execute("SELECT * FROM USER;")
         
        # 使用 fetchone() 方法获取单条数据.
        data = cursor.fetchone()
         
        print ("Database data : %s " % data)
         
        # 关闭数据库连接
        cursor.close()
        db.close()
    
3.数据库DML操作
事务执行过程
        import pymysql
 
        # 打开数据库连接
        db = pymysql.connect(**config) # 省略连接信息
         
        # 使用cursor()方法获取操作游标 
        cursor = db.cursor()
         
        # SQL插入语句
        sql = """INSERT INTO EMPLOYEE(FIRST_NAME,
                 LAST_NAME, AGE, SEX, INCOME)
                 VALUES ('Mac', 'Mohan', 20, 'M', 2000)"""
        try:
           # 执行sql语句
           cursor.execute(sql)
           
           # 提交到数据库执行
           db.commit()
        except:
           # 如果发生错误则回滚
           db.rollback()
         
        # 关闭当前游标对象
        cursor.close()
        # 关闭数据库连接
        db.close()
        
        # 执行传入的SQL也可以更加的灵活,可以使用另外一种%s 占位符,后续的参数依次传入。
        sql2 = """INSERT INTO EMPLOYEE(FIRST_NAME,
                 LAST_NAME, AGE, SEX, INCOME)
                 VALUES ('%s', '%s', %s, '%s', %s)"""
        cursor.execute(sql2, 'Mac', 'Mohan', 20, 'M', 2000)
4.数据库DQL操作

Python查询Mysql使用常用几个方法。

fetchone(): 该方法获取下一个查询结果集。结果集是一个对象.
fetchmany():获取结果集的指定几行.
fetchall(): 接收全部的返回结果行.
rowcount: 这是一个只读属性,并返回执行execute()方法后影响的行数。

        import pymysql
         
        # 打开数据库连接
        db = pymysql.connect(**config)
         
        # 使用cursor()方法获取操作游标 
        cursor = db.cursor()
         
        # SQL 查询语句
        sql = "SELECT * FROM EMPLOYEE WHERE INCOME > %s" % (1000)
        try:
           # 执行SQL语句
           cursor.execute(sql)
           # 获取所有记录列表
           result1=cursor.fetchone()
           result2=cursor.fetchmany(2)
           results = cursor.fetchall()
           print(result1)
           print(result2)
           print(results)
        except:
           print ("Error: unable to fetch data")
         
        # 关闭数据库连接
        cursor.close()
        db.close()

三、工具类封装

通过封装常用方法将会大大降低对数据库操作的成本。接下来分为几步进行操作:
1.可以通过env文件来存储数据库的连接信息
2.将env文件数据加载进系统环境变量
3.从系统环境变量中获取对应连接数据
4.连接数据库,操作增删改查

# .env
DB_INFO={"host": "127.0.0.1","port":3306,"user": "user","passwd": "123456","charset": "utf8"}
import io
import os


class EnvironmentVarUtils(object):
    def __init__(self, fileName=None):
        self.file_name = fileName
        self._load_dot_env_file(self._get_environment_path())

    def _get_environment_path(self):
        """
        :return: project_path
        """
        return os.path.join(os.path.dirname(os.getcwd()), '.env') if self.file_name is None\
            else os.path.join(os.path.dirname(os.getcwd()), self.file_name)

    def _load_dot_env_file(self, dot_env_path):
        """ load .env file.
        Args:
            dot_env_path (str): .env file path
        """
        if not os.path.isfile(dot_env_path):
            raise FileNotFoundError(".env file not found Error.")

        print("Loading environment variables from 【{}】".format(dot_env_path))
        env_variables_mapping = {}

        with io.open(dot_env_path, 'r', encoding='utf-8') as fp:
            for line in fp:
                if "=" in line:
                    variable, value = line.split("=", 1)
                else:
                    raise Exception(".env format error")

                env_variables_mapping[variable.strip()] = value.strip()
        self._set_os_environ(env_variables_mapping)

    @staticmethod
    def _set_os_environ(variables_mapping):
        """ set variables mapping to os.environ """
        for variable in variables_mapping:
            os.environ[variable] = variables_mapping[variable]
            print("Set OS environment variable: {}".format(variable))

    @staticmethod
    def get_os_environ(variable_name):
        """ get value of environment variable.
        """
        try:
            return os.environ[variable_name]
        except Exception as e:
            raise e
import pymysql


class SqlHelper(object):
    def __init__(self, config):
        self.connect = pymysql.connect(**eval(config))
        self.connect.autocommit(True)
        # default return tuple, DictCursor return Json .
        self.cursor = self.connect.cursor()

    def __enter__(self):
        # DictCursor return Json .
        self.cursor = self.connect.cursor(cursor=pymysql.cursors.DictCursor)
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.cursor.close()
        self.connect.close()

    def queryAll(self, sql, params=None):
        """
        :param sql:
        :param params:
        :return:
        """
        self.cursor.execute(sql, params)
        return self.cursor.fetchall()

    def queryMany(self, sql, num, params=None):
        """
        :param sql:
        :param num:
        :param params:
        :return:
        """
        self.cursor.execute(sql, params)
        return self.cursor.fetchmany(num)

    def queryOne(self, sql, params=None):
        """
        :param sql:
        :param params:
        :return:
        """
        self.cursor.execute(sql, params)
        return self.cursor.fetchone()

    def operation(self, sql, params=None, DML=True):
        """
        DML: insert / update / delete
        DDL: CREATE TABLE/VIEW/INDEX/SYN/CLUSTER
        :param DML:
        :param sql:
        :param params:
        :return:
        """
        try:
            self.cursor.execute(sql, params)
        except Exception as e:
            if DML:
                self.connect.rollback()
            raise e

    def batch_operation(self, sql_list, params_list=None, DML=True):
        """
            Process multiple SQL files in batches .
        :param DML:
        :param sql_list:
        :param params_list:
        :return:
        """
        for i in range(len(sql_list)):
            try:
                if params_list is not None:
                    self.operation(sql_list[i], params_list[i], DML)
                else:
                    self.operation(sql_list[i], params_list, DML)
            except Exception as e:
                raise e


    def batch_processing(self, sql, params_list, DML=True):
        """
         The same SQL is executed multiple times in batches.
        :param DML:
        :param sql:
        :param params_list:
        :return:
        """
        try:
            self.cursor.executemany(sql, params_list)
        except Exception as e:
            if DML:
                self.connect.rollback()
            raise e

    def __del__(self):
        """
            Automatic disconnection
        :return:
        """
        if self.connect.open:  # 解决连接重复关闭的
            self.cursor.close()
            self.connect.close()

1.正常方式执行

if __name__ == '__main__':
    sql = "select age from `demo`.`user` where name= 'Amy';"
    env = EnvironmentVarUtils()  # 初始化对象加载env文件数据
    config = env.get_os_environ("DB_INFO")  # 获取指定key数据

    # 1.正常方式执行
    db = SqlHelper(config)
    result = db.queryOne(sql)
    print(result)

-------------------------------------------------------------------------------

控制台输出:

Loading environment variables from 【C:\Users\lenovo\PycharmProjects\job\httpRunner_demo\.env】
Set OS environment variable: USERNAME
Set OS environment variable: PASSWORD
Set OS environment variable: BASE_URL
Set OS environment variable: db_info
(18,)

Process finished with exit code 0

2.通过上下文管理的方式执行

if __name__ == '__main__':
    sql = "select age from `demo`.`user` where name= 'Amy';"
    env = EnvironmentVarUtils()  # 初始化对象加载env文件数据
    config = env.get_os_environ("DB_INFO")  # 获取指定key数据

    # 2.上下文管理方式执行
    with SqlHelper(config) as db:
        result = db.queryOne(sql)
        print(result)

-------------------------------------------------------------------------------

控制台输出:

Loading environment variables from 【C:\Users\lenovo\PycharmProjects\job\httpRunner_demo\.env】
Set OS environment variable: USERNAME
Set OS environment variable: PASSWORD
Set OS environment variable: BASE_URL
Set OS environment variable: db_info
{'age': 18}

Process finished with exit code 0

以上亲测可用,可以尝试在自动化项目实践中操作。有问题欢迎留言讨论。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,636评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,890评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,680评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,766评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,665评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,045评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,515评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,182评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,334评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,274评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,319评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,002评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,599评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,675评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,917评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,309评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,885评论 2 341

推荐阅读更多精彩内容