對象-關係映射(Object/Relation Mapping,簡稱ORM),是隨着面向對象的軟件開發方法發展而產生的。面向對象的開發方法是當今企業級應用開發環境中的主流開發方法,關係數據庫是企業級應用環境中永久存放數據的主流數據存儲系統。對象和關係數據是業務實體的兩種表現形式,業務實體在內存中表現爲對象,在數據庫中表現爲關係數據。內存中的對象之間存在關聯和繼承關係,而在數據庫中,關係數據沒法直接表達多對多關聯和繼承關係。所以,對象-關係映射(ORM)系統通常以中間件的形式存在,主要實現程序對象到關係數據庫數據的映射。node
面向對象是從軟件工程基本原則(如耦合、聚合、封裝)的基礎上發展起來的,而關係數據庫則是從數學理論發展而來的,兩套理論存在顯著的區別。爲了解決這個不匹配的現象,對象關係映射技術應運而生。O/R中字母O起源於"對象"(Object),而R則來自於"關係"(Relational)。幾乎全部的程序裏面,都存在對象和關係數據庫。在業務邏輯層和用戶界面層中,咱們是面向對象的。當對象信息發生變化的時候,咱們須要把對象的信息保存在關係數據庫中。python
當開發一個應用程序的時候(不使用O/R Mapping),可能會寫很多數據訪問層的代碼,用來從數據庫保存,刪除,讀取對象信息,等等。在DAL中寫了不少的方法來讀取對象數據,改變狀態對象等等任務。而這些代碼寫起來老是重複的。mysql
若是開你最近的程序,看看DAL代碼,確定會看到不少近似的通用的模式。咱們以保存對象的方法爲例,傳入一個對象,爲SqlCommand對象添加SqlParameter,把全部屬性和對象對應,設置SqlCommand的CommandText屬性爲存儲過程,而後運行SqlCommand。對於每一個對象都要重複的寫這些代碼。 除此以外,還有更好的辦法嗎?有,引入一個O/R Mapping。實質上,一個O/R Mapping會爲你生成DAL。與其本身寫DAL代碼,不如用O/R Mapping。用O/R Mapping保存,刪除,讀取對象,O/R Mapping負責生成SQL,你只須要關心對象就好。對象關係映射成功運用在不一樣的面向對象持久層產品中,sql
•ORM:及Object-Relational Mapping,把關係數據庫的表結構映射到對象上數據庫
•咱們先來可能一個例子:數組
•若是咱們從數據庫查出來幾條數據,須要你在python中表示出來,若是你沒有接觸過ORM技術,你或許會使用下面的形式來存儲這個數據:安全
[ (1, ‘feng’), (2, ‘shang’), (3, ‘huo’), ]
若是你想知道表結構是什麼樣的,是否是就費勁了,若是你想快速的取出其中的元素,就須要聽聽ORM的思想了。bash
數據庫中每次查出來的數據都用一個類表示,這個類的屬性和數據庫中表的字段一一對應。多條數據,就是一個list,每一行數據都是一個類來表示,以下所示:session
class User(object): def __init__(self, id, name): self.id = id self.name = name [ User(1, 「feng」), User(2, 「shang」), User(3, 「huo」), ]
當咱們須要得到id,或者name的時候,只須要經過循環獲取到對象,直接經過user1.id或者user1.name就能夠獲取到id和name的屬性。而且使得數據的存取很是的規範,這樣ORM架構應用而生。架構
Python中最有名的ORM架構就是SQLAlchemy,咱們主要就是來學習SQLAlchemy的使用
pip install SQLAlchemy
yum install mysql-server mysql service mysqld restart sysctmctl restart mysql.service
create database sqlalchemy;
GRANT ALL PRIVILEGES ON *.* TO 'fxq'@'%' IDENTIFIED BY ‘123456’;
from sqlalchemy import create_engine engine = create_engine('mysql://fxq:123456@192.168.100.101/sqlalchemy', echo=True)
echo參數爲True時,會顯示每條執行的SQL語句,能夠關閉,
create_engine()返回一個Engine的實例,而且它表示經過數據庫語法處理細節的核心接口,在這種狀況下,數據庫語法將會被解釋成python的類方法。
解釋說明:
mysql://fxq:123456@192.168.100.101/sqlalchemy
mysql: 指定是哪一種數據庫鏈接
第一個fxq: 用戶名
123456: fxq用戶對應的密碼
192.168.100.101: 數據庫的ip
sqlalchemy: 數據庫須要鏈接庫的名字
from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker sql = '''create table student( id int not null primary key, name varchar(50), age int, address varchar(100)); ''' engine = create_engine('mysql+pymysql://fxq:123456@192.168.100.101/sqlalchemy') conn = engine.connect() conn.execute(sql) engine.connect() #表示獲取到數據庫鏈接。相似咱們在MySQLdb中游標course的做用。
#!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2018/5/10 21:58 # @Author : Feng Xiaoqing # @File : demo2.py # @Function: ----------- from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String engine = create_engine('mysql+pymysql://fxq:123456@192.168.100.101/sqlalchemy') metadata = MetaData(engine) student = Table('student', metadata, Column('id', Integer, primary_key=True), Column('name', String(50), ), Column('age', Integer), Column('address', String(10)), ) metadata.create_all(engine)
以上方式均可以建立數據庫表
MetaData類主要用於保存表結構,鏈接字符串等數據,是一個多表共享的對象
metadata = MetaData(engine) #綁定一個數據源的metadata
metadata.create_all(engine) #是來建立表,這個操做是安全的操做,會先判斷表是否存在。
構造函數:
Table.__init__(self, name, metadata,*args, **kwargs)
name 表名
metadata 共享的元數據
*args Column 是列定義,詳見下一節
下面是可變參數 **kwargs 定義
schema 此表的結構名稱,默認None
autoload 自動從現有表中讀入表結構,默認False
autoload_with 從其餘engine讀取結構,默認None
include_columns 若是autoload設置爲True,則此項數組中的列明將被引用,沒有寫的列明將被忽略,None表示全部都列明都引用,默認None
mustexist 若是爲True,表示這個表必須在其餘的python應用中定義,必須是metadata的一部分,默認False
useexisting 若是爲True,表示這個表必須被其餘應用定義過,將忽略結構定義,默認False
owner 表全部者,用於Orcal,默認None
quote 設置爲True,若是代表是SQL關鍵字,將強制轉義,默認False
quote_schema 設置爲True,若是列明是SQL關鍵字,將強制轉義,默認False
mysql_engine mysql專用,能夠設置'InnoDB'或'MyISAM'
構造函數:
Column.__init__(self, name, type_, *args, **kwargs)
一、name 列名
二、type_ 類型,更多類型 sqlalchemy.types
三、*args Constraint(約束), ForeignKey(外鍵), ColumnDefault(默認), Sequenceobjects(序列)定義
四、key 列名的別名,默認None
下面是可變參數 **kwargs
五、primary_key 若是爲True,則是主鍵
六、nullable 是否可爲Null,默認是True
七、default 默認值,默認是None
八、index 是不是索引,默認是True
九、unique 是否惟一鍵,默認是False
十、onupdate 指定一個更新時候的值,這個操做是定義在SQLAlchemy中,不是在數據庫裏的,當更新一條數據時設置,大部分用於updateTime這類字段
十一、autoincrement 設置爲整型自動增加,只有沒有默認值,而且是Integer類型,默認是True
十二、quote 若是列明是關鍵字,則強制轉義,默認False
說到數據庫,就離不開Session。Session的主要目的是創建與數據庫的會話,它維護你加載和關聯的全部數據庫對象。它是數據庫查詢(Query)的一個入口。
在Sqlalchemy中,數據庫的查詢操做是經過Query對象來實現的。而Session提供了建立Query對象的接口。
Query對象返回的結果是一組同一映射(Identity Map)對象組成的集合。事實上,集合中的一個對象,對應於數據庫表中的一行(即一條記錄)。所謂同一映射,是指每一個對象有一個惟一的ID。若是兩個對象(的引用)ID相同,則認爲它們對應的是相同的對象。
要完成數據庫查詢,就須要創建與數據庫的鏈接。這就須要用到Engine對象。一個Engine多是關聯一個Session對象,也可能關聯一個數據庫表。
固然Session最重要的功能仍是實現原子操做。
ORM經過session與數據庫創建鏈接進行通訊,以下所示:
from sqlalchemy.orm import sessionmaker DBSession = sessionmaker(bind=engine) session = DBSession()
經過sessionmake方法建立一個Session工廠,而後在調用工廠的方法來實例化一個Session對象。
#!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2018/5/10 20:58 # @Author : Feng Xiaoqing # @File : demo1.py # @Function: ----------- from sqlalchemy import create_engine, Column, Integer, String from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker engine = create_engine('mysql+pymysql://fxq:123456@192.168.100.101/sqlalchemy') DBsession = sessionmaker(bind=engine) session = DBsession() Base = declarative_base() class Student(Base): __tablename__ = 'student' id = Column(Integer, primary_key=True) name = Column(String(100)) age = Column(Integer) address = Column(String(100)) student1 = Student(id=1001, name='ling', age=25, address="beijing") student2 = Student(id=1002, name='molin', age=18, address="jiangxi") student3 = Student(id=1003, name='karl', age=16, address="suzhou") session.add_all([student1, student2, student3]) session.commit() session.close()
查詢是這個裏面最爲複雜,最爲繁瑣的一個步驟。
經過Session的query()方法建立一個查詢對象。這個函數的參數數量是可變的,參數能夠是任何類或者是類的描述的集合。下面來看一個例子:
#!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2018/5/11 21:49 # @Author : Feng Xiaoqing # @File : search.py # @Function: ----------- from sqlalchemy import Column from sqlalchemy import Integer from sqlalchemy import String from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker Base = declarative_base() class Student(Base): __tablename__ = 'student' id = Column(Integer, primary_key=True) name = Column(String(50)) age = Column(Integer) address = Column(String(100)) engine = create_engine('mysql+pymysql://fxq:123456@192.168.100.101/sqlalchemy') DBSession = sessionmaker(bind=engine) session = DBSession() my_stdent = session.query(Student).filter_by(name="fengxiaoqing2").first() print(my_stdent)
此時咱們看到的輸出結果是這樣的:
<__main__.Student object at 0x032745F0>
前面咱們在賦值的時候,咱們能夠經過實例化一個對象,而後直接映射到數據庫中,那咱們在查詢出來的數據sqlalchemy直接給映射成一個對象了(或者是每一個元素爲這種對象的列表),對象和咱們建立表時候的class是一致的,咱們就也能夠直接經過對象的屬性就能夠直接調用就能夠了。
#!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2018/5/11 21:49 # @Author : Feng Xiaoqing # @File : search.py # @Function: ----------- from sqlalchemy import Column from sqlalchemy import Integer from sqlalchemy import String from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker Base = declarative_base() class Student(Base): __tablename__ = 'student' id = Column(Integer, primary_key=True) name = Column(String(50)) age = Column(Integer) address = Column(String(100)) engine = create_engine('mysql+pymysql://fxq:123456@192.168.100.101/sqlalchemy') DBSession = sessionmaker(bind=engine) session = DBSession() my_stdent = session.query(Student).filter_by(name="fengxiaoqing2").first() print(my_stdent.id,my_stdent.name,my_stdent.age,my_stdent.address)
結果:
1000311 fengxiaoqing2 182 chengde
#!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2018/5/11 21:49 # @Author : Feng Xiaoqing # @File : search.py # @Function: ----------- from sqlalchemy import Column from sqlalchemy import Integer from sqlalchemy import String from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker Base = declarative_base() class Student(Base): __tablename__ = 'student' id = Column(Integer, primary_key=True) name = Column(String(50)) age = Column(Integer) address = Column(String(100)) engine = create_engine('mysql+pymysql://fxq:123456@192.168.100.101/sqlalchemy') DBSession = sessionmaker(bind=engine) session = DBSession() my_stdent = session.query(Student).filter(Student.name.like("%feng%")) print(my_stdent)
結果:
SELECT student.id AS student_id, student.name AS student_name, student.age AS student_age, student.address AS student_address FROM student WHERE student.name LIKE %s
根據結果,咱們能夠看出來
filter_by最後的結果就是一個sql語句,咱們排錯的時候就能夠經過這個來排查咱們sql是否正確。
如下的這些過濾操做均可以在filter函數中使用:
equals: query(Student).filter(Student.id == 10001) not equals: query(Student).filter(Student.id != 100) LIKE: query(Student).filter(Student.name.like(「%feng%」)) IN: query(Student).filter(Student.name.in_(['feng', 'xiao', 'qing'])) not in query(Student).filter(~Student.name.in_(['feng', 'xiao', 'qing'])) AND: from sqlalchemy import and_ query(Student).filter(and_(Student.name == 'fengxiaoqing', Student.id ==10001)) 或者 query(Student).filter(Student.name == 'fengxiaoqing').filter(Student.address == 'chengde') OR: from sqlalchemy import or_ query.filter(or_(Student.name == 'fengxiaoqing', Student.age ==18))
all() 返回一個列表
#!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2018/5/11 21:49 # @Author : Feng Xiaoqing # @File : search.py # @Function: ----------- from sqlalchemy import Column from sqlalchemy import Integer from sqlalchemy import String from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker Base = declarative_base() class Student(Base): __tablename__ = 'student' id = Column(Integer, primary_key=True) name = Column(String(50)) age = Column(Integer) address = Column(String(100)) engine = create_engine('mysql+pymysql://fxq:123456@192.168.100.101/sqlalchemy') DBSession = sessionmaker(bind=engine) session = DBSession() my_stdent = session.query(Student).filter(Student.name.like("%feng%")).all() print(my_stdent)
結果:
[<__main__.Student object at 0x031405B0>, <__main__.Student object at 0x030FCA70>, <__main__.Student object at 0x031405F0>]
能夠經過遍歷列表來獲取每一個對象。
one() 返回且僅返回一個查詢結果。當結果的數量不足一個或者多於一個時會報錯。
把上面的all改爲one就報錯了。
first() 返回至多一個結果,並且以單項形式,而不是隻有一個元素的tuple形式返回這個結果.
#!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2018/5/11 21:49 # @Author : Feng Xiaoqing # @File : search.py # @Function: ----------- from sqlalchemy import Column from sqlalchemy import Integer from sqlalchemy import String from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker Base = declarative_base() class Student(Base): __tablename__ = 'student' id = Column(Integer, primary_key=True) name = Column(String(50)) age = Column(Integer) address = Column(String(100)) engine = create_engine('mysql+pymysql://fxq:123456@192.168.100.101/sqlalchemy') DBSession = sessionmaker(bind=engine) session = DBSession() my_stdent = session.query(Student).filter(Student.name.like("%feng%")).first() print(my_stdent)
結果:
<__main__.Student object at 0x030A3610>
Filter: 能夠像寫 sql 的 where 條件那樣寫 > < 等條件,但引用列名時,須要經過 類名.屬性名 的方式。
filter_by: 可使用 python 的正常參數傳遞方法傳遞條件,指定列名時,不須要額外指定類名。,參數名對應名類中的屬性名,但彷佛不能使用 > < 等條件。
當使用filter的時候條件之間是使用「==",fitler_by使用的是"="。
user1 = session.query(User).filter_by(id=1).first() user1 = session.query(User).filter(User.id==1).first()
filter不支持組合查詢,只能連續調用filter來變相實現。
而filter_by的參數是**kwargs,直接支持組合查詢。
好比:
q = sess.query(IS).filter(IS.node == node and IS.password == password).all()
更新就是查出來,直接更改就能夠了
#!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2018/5/11 22:27 # @Author : Feng Xiaoqing # @File : update.py # @Function: ----------- from sqlalchemy import Column from sqlalchemy import Integer from sqlalchemy import String from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker Base = declarative_base() class Student(Base): __tablename__ = 'student' id = Column(Integer, primary_key=True) name = Column(String(50)) age = Column(Integer) address = Column(String(100)) engine = create_engine('mysql+pymysql://fxq:123456@192.168.100.101/sqlalchemy') DBSession = sessionmaker(bind=engine) session = DBSession() my_stdent = session.query(Student).filter(Student.id == 1002).first() my_stdent.name = "fengxiaoqing" my_stdent.address = "chengde" session.commit() student1 = session.query(Student).filter(Student.id == 1002).first() print(student1.name, student1.address)
結果:
MariaDB [sqlalchemy]> select * from student; +---------+---------------+------+---------+ | id | name | age | address | +---------+---------------+------+---------+ | 1002 | molin | 18 | jiangxi | | 1003 | karl | 16 | suzhou | | 100011 | fengxiaoqing | 18 | chengde | | 100021 | fengxiaqing1 | 181 | chengde | | 1000111 | fengxiaoqing | 18 | chengde | | 1000211 | fengxiaqing1 | 181 | chengde | | 1000311 | fengxiaoqing2 | 182 | chengde | +---------+---------------+------+---------+ 7 rows in set (0.00 sec) MariaDB [sqlalchemy]> select * from student; +---------+---------------+------+---------+ | id | name | age | address | +---------+---------------+------+---------+ | 1002 | fengxiaoqing | 18 | chengde | | 1003 | karl | 16 | suzhou | | 100011 | fengxiaoqing | 18 | chengde | | 100021 | fengxiaqing1 | 181 | chengde | | 1000111 | fengxiaoqing | 18 | chengde | | 1000211 | fengxiaqing1 | 181 | chengde | | 1000311 | fengxiaoqing2 | 182 | chengde | +---------+---------------+------+---------+ 7 rows in set (0.00 sec) MariaDB [sqlalchemy]>
刪除其實也是跟查詢相關的,直接查出來,調用delete()方法直接就能夠刪除掉。
#!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2018/5/11 22:27 # @Author : Feng Xiaoqing # @File : update.py # @Function: ----------- from sqlalchemy import Column from sqlalchemy import Integer from sqlalchemy import String from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker Base = declarative_base() class Student(Base): __tablename__ = 'student' id = Column(Integer, primary_key=True) name = Column(String(50)) age = Column(Integer) address = Column(String(100)) engine = create_engine('mysql+pymysql://fxq:123456@192.168.100.101/sqlalchemy') DBSession = sessionmaker(bind=engine) session = DBSession() session.query(Student).filter(Student.id == 1001).delete() session.commit() session.close()
統計count()
#!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2018/5/11 22:27 # @Author : Feng Xiaoqing # @File : update.py # @Function: ----------- from sqlalchemy import Column from sqlalchemy import Integer from sqlalchemy import String from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker Base = declarative_base() class Student(Base): __tablename__ = 'student' id = Column(Integer, primary_key=True) name = Column(String(50)) age = Column(Integer) address = Column(String(100)) engine = create_engine('mysql+pymysql://fxq:123456@192.168.100.101/sqlalchemy') DBSession = sessionmaker(bind=engine) session = DBSession() print(session.query(Student).filter(Student.name.like("%feng%")).count())
分組 group_by()
#!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2018/5/11 22:27 # @Author : Feng Xiaoqing # @File : update.py # @Function: ----------- from sqlalchemy import Column from sqlalchemy import Integer from sqlalchemy import String from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker Base = declarative_base() class Student(Base): __tablename__ = 'student' id = Column(Integer, primary_key=True) name = Column(String(50)) age = Column(Integer) address = Column(String(100)) engine = create_engine('mysql+pymysql://fxq:123456@192.168.100.101/sqlalchemy') DBSession = sessionmaker(bind=engine) session = DBSession() std_group_by = session.query(Student).group_by(Student.age) print(std_group_by)
結果的sql語句以下:
SELECT student.id AS student_id, student.name AS student_name, student.age AS student_age, student.address AS student_address FROM student GROUP BY student.age
排序 order_by() 反序在order_by裏面用desc()方法
#!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2018/5/11 22:27 # @Author : Feng Xiaoqing # @File : update.py # @Function: ----------- from sqlalchemy import Column from sqlalchemy import Integer from sqlalchemy import String from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker Base = declarative_base() class Student(Base): __tablename__ = 'student' id = Column(Integer, primary_key=True) name = Column(String(50)) age = Column(Integer) address = Column(String(100)) engine = create_engine('mysql+pymysql://fxq:123456@192.168.100.101/sqlalchemy') DBSession = sessionmaker(bind=engine) session = DBSession() std_ord_desc = session.query(Student).filter(Student.name.like("%feng%")).order_by(Student.id.desc()).all() for i in std_ord_desc: print(i.id)
結果:
1000311 1000211 1000111 100021 100011 1002