對象-關係映射(Object-Relational Mapping,簡稱ORM),面向對象的開發方法是當今企業級應用開發環境中的主流開發方法,關係數據庫是企業級應用環境中永久存放數據的主流數據存儲系統。對象和關係數據是業務實體的兩種表現形式,業務實體在內存中表現爲對象,在數據庫中表現爲關係數據。內存中的對象之間存在關聯和繼承關係,而在數據庫中,關係數據沒法直接表達多對多關聯和繼承關係。所以,對象-關係映射(ORM)系統通常以中間件的形式存在,主要實現程序對象到關係數據庫數據的映射。java
當咱們實現一個應用程序時(不使用O/R Mapping),咱們可能會寫特別多數據訪問層的代碼,從數據庫保存、刪除、讀取對象信息,而這些代碼都是重複的。而使用ORM則會大大減小重複性代碼。對象關係映射(Object Relational Mapping,簡稱ORM),主要實現程序對象到關係數據庫數據的映射。python
面向對象是從軟件工程基本原則(如耦合、聚合、封裝)的基礎上發展起來的,而關係數據庫則是從數學理論發展而來的,兩套理論存在顯著的區別。爲了解決這個不匹配的現象,對象關係映射技術應運而生。O/R中字母O起源於"對象"(Object),而R則來自於"關係"(Relational)。幾乎全部的程序裏面,都存在對象和關係數據庫。在業務邏輯層和用戶界面層中,咱們是面向對象的。當對象信息發生變化的時候,咱們須要把對象的信息保存在關係數據庫中。mysql
優勢:程序員
缺點:sql
sqlalchemy是python編程語言下的一款ORM框架,該框架創建在數據庫API上,使用關係對象映射進行數據庫操做,簡言之即是:將對象轉換成SQL,而後使用數據API執行SQL並獲取執行結果。數據庫
須要本身把數據庫中的表映射成類,而後才能經過對象的方式去調用。SQLAlchemy不止能夠支持MYSQL,還能夠支持Oracle等。express
Dialect用於和數據API進行交流,根據配置文件的不一樣調用不一樣的數據庫API,從而實現對數據庫的操做:編程
MySQL-Python mysql+mysqldb://<user>:<password>@<host>[:<port>]/<dbname> pymysql mysql+pymysql://<username>:<password>@<host>/<dbname>[?<options>] MySQL-Connector mysql+mysqlconnector://<user>:<password>@<host>[:<port>]/<dbname> cx_Oracle oracle+cx_oracle://user:pass@host:port/dbname[?key=value&key=value...]
案例:緩存
pip install SQLAlchemy
import sqlalchemy print([obj for obj in dir(sqlalchemy) if not obj.startswith("__")]) ['ARRAY', 'BIGINT', 'BINARY', 'BLANK_SCHEMA', 'BLOB', 'BOOLEAN', 'BigInteger', 'Binary', 'Boolean', 'CHAR', 'CLOB', 'CheckConstraint', 'Column', 'ColumnDefault', 'Constraint', 'DATE', 'DATETIME', 'DDL', 'DECIMAL', 'Date', 'DateTime', 'DefaultClause', 'Enum', 'FLOAT', 'FetchedValue', 'Float', 'ForeignKey', 'ForeignKeyConstraint', 'INT', 'INTEGER', 'Index', 'Integer', 'Interval', 'JSON', 'LargeBinary', 'MetaData', 'NCHAR', 'NUMERIC', 'NVARCHAR', 'Numeric', 'PassiveDefault', 'PickleType', 'PrimaryKeyConstraint', 'REAL', 'SMALLINT', 'Sequence', 'SmallInteger', 'String', 'TEXT', 'TIME', 'TIMESTAMP', 'Table', 'Text', 'ThreadLocalMetaData', 'Time', 'TypeDecorator', 'Unicode', 'UnicodeText', 'UniqueConstraint', 'VARBINARY', 'VARCHAR', 'alias', 'all_', 'and_', 'any_', 'asc', 'between', 'bindparam', 'case', 'cast', 'collate', 'column', 'cprocessors', 'create_engine', 'cresultproxy', 'cutils', 'delete', 'desc', 'dialects', 'distinct', 'engine', 'engine_from_config', 'event', 'events', 'exc', 'except_', 'except_all', 'exists', 'extract', 'false', 'func', 'funcfilter', 'insert', 'inspect', 'inspection', 'interfaces', 'intersect', 'intersect_all', 'join', 'lateral', 'literal', 'literal_column', 'log', 'modifier', 'not_', 'null', 'or_', 'outerjoin', 'outparam', 'over', 'pool', 'processors', 'schema', 'select', 'sql', 'subquery', 'table', 'tablesample', 'text', 'true', 'tuple_', 'type_coerce', 'types', 'union', 'union_all', 'update', 'util', 'within_group']
from sqlalchemy import create_engine #鏈接數據庫,生成engine對象;最大鏈接數爲5個 engine = create_engine("mysql+pymysql://root:root@127.0.0.1:3306/bigdata", max_overflow=5) print(engine) #Engine(mysql+pymysql://root:***@127.0.0.1:3306/bigdata) result = engine.execute('select * from table1') #不用commit(),會自動commit print(result.fetchall())
from sqlalchemy import Table, Column, Integer, String, MetaData metadata = MetaData() #至關於實例一個父類 user = Table('user', metadata, #至關於讓Table繼承metadata類 Column('id', Integer, primary_key=True), Column('name', String(20)), ) color = Table('color', metadata, #表名color Column('id', Integer, primary_key=True), Column('name', String(20)), ) metadata.create_all(engine) #table已經與metadate綁定
conn.execute解讀:session
def execute(self, object, *multiparams, **params): r"""Executes a SQL statement construct and returns a :class:`.ResultProxy`. :param object: The statement to be executed. May be one of: * a plain string * any :class:`.ClauseElement` construct that is also a subclass of :class:`.Executable`, such as a :func:`~.expression.select` construct * a :class:`.FunctionElement`, such as that generated by :data:`.func`, will be automatically wrapped in a SELECT statement, which is then executed. * a :class:`.DDLElement` object * a :class:`.DefaultGenerator` object * a :class:`.Compiled` object :param \*multiparams/\**params: represent bound parameter values to be used in the execution. Typically, the format is either a collection of one or more dictionaries passed to \*multiparams:: conn.execute( table.insert(), {"id":1, "value":"v1"}, {"id":2, "value":"v2"} ) ...or individual key/values interpreted by \**params:: conn.execute( table.insert(), id=1, value="v1" ) In the case that a plain SQL string is passed, and the underlying DBAPI accepts positional bind parameters, a collection of tuples or individual values in \*multiparams may be passed:: conn.execute( "INSERT INTO table (id, value) VALUES (?, ?)", (1, "v1"), (2, "v2") ) conn.execute( "INSERT INTO table (id, value) VALUES (?, ?)", 1, "v1" ) Note above, the usage of a question mark "?" or other symbol is contingent upon the "paramstyle" accepted by the DBAPI in use, which may be any of "qmark", "named", "pyformat", "format", "numeric". See `pep-249 <http://www.python.org/dev/peps/pep-0249/>`_ for details on paramstyle. To execute a textual SQL statement which uses bound parameters in a DBAPI-agnostic way, use the :func:`~.expression.text` construct. """
增
conn = engine.connect() conn.execute(user.insert(), {'id': 20, 'name': 'wqbin'}) conn.execute(user.insert(), {'id': 21, 'name': 'wang'}) conn.execute(user.insert(), {'id': 25, 'name': 'wangyang'}) conn.execute(user.insert(), { 'name': 'wangquincy'}) conn.close()
conn = engine.connect() conn.execute(user.delete().where(user.c.id== "21")) conn.close()
conn = engine.connect() # 將name=="wqbin"更改成"name=="wqbin123"··· conn.execute(user.update().where(user.c.name == 'wqbin').values(name='wqbin123')) conn.execute("""update user set name='wangyang123' where name ='wangyang' """) conn.close()
# 查詢 下面不能寫 sql = user.select... 會曝錯 sql = select([user, ]) sql = select([user.c.id, ]) sql = select([user.c.name, color.c.name]).where(user.c.id==25) sql = select([user.c.name]).order_by(user.c.name) sql = user.select([user]).group_by(user.c.name) result = conn.execute(sql) print(result.fetchall()) conn.close()
from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String from sqlalchemy.orm import sessionmaker Base = declarative_base() # 生成一個SqlORM基類(已經封裝metadata) # echo=True能夠查看建立表的過程 engine = create_engine("mysql+pymysql://root:root@localhost:3306/bigdata") class Host(Base): __tablename__ = 'hosts' # 表名爲host id = Column(Integer, primary_key=True, autoincrement=True) hostname = Column(String(64), unique=True, nullable=False) ip_addr = Column(String(128), unique=True, nullable=False) port = Column(Integer, default=22) Base.metadata.create_all(engine) # 建立全部表結構 if __name__ == '__main__': # 建立與數據庫的會話sessionclass,注意,這裏返回給session的是個class類,不是實例 SessionCls = sessionmaker(bind=engine) session = SessionCls() # 鏈接的實例 # 準備插入數據 h1 = Host(hostname='hadoop01', ip_addr='192.168.154.201') # 實例化(未建立) h2 = Host(hostname='hadoop02', ip_addr='192.168.154.202', port=24) h3 = Host(hostname='hadoop03', ip_addr='192.168.154.203', port=24) session.add(h1) #也能夠用下面的批量處理 session.add_all([h2,h3]) h2.hostname='hadoop021' #只要沒提交,此時修改也沒問題 # 查詢數據,返回一個對象 .first()返回一個 .all()返回全部 obj = session.query(Host).filter(Host.port == "24").first() print("-->", obj) obj=session.delete(obj) # 刪除行 print("-->", obj) session.commit() # 提交
engine = create_engine("mysql+pymysql://root:root@localhost:3306/bigdata", echo=True) class Host(Base): __tablename__ = 'hosts' #表名 id = Column(Integer, primary_key=True, autoincrement=True) #默認自增 hostname = Column(String(64), unique=True, nullable=False) ip_addr = Column(String(128), unique=True, nullable=False) port = Column(Integer, default=22) #外鍵關聯,主機與組名關聯,一個組對應多個主機 group_id = Column(Integer, ForeignKey("group.id")) class Group(Base): __tablename__ = "group" id = Column(Integer,primary_key=True) name = Column(String(64), unique=True, nullable=False) class Group2(Base): __tablename__ = "group2" id = Column(Integer,primary_key=True) name = Column(String(64), unique=True, nullable=False) Base.metadata.create_all(engine) # 建立全部表結構==>若是存在,不會報錯,也不會更新表結構 if __name__ == '__main__': # 建立與數據庫的會話session class ,注意,這裏返回給session的是個class,不是實例 SessionCls = sessionmaker(bind=engine) session = SessionCls() #鏈接的實例 session.commit() #提交
建立全部表結構==>若是存在,不會報錯,也不會更新表結構
==========刪除hosts從新運行======
from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String,ForeignKey from sqlalchemy.orm import sessionmaker,relationship Base = declarative_base() # 生成一個SqlORM 基類(已經封閉metadata) #echo=True能夠查看建立表的過程 engine = create_engine("mysql+pymysql://root:root@localhost:3306/bigdata", echo=True) class Host(Base): __tablename__ = 'hosts' #表名 id = Column(Integer, primary_key=True, autoincrement=True) #默認自增 hostname = Column(String(64), unique=True, nullable=False) ip_addr = Column(String(128), unique=True, nullable=False) port = Column(Integer, default=22) #外鍵關聯,主機與組名關聯,一個組對應多個主機 group_id = Column(Integer, ForeignKey("group.id")) class Group(Base): __tablename__ = "group" id = Column(Integer,primary_key=True) name = Column(String(64), unique=True, nullable=False) class Group2(Base): __tablename__ = "group2" id = Column(Integer,primary_key=True) name = Column(String(64), unique=True, nullable=False) Base.metadata.create_all(engine) # 建立全部表結構==>若是存在,不會報錯,也不會更新表結構 if __name__ == '__main__': # 建立與數據庫的會話session class ,注意,這裏返回給session的是個class,不是實例 SessionCls = sessionmaker(bind=engine) session = SessionCls() #鏈接的實例 h1 = Host(hostname='hadoop01', ip_addr='192.168.154.201') # 實例化(未建立) h2 = Host(hostname='hadoop02', ip_addr='192.168.154.202', port=24) h3 = Host(hostname='hadoop03', ip_addr='192.168.154.203', port=24) session.add_all([h1,h2,h3]) g1 = Group(name = "g1") g2 = Group(name = "g2") g3 = Group(name = "g3") g4 = Group(name = "g4") session.add_all([g1,g2,g3,g4]) session.query(Host).filter(Host.hostname=="hadoop02").update({"port":23,"group_id":1}) session.commit() #提交 session.close()
還發現一個問題,添加一個不存在值的外檢會報錯:
能夠獲取已經關聯的group_id後,但如何獲取已關聯的組的組名??
print(h.group.name) #AttributeError:'Host'object has no attribute 'group'
由於Host類根本就沒有group屬性!!
解決方法:
from sqlalchemy.orm import relationship #導入relationship class Host(Base): __tablename__ = 'hosts' #表名 id = Column(Integer, primary_key=True, autoincrement=True) #默認自增 hostname = Column(String(64), unique=True, nullable=False) ip_addr = Column(String(128), unique=True, nullable=False) port = Column(Integer, default=22) #外鍵關聯,主機與組名關聯 group_id = Column(Integer, ForeignKey("group.id")) group = relationship("Group")
那雙向關聯也要在Group類增長:hosts = relationship("Host")
可是也有隻用一句代碼就實現雙向關聯:
group=relationship("Group",backref="host_list")
其實還有聚合計算和多對多關聯,可是我認爲使用ORM操做這種計算過於複雜不如寫sql。。。。