SQL-Alchemy 的使用
SQL-Alchemy
SQLAlchemy是Python编程语言下的一款ORM框架,该框架建立在数据库API之上,使用关系对象映射进行数据库操作,简言之便是:将对象转换成SQL,然后使用数据API执行SQL并获取执行结果.
参考文献:https://www.cnblogs.com/wupeiqi/articles/8259356.html
1.安装介绍
# 安装 pip install sqlalchemy # 操作mysql 的时候依赖pymysql pip install pymysql
组成部分:
- Engine:框架的引擎。
- Connection Pooling:数据库连接池。
- Dialect:选择连接数据库的DB API 种类。
- Schema/Types:架构和类型。
- SQL Exprression Language:SQL表达式语言。
SQLAlchemy 本身无法操作数据库,其必须以pymysql
等第三方插件,Dialect用于和数据API进行交流, 根据配置文件的不同调用不同的数据库 API,从而实现对数据库的操作,
pymysql mysql+pymysql://<username>:<``password``>@<host>/<dbname>[?<options>] cx_Oracle oracle+cx_oracle://``user``:pass@host:port/dbname[key``=value&``key``=value.]
2.简单使用
使用 Engine/ConnectionPooling/Dialect 进行数据库操作,Engine 使用ConnectionPooling连接数据库,然后再通过Dialect执行SQL语句。
2.1 创建引擎
from sqlalchemy import create_engine # 创建引擎 engine = create_engine( "mysql+pymysql://root:1234567@127.0.0.1:3306/testflask?charset=utf8", max_overflow=0, # 超过连接池大小外最多创建的连接 pool_size=5, # 连接池中数量的大小 pool_timeout=30, # 连接池中没有连接的时候的最长的等待秒数,超时则报错 pool_recycle= -1 # 默认值是 -1,不回收,多久之后对线程池中的线程进行一次连接的回收 )
create_engine()
函数创建引擎的更多参数,可以在Pycharm
中点击查看源码,观看注释查看更多参数的用法。
2.2 执行原生sql
获取连接对象执行 sql 语句。
from sqlalchemy import create_engine # 创建引擎 engine = create_engine( # "mysql+pymysql://root:1234567@127.0.0.1:3306/testflask?charset=utf8", "mysql+pymysql://root:1234567@127.0.0.1:3306/test?charset=utf8", max_overflow=0, # 超过连接池大小外最多创建的连接 pool_size=5, # 连接池中数量的大小 pool_timeout=30, # 连接池中没有连接的时候的最长的等待秒数,超时则报错 pool_recycle= -1 # 默认值是 -1,不回收,多久之后对线程池中的线程进行一次连接的回收(重置) ) def task(): conn = engine.raw_connection()# 返回数据库连接对象,后续操作与pymysql操作相同 cur = conn.cursor() cur.execute("select * from t1") res = cur.fetchall() # 元组嵌套元组 print(res) cur.close() conn.close() task()
直接使用引擎执行 SQL 语句。
from sqlalchemy import create_engine # 创建引擎 engine = create_engine( # "mysql+pymysql://root:1234567@127.0.0.1:3306/testflask?charset=utf8", "mysql+pymysql://root:1234567@127.0.0.1:3306/test?charset=utf8", max_overflow=0, # 超过连接池大小外最多创建的连接 pool_size=5, # 连接池中数量的大小 pool_timeout=30, # 连接池中没有连接的时候的最长的等待秒数,超时则报错 pool_recycle= -1 # 默认值是 -1,不回收,多久之后对线程池中的线程进行一次连接的回收 ) def task(): cur = engine.execute("select * from t1") # 直接使用引擎执行SQL语句。 res = cur.fetchall() print(res)# 返回的是列表嵌套元组 cur.close() task()
3.ORM 使用
ORM:是对象关系映射数据库模型。将 Python 的类映射到数据库中进行操作。
3.1 创建表
3.1.1 创建单表
创建单个表,或者无关联的多表
from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column,Integer,String,UniqueConstraint # 创建引擎 engine = create_engine( "mysql+pymysql://root:1234567@127.0.0.1:3306/testflask?charset=utf8", # "mysql+pymysql://root:1234567@127.0.0.1:3306/test?charset=utf8", max_overflow=0, # 超过连接池大小外最多创建的连接 pool_size=5, # 连接池中数量的大小 pool_timeout=30, # 连接池中没有连接的时候的最长的等待秒数,超时则报错 pool_recycle= -1 # 默认值是 -1,不回收,多久之后对线程池中的线程进行一次连接的回收 ) Base = declarative_base() # 实例化映射关系的类对象,后期的数据库表类直接继承该类。 class User(Base):# 继承类 __tablename__ = 'users' # 表名 id = Column(Integer,primary_key=True) # 主键 name = Column(String(32),index=True,nullable=False) # 普通约束,非空约束。 email = Column(String(32),unique=True) # 限制唯一约束 class Books(Base): __tablename__ = 'books' id = Column(Integer, primary_key=True) # 主键 book = Column(String(32),nullable=False) # 创建 author = Column(String(32),nullable=False) __table_args__ = ( # 相当于 Django ORM 中的class Meta。 UniqueConstraint('book', 'author', name='uix_id_name'), # Index('ix_id_name', 'name', 'email'), ) def init_db(): Base.metadata.create_all(engine) # 创建所有表 init_db()
3.1.2 删除所有表
from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column,Integer,String,UniqueConstraint # 创建引擎 engine = create_engine( "mysql+pymysql://root:1234567@127.0.0.1:3306/testflask?charset=utf8", # "mysql+pymysql://root:1234567@127.0.0.1:3306/test?charset=utf8", max_overflow=0, # 超过连接池大小外最多创建的连接 pool_size=5, # 连接池中数量的大小 pool_timeout=30, # 连接池中没有连接的时候的最长的等待秒数,超时则报错 pool_recycle= -1 # 默认值是 -1,不回收,多久之后对线程池中的线程进行一次连接的回收 ) Base = declarative_base() # 实例化映射关系的类对象,后期的数据库表类直接继承该类。 class User(Base): __tablename__ = 'users' # 表名 id = Column(Integer,primary_key=True) # 主键 name = Column(String(32),index=True,nullable=False) # 普通约束,非空约束。 email = Column(String(32),unique=True) # 限制唯一约束 class Books(Base): __tablename__ = 'books' id = Column(Integer, primary_key=True) # 主键 book = Column(String(32),nullable=False) author = Column(String(32),nullable=False) __table_args__ = ( UniqueConstraint('book', 'author', name='uix_id_name'), # Index('ix_id_name', 'name', 'email'), ) def init_db(): Base.metadata.create_all(engine) def drop_db(): # 删除表的所有函数。执行 Base.metadata.drop_all(engine) if __name__ == '__main__': drop_db()
SQLAlchemy 不使用 flask-script 作为 flask 的组件的时候,无法检测到相关的单表的变化。
补充:删除所有表的时候,有时存在外键关联的时候可能会导致删除所有的失败,需要首先注释对应的关联的第三张表,或者在 mysql 中手动删除第三张关联表。
3.1.3 创建多表
创建多表之间的关联,根据表之间的关联进行表的创建。
-
一对多 or 一对一
from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column,Integer,String,UniqueConstraint,ForeignKey from sqlalchemy.orm import relationship # 创建引擎 engine = create_engine( "mysql+pymysql://root:1234567@127.0.0.1:3306/testflask?charset=utf8", # "mysql+pymysql://root:1234567@127.0.0.1:3306/test?charset=utf8", max_overflow=0, # 超过连接池大小外最多创建的连接 pool_size=5, # 连接池中数量的大小 pool_timeout=30, # 连接池中没有连接的时候的最长的等待秒数,超时则报错 pool_recycle= -1# 默认值是 -1,不回收,多久之后对线程池中的线程进行一次连接的回收 ) Base = declarative_base() # 实例化映射关系的类对象,后期的数据库表类直接继承该类。 class User(Base): __tablename__ = 'users' # 表名 id = Column(Integer,primary_key=True) # 主键 name = Column(String(32),index=True,nullable=False) # 普通约束,非空约束。 email = Column(String(32),unique=True) # 限制唯一约束 class Student(Base): __tablename__ = 'student' nid = Column(Integer,primary_key=True) id = Column(Integer,ForeignKey("users.id")) # 使用外键的时候,ForignKey()中的参数是表名(__tablename__).列名,不是类名 stu_no = Column(String(32),nullable=False) # 与生成的表结构无关,仅用于方便查询,第一个参数一般是类名。 hobby = relationship("User", backref='stu') def init_db(): Base.metadata.create_all(engine) def drop_db(): Base.metadata.drop_all(engine) if __name__ == '__main__': # drop_db() init_db() relationship 用于数据的跨表查询,类似django中的跨表时使用的
.
和双下划线__
. -
多对多
from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column,Integer,String,UniqueConstraint,ForeignKey from sqlalchemy.orm import relationship # 创建引擎 engine = create_engine( "mysql+pymysql://root:1234567@127.0.0.1:3306/testflask?charset=utf8", # "mysql+pymysql://root:1234567@127.0.0.1:3306/test?charset=utf8", max_overflow=0, # 超过连接池大小外最多创建的连接 pool_size=5, # 连接池中数量的大小 pool_timeout=30, # 连接池中没有连接的时候的最长的等待秒数,超时则报错 pool_recycle= -1 # 默认值是 -1,不回收,多久之后对线程池中的线程进行一次连接的回收(重置) ) Base = declarative_base() # 实例化映射关系的类对象,后期的数据库表类直接继承该类。 class Group(Base): __tablename__ = 'group' id = Column(Integer,primary_key=True) name = Column(String(64),unique=True,nullable=False) servers = relationship('Server',secondary='servergroup',backref='groups') class Server(Base): __tablename__ = 'server' id = Column(Integer,primary_key=True,autoincrement=True)# 设置主键自增 hostname = Column(String(64),unique=True,nullable=False)# 非空约束,唯一约束 class ServerGroup(Base): __tablename__ = 'servergroup' id = Column(Integer,primary_key=True,autoincrement=True) # 创建两个外键约束, server_id = Column(Integer,ForeignKey("server.id")) group_id = Column(Integer,ForeignKey("group.id")) def init_db(): Base.metadata.create_all(engine) def drop_db(): Base.metadata.drop_all(engine) if __name__ == '__main__': drop_db() # 删除之前的表啊 init_db() # 创建现有的模型表 多对多之间的约束,在 SQLAlchemy 中多对多的第三张表只能自己手动建立,不能像在Django中的 ORM 一样程序中自动创建第三张表,只能手动创建。
3.2 增删改查
3.2.1 增加数据
单表插入,创建session
使用,创建 orm 对象,使用session
提交事务,关闭session
.
from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column,Integer,String,UniqueConstraint,ForeignKey from sqlalchemy.orm import relationship,sessionmaker # 创建引擎 engine = create_engine( "mysql+pymysql://root:1234567@127.0.0.1:3306/testflask?charset=utf8", # "mysql+pymysql://root:1234567@127.0.0.1:3306/test?charset=utf8", max_overflow=0, # 超过连接池大小外最多创建的连接 pool_size=5, # 连接池中数量的大小 pool_timeout=30, # 连接池中没有连接的时候的最长的等待秒数,超时则报错 pool_recycle= -1 # 默认值是 -1,不回收,多久之后对线程池中的线程进行一次连接的回收 ) Base = declarative_base() # 实例化映射关系的类对象,后期的数据库表类直接继承该类。 class User(Base): __tablename__ = 'users' # 表名 id = Column(Integer,primary_key=True) # 主键 name = Column(String(32),index=True,nullable=False) # 普通约束,非空约束。 email = Column(String(32),unique=True) # 限制唯一约束 class Books(Base): __tablename__ = 'books' id = Column(Integer, primary_key=True) # 主键 book = Column(String(32),nullable=False) author = Column(String(32),nullable=False) __table_args__ = ( UniqueConstraint('book', 'author', name='uix_id_name'), # Index('ix_id_name', 'name', 'email'), ) # 创建所有表 def init_db(): Base.metadata.create_all(engine) # 删除所有表 def drop_db(): Base.metadata.drop_all(engine) if __name__ == '__main__': # drop_db() # init_db() Session = sessionmaker(bind=engine) # 每次执行数据库操作的时候都需要创建一个session session = Session() # 执行 orm 操作 obj_user = User(name='123',email='123@qq.com') session.add(obj_user) # 提交事务 session.commit() # 关闭session session.close()
增加多条数据
from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column,Integer,String,UniqueConstraint,ForeignKey from sqlalchemy.orm import relationship,sessionmaker,scoped_session # 创建引擎 engine = create_engine( "mysql+pymysql://root:1234567@127.0.0.1:3306/testflask?charset=utf8", max_overflow=0, # 超过连接池大小外最多创建的连接 pool_size=5, # 连接池中数量的大小 pool_timeout=10, # 连接池中没有连接的时候的最长的等待秒数,超时则报错 pool_recycle= -1 # 默认值是 -1,不回收,多久之后对线程池中的线程进行一次连接的回收 ) Base = declarative_base() # 实例化映射关系的类对象,后期的数据库表类直接继承该类。 class User(Base): __tablename__ = 'users' # 表名 id = Column(Integer,primary_key=True) # 主键 name = Column(String(32),index=True,nullable=False) # 普通约束,非空约束。 # email = Column(String(32),unique=True) # 限制唯一约束 email = Column(String(32)) # 限制唯一约束 Session = sessionmaker(bind=engine) session = scoped_session(Session) # 创建线程安全的session 对象 def orm_func(): # 添加一条 obj = User(name='1234', email='123@qq.com') session.add(obj) # 添加多条,参数是列表嵌套,表的对象。 session.add_all([ User(name='123456', email='123@qq.com'), User(name='123456', email='123@qq.com'), User(name='123456', email='123@qq.com'), ]) # 提交事务 session.commit() if __name__ == '__main__': orm_func()
3.2.2 查询数据
from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column,Integer,String,UniqueConstraint,ForeignKey from sqlalchemy.orm import relationship,sessionmaker,scoped_session # 创建引擎 engine = create_engine( "mysql+pymysql://root:1234567@127.0.0.1:3306/testflask?charset=utf8", # "mysql+pymysql://root:1234567@127.0.0.1:3306/test?charset=utf8", max_overflow=0, # 超过连接池大小外最多创建的连接 pool_size=5, # 连接池中数量的大小 pool_timeout=10, # 连接池中没有连接的时候的最长的等待秒数,超时则报错 pool_recycle= -1 # 默认值是 -1,不回收,多久之后对线程池中的线程进行一次连接的回 ) Base = declarative_base() # 实例化映射关系的类对象,后期的数据库表类直接继承该类。 class User(Base): __tablename__ = 'users' # 表名 id = Column(Integer,primary_key=True) # 主键 name = Column(String(32),index=True,nullable=False) # 普通约束,非空约束。 # email = Column(String(32),unique=True) # 限制唯一约束 email = Column(String(32)) # 限制唯一约束 Session = sessionmaker(bind=engine) session = scoped_session(Session) # 创建线程安全的session 对象 def query_task(): rsp1 = session.query(User).all() # 输入表的类,查询所有 # label 是相当与 SQL 语句中的 AS 列名。 rsp2 = session.query(User.name.label('asname'),User.email).all() # 条件筛选 rsp3 = session.query(User).filter(User.name == '1234').all() # 不需要出入表名的类名 rsp4 = session.query(User).filter_by(name='123').all() # 获取第一条数据。 rsp5 = session.query(User).filter_by(name='123').first() print("rsp1",rsp1,'\n',[i.name for i in rsp1]) print("rsp2",rsp2,'\n',[i.asname for i in rsp2]) print("rsp3",rsp3,'\n',[i.name for i in rsp3]) print("rsp4",rsp4,'\n',[i.name for i in rsp4]) print("rsp5",rsp5,rsp5.name)
条件查询,升序排序。
from sqlalchemy.sql import text rsp6 = session.query(User).filter(text("id<:value and name=:name")).params(value=5,name='123').order_by(User.id).all() print("rsp6",rsp6,'\n',[i.id for i in rsp6])
降序排序。
from sqlalchemy.sql import text rsp6 = session.query(User).filter(text("id<:value and name=:name")).params(value=5,name='123').order_by(-User.id).all() print("rsp6",rsp6,'\n',[i.id for i in rsp6])
执行原生 SQL 语句
from sqlalchemy.sql import text r7 = session.query(User).from_statement(text("select * from users where id=:id")).params(id='1').all() print("r7",r7,'\n',[i.id for i in r7])
3.2.3 删除数据
# ******* 删除数据 ********* session.query(User).filter(User.id > 5).delete() # 删除id> 5的数据 session.commit() #提交事务 session.close()
删除数据的时候filter()
内部的参数是筛选的条件,之后就是使用delete()
进行删除,删除后进行提交事务,最后关闭session.close()
.
3.2.4 修改数据
# 修改数据 session.query(User).filter(User.id>0).update({"name":"678"}) session.query(User).filter(User.id>0).update({User.name:User.name+"099"},synchronize_session=False) session.query(User).filter(User.id>0).update({"email":User.email+'666'}) session.commit() session.close()
3.3 常用功能
3.3.1 条件筛选补充
# filter_by 直接用属性名,filter()类名.属性名 ret = session.query(User).filter_by(name='678099').all() print(ret)
参考文献:https://www.jianshu.com/p/f40a4987891b
ret = session.query(User).filter(User.id>2,User.name=='678099').all() print(ret,'\n',[i.id for i in ret])
# 使用between,确定范围。 ret = session.query(User).filter(User.id.between(1,3),User.name == '678099').all() print([i.id for i in ret]) >>> [1,2,3]
使用in
关键字,和not in
这两个关键字
# in 关键字 ret = session.query(User).filter(User.id.in_([1, 3, 4])).all() print([i.id for i in ret]) # not in 的使用 ret = session.query(User).filter(~User.id.in_([1, 3, 4])).all() print([i.id for i in ret])
使用查询结果当做临时表.
ret = session.query(User).filter(User.id.in_(session.query(User.id).filter_by(name='678099'))).all() print([i.id for i in ret]) >>> [1, 2, 3, 4, 5]
分组,排序,跨表查询;
from sqlalchemy import and_, or_ ret = session.query(Users).filter(and_(Users.id > 3, Users.name == 'eric')).all() ret = session.query(Users).filter(or_(Users.id < 2, Users.name == 'eric')).all() ret = session.query(Users).filter( or_( Users.id < 2, and_(Users.name == 'eric', Users.id > 3), Users.extra != "" )).all() # 通配符 ret = session.query(Users).filter(Users.name.like('e%')).all() ret = session.query(Users).filter(~Users.name.like('e%')).all() # 限制 ret = session.query(Users)[1:2] # 排序 ret = session.query(Users).order_by(Users.name.desc()).all() ret = session.query(Users).order_by(Users.name.desc(), Users.id.asc()).all() # 分组 from sqlalchemy.sql import func ret = session.query(Users).group_by(Users.extra).all() ret = session.query( func.max(Users.id), func.sum(Users.id), func.min(Users.id)).group_by(Users.name).all() ret = session.query( func.max(Users.id), func.sum(Users.id), func.min(Users.id)).group_by(Users.name).having(func.min(Users.id) >2).all() # 连表 ret = session.query(Users, Favor).filter(Users.id == Favor.nid).all() ret = session.query(Person).join(Favor).all() ret = session.query(Person).join(Favor, isouter=True).all() # 组合 q1 = session.query(Users.name).filter(Users.id > 2) q2 = session.query(Favor.caption).filter(Favor.nid < 2) ret = q1.union(q2).all() q1 = session.query(Users.name).filter(Users.id > 2) q2 = session.query(Favor.caption).filter(Favor.nid < 2) ret = q1.union_all(q2).all()
3.3.2 relationship的使用
外键操作。
import time import threading from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint, Index from sqlalchemy.orm import sessionmaker, relationship from sqlalchemy import create_engine from sqlalchemy.sql import text from sqlalchemy.engine.result import ResultProxy from db import Users, Hosts, Hobby, Person engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/s6?charset=utf8", max_overflow=0, pool_size=5) Session = sessionmaker(bind=engine) session = Session() # 添加 """ session.add_all([ Hobby(caption='乒乓球'), Hobby(caption='羽毛球'), Person(name='张三', hobby_id=3), Person(name='李四', hobby_id=4), ]) person = Person(name='张九', hobby=Hobby(caption='姑娘')) session.add(person) hb = Hobby(caption='人妖') hb.pers = [Person(name='文飞'), Person(name='博雅')] session.add(hb) session.commit() """ # 使用relationship正向查询 """ v = session.query(Person).first() print(v.name) print(v.hobby.caption) """ # 使用relationship反向查询 """ v = session.query(Hobby).first() print(v.caption) print(v.pers) """ session.close()
操作多对多的表
import time import threading from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint, Index from sqlalchemy.orm import sessionmaker, relationship from sqlalchemy import create_engine from sqlalchemy.sql import text from sqlalchemy.engine.result import ResultProxy from db import Users, Hosts, Hobby, Person, Group, Server, Server2Group engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/s6?charset=utf8", max_overflow=0, pool_size=5) Session = sessionmaker(bind=engine) session = Session() # 添加 """ session.add_all([ Server(hostname='c1.com'), Server(hostname='c2.com'), Group(name='A组'), Group(name='B组'), ]) session.commit() s2g = Server2Group(server_id=1, group_id=1) session.add(s2g) session.commit() gp = Group(name='C组') gp.servers = [Server(hostname='c3.com'),Server(hostname='c4.com')] session.add(gp) session.commit() ser = Server(hostname='c6.com') ser.groups = [Group(name='F组'),Group(name='G组')] session.add(ser) session.commit() """ # 使用relationship正向查询 """ v = session.query(Group).first() print(v.name) print(v.servers) """ # 使用relationship反向查询 """ v = session.query(Server).first() print(v.hostname) print(v.groups) """ session.close()
3.4 线程安全
基于scoped_session
实现线程安全;
3.4.1 问题引出
import threading from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column,Integer,String,UniqueConstraint,ForeignKey from sqlalchemy.orm import relationship,sessionmaker # 创建引擎 engine = create_engine( "mysql+pymysql://root:1234567@127.0.0.1:3306/testflask?charset=utf8", # "mysql+pymysql://root:1234567@127.0.0.1:3306/test?charset=utf8", max_overflow=0, # 超过连接池大小外最多创建的连接 pool_size=5, # 连接池中数量的大小 pool_timeout=10, # 连接池中没有连接的时候的最长的等待秒数,超时则报错 pool_recycle= -1 # 默认值是 -1,不回收,多久之后对线程池中的线程进行一次连接的回收 ) Base = declarative_base() # 实例化映射关系的类对象,后期的数据库表类直接继承该类。 class User(Base): __tablename__ = 'users' # 表名 id = Column(Integer,primary_key=True) # 主键 name = Column(String(32),index=True,nullable=False) # 普通约束,非空约束。 # email = Column(String(32),unique=True) # 限制唯一约束 email = Column(String(32)) # 限制唯一约束 class Books(Base): __tablename__ = 'books' id = Column(Integer, primary_key=True) # 主键 book = Column(String(32),nullable=False) author = Column(String(32),nullable=False) __table_args__ = ( UniqueConstraint('book', 'author', name='uix_id_name'), # Index('ix_id_name', 'name', 'email'), ) def init_db(): Base.metadata.create_all(engine) def drop_db(): Base.metadata.drop_all(engine) Session = sessionmaker(bind=engine) def insert_task(): # 不出错,但是每个线程函数中都需要手动创建一个session # 每次执行数据库操作的时候都需要创建一个session session = Session() # 每一个session 中都创建一个session # 执行 orm 操作 obj_user = User(name='123', email='123@qq.com') session.add(obj_user) # 提交事务 session.commit() # 关闭session session.close() if __name__ == '__main__': drop_db() init_db() for i in range(10): t = threading.Thread(target=insert_task) t.start()
引出:当把session
创建在线程函数之外,代码和结果如下。
import threading from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column,Integer,String,UniqueConstraint,ForeignKey from sqlalchemy.orm import relationship,sessionmaker # 创建引擎 engine = create_engine( "mysql+pymysql://root:1234567@127.0.0.1:3306/testflask?charset=utf8", # "mysql+pymysql://root:1234567@127.0.0.1:3306/test?charset=utf8", max_overflow=0, # 超过连接池大小外最多创建的连接 pool_size=5, # 连接池中数量的大小 pool_timeout=10, # 连接池中没有连接的时候的最长的等待秒数,超时则报错 pool_recycle= -1 # 默认值是 -1,不回收,多久之后对线程池中的线程进行一次连接的回收(重置) ) Base = declarative_base() # 实例化映射关系的类对象,后期的数据库表类直接继承该类。 class User(Base): __tablename__ = 'users' # 表名 id = Column(Integer,primary_key=True) # 主键 name = Column(String(32),index=True,nullable=False) # 普通约束,非空约束。 # email = Column(String(32),unique=True) # 限制唯一约束 email = Column(String(32)) # 限制唯一约束 class Books(Base): __tablename__ = 'books' id = Column(Integer, primary_key=True) # 主键 book = Column(String(32),nullable=False) author = Column(String(32),nullable=False) __table_args__ = ( UniqueConstraint('book', 'author', name='uix_id_name'), # Index('ix_id_name', 'name', 'email'), ) def init_db(): Base.metadata.create_all(engine) def drop_db(): Base.metadata.drop_all(engine) Session = sessionmaker(bind=engine) session = Session() def insert_task(): # 每次执行数据库操作的时候都需要创建一个session # session = Session() # 每一个session 中都创建一个session # 执行 orm 操作 obj_user = User(name='123', email='123@qq.com') session.add(obj_user) # 提交事务 session.commit() # 关闭session session.close() if __name__ == '__main__': drop_db() init_db() for i in range(10): t = threading.Thread(target=insert_task) t.start()
报错,因为当异步线程请求session
的时候可能在关闭的状态。解决方法可以直接按照最开始的状态在每一个线程中设置自己的session
进行处理,也可以按照下方的方法进行处理。
3.4.2 线程安全
from sqlalchemy.orm import scpoed_session Session = sessionmaker(bind=engine) session = scoped_session(Session) # 创建线程安全的session 对象 # 接下来的操作与普通session完全相同。
示例
import threading from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column,Integer,String,UniqueConstraint,ForeignKey from sqlalchemy.orm import relationship,sessionmaker,scoped_session # 创建引擎 engine = create_engine( "mysql+pymysql://root:1234567@127.0.0.1:3306/testflask?charset=utf8", # "mysql+pymysql://root:1234567@127.0.0.1:3306/test?charset=utf8", max_overflow=0, # 超过连接池大小外最多创建的连接 pool_size=5, # 连接池中数量的大小 pool_timeout=10, # 连接池中没有连接的时候的最长的等待秒数,超时则报错 pool_recycle= -1 # 默认值是 -1,不回收,多久之后对线程池中的线程进行一次连接的回收(重置) ) Base = declarative_base() # 实例化映射关系的类对象,后期的数据库表类直接继承该类。 class User(Base): __tablename__ = 'users' # 表名 id = Column(Integer,primary_key=True) # 主键 name = Column(String(32),index=True,nullable=False) # 普通约束,非空约束。 # email = Column(String(32),unique=True) # 限制唯一约束 email = Column(String(32)) # 限制唯一约束 class Books(Base): __tablename__ = 'books' id = Column(Integer, primary_key=True) # 主键 book = Column(String(32),nullable=False) author = Column(String(32),nullable=False) __table_args__ = ( UniqueConstraint('book', 'author', name='uix_id_name'), # Index('ix_id_name', 'name', 'email'), ) def init_db(): Base.metadata.create_all(engine) def drop_db(): Base.metadata.drop_all(engine) Session = sessionmaker(bind=engine) session = scoped_session(Session) # 创建线程安全的session 对象 def insert_task(): # 每次执行数据库操作的时候都需要创建一个session # session = Session() # 每一个session 中都创建一个session # 执行 orm 操作 obj_user = User(name='123', email='123@qq.com') session.add(obj_user) # 提交事务 session.commit() # 关闭session session.close() if __name__ == '__main__': drop_db() init_db() for i in range(10): t = threading.Thread(target=insert_task) t.start()
插入到对应的数据表中。
3.4.3 原理剖析
为每一个线程创建一个session
之间的数据却有不相互影响,应该就是therading.local()
函数。
from sqlalchemy.orm import scpoed_session Session = sessionmaker(bind=engine) session = scoped_session(Session) # 创建线程安全的session 对象 # 接下来的操作与普通session完全相同。
点击查看源码。
# 行线程安全的类 class scoped_session(ScopedSessionMixin): """Construct a new :class:`.scoped_session`. :param session_factory: a factory to create new :class:`.Session` instances. This is usually, but not necessarily, an instance of :class:`.sessionmaker`. :param scopefunc: optional function which defines the current scope. If not passed, the :class:`.scoped_session` object assumes "thread-local" scope, and will use a Python ``threading.local()`` in order to maintain the current :class:`.Session`. If passed, the function should return a hashable token; this token will be used as the key in a dictionary in order to store and retrieve the current :class:`.Session`. """ _support_async = False session_factory = None # 实例化对象,就执行改方法,`session_factory`猜想为session的工厂,工厂模式,产生session def __init__(self, session_factory, scopefunc=None): # 参数的介绍参考解析,大致意思是没有自定义的线程范围函数,则通过 # theading.local()函数设置键值区分 session self.session_factory = session_factory # 传入创建Session的对象 if scopefunc: # 第二个参数有传入值且不为空的时候执行 self.registry = ScopedRegistry(session_factory, scopefunc) else: # 默认情况,执行后面的类。 self.registry = ThreadLocalRegistry(session_factory) def remove(self): # if self.registry.has(): self.registry().close() self.registry.clear() def query_property(self, query_cls=None): class query(object): def __get__(s, instance, owner): try: mapper = class_mapper(owner) if mapper: if query_cls: # custom query class return query_cls(mapper, session=self.registry()) else: # session's configured query class return self.registry().query(mapper) except orm_exc.UnmappedClassError: return None return query()
ThreadLocalRegistry()类,
class ThreadLocalRegistry(ScopedRegistry): """A :class:`.ScopedRegistry` that uses a ``threading.local()`` variable for storage. """ # 执行`__init__()`方法 def __init__(self, createfunc): self.createfunc = createfunc # 传入的是创建`session`的函数。 self.registry = threading.local() # 实例化 threading.local()对象。 def __call__(self): try: # 不是第一次,直接返回session值 return self.registry.value except AttributeError: # 第一次创建session() # self.createfunc 就是 传入的 Session,加上括号代表实例化,并写入 # threading.local()中 val = self.registry.value = self.createfunc() return val # 返回创建好的值 def has(self): # 通过反射查看值是否存在 return hasattr(self.registry, "value") def set(self, obj): # 设置相关的值 self.registry.value = obj def clear(self): try: # 清空值 del self.registry.value except AttributeError: pass
继续努力,终成大器。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?