目的
总结项目中使用到sqlalchemy的部分。(python中的对象映射技术,更方便地使用)
使用
基础
安装
pip install SQLAlchemy -i https://pypi.douban.com/simple pip install mysql-connector-python -i https://pypi.douban.com/simple
增删改查
# 主要引用自廖雪峰老师的使用SQLAlchemy # 导入: from sqlalchemy import Column, Integer, String, create_engine from sqlalchemy.orm import sessionmaker from sqlalchemy.ext.declarative import declarative_base # 创建对象的基类: Base = declarative_base() # 定义User对象: class User(Base): # 表的名字: __tablename__ = 'user' # 表的结构: id = Column(Integer, primary_key=True)
name = Column(String(20))
# 初始化数据库连接:
engine = create_engine('mysql+mysqlconnector://root:password@localhost:3306/test')
# 创建DBSession类型:
DBSession = sessionmaker(bind=engine)
# 创建session对象:
session = DBSession()
# 创建新User对象: #数据对象得到创建,此时为Transient状态
new_user = User(id=5, name='Bob')
# 添加到session: #数据对象被关联到session上,此时为Pending状态
session.add(new_user)
# 提交即保存到数据库: #数据对象被推到数据库中,此时为Persistent状态
session.commit()
# 关闭session:
session.close()
# 查询
user = session.query(User).filter(User.id==5).first();
# user = session.query(User).get(5); # get参数为id
# user = session.query(User).filter_by(id=5).first(); #条件
# 删除,在查询基础上
session.delete(user)
# 改,在查询基础上
user.name='Alice'
session.commit()
session.close()
数据库操作
def init_db(): """初始化生成上述所有表,可重复执行,内部自动判断表是否存在""" Base.metadata.create_all(engine) def drop_db(): # 删除表 Base.metadata.drop_all(engine)
一些区分项
- filter与filter_by: 前者条件更为丰富,插入的是条件Model.item == Number; filter_by插入的是kwargs键值对,item=Number
- 查询的启动项,即session.query(xxxx).xxx()的最后一项:主要有返回单个值得first, scalar及多个值得all, 其中 first返回第一个结果或者空,scalar如果有多个结果时报错,其他情况同first。
- filter或者filter_by中,多个条件或语句逗号隔开,默认为“and”关系, or需要引入filter_by(or_(rule1, rule2))
高级
对session进行包装
@contextmanager def scope_session(DBSession): session = DBSession() try: yield session # 返回session对象 session.commit() except Exception as e: session.rollback() # 异常时回滚,否则之后所有orm操作全部失效 logger.error(f"{e}", exc_info=True) # exc_info=True可输出错误栈 finally: session.close() DBSession.remove() # 释放session,用于多线程时,防止数据库连接超时失效 # 使用方式 with scope_session(DBSession) as session: user = session.query(User).get(5)
session使用进程池及防止长时间失效
engine = create_engine(DB_URL, # DB_URL同上 max_overflow=5, # 超过连接池大小外最多创建的连接 pool_size=10, # 连接池大小 pool_timeout=30, # 池中没有线程最多等待的时间,否则报错 pool_recycle=7200, # 多久之后对线程池中的线程进行一次连接的回收(重置) echo=False, # 是否回显 isolation_level="READ_COMMITTED" # 实时监控数据库变化 ) # engine修改为上面代码,使用后DBSession.remove()归还连接到线程池。
关联查询relationship
from sqlalchemy.orm import relationship, foreign, remote
from sqlalchemy import Column, Integer, String
class Book(Base): __tablename__ = 'book'
id = Column(Integer, primary_key=True)
isbn = Column(String(11), nullable=False, comment="isbn编码") owner_id = Column(Integer, comment="拥有者") user= relationship( 'User', # 引用的对象 uselist=True, # 结果是单个还是列表 primaryjoin=foreign(owner_id) == remote(User.id), backref="books") # 标准答案 # foreign与remote可以实现在数据库不设置外键的情况下代码中以外键使用。 user.books or book.user
批量操作
User类下添加属性方法 @property def info_dict(self): _needs = ['id', 'name'] return {name: getattr(self, name) for name in _needs} users = [User(id=1, name='Yin'), User(id=2, name='Yang'] with scope_session(DBSession) as session: # 法一 session.bulk_save_objects(users) # 添加对象列表,增加参数 session.bulk_save_objects(users, return_defaults=True)#可返回修改部分,例如主键为自增,当前项id=None, 运行后users中的对象id为数据库id # 法二 user_dicts = [user.info_dict for user in users] session.bulk_insert_mappings(User, user_dicts) # 插入 session.bulk_update_mappings(User, user_dicts) # 更新
其他
# 从一个数据库中查询一个对象插入到另一个数据库中, 修改对象状态。 from sqlalchemy.orm import make_transient with scope_session(DBSession1) as session: user = session.query(User).get(5) make_transient(user) with scope_session(DBSession2) as target_sess: target_sess.add(user) # 执行mysql语句 session.execute('call mysql_procedure();') # 对象丢失时使用或跨session使用对象 session.merge(user) #复杂查询 user_ids = {1, 2, 3} results = session.query(User.id, Book).filter(User.id.in_(user_ids)).join(Book, User.id == Book.owner_id).order_by(User.id).all()
# 多线程使用 from concurrent.futures import ThreadPoolExecutor, as_completed # 先用一个session查询相关对象,将查出的对象传入函数中,在函数中新建session,再merge; 或者把session也传入。 executor = ThreadPoolExecutor(max_workers=4) def test(user): print(f"hello {user}") with scope_session(DBSession) as session: books = session.query(Book).filter(Book.owner_id== user.id).all() for book in books: print(f"{user.name} own book {book.isbn}") return True def get_result(future): data = future.result() print(data) user = session.query(User).get(5) executor.submit(test, user).add_done_callback(get_result)
参考
廖雪峰 https://www.liaoxuefeng.com/wiki/1016959663602400/1017803857459008
K.Takanashi https://www.cnblogs.com/franknihao/p/7268752.html