SQlALchemy session详解
系列文章:
概念
session用于创建程序和数据库之间的会话,所有对象的载入和保存都需通过session对象 。
通过sessionmaker调用创建一个工厂,并关联Engine以确保每个session都可以使用该Engine连接资源:
from sqlalchemy.orm import sessionmaker
# 创建session
DbSession = sessionmaker(bind=engine)
session = DbSession()
操作
session的常见操作方法包括:
- flush:预提交,提交到数据库文件,还未写入数据库文件中
- commit:提交了一个事务,把内存的数据直接写入数据库
- rollback:回滚
- close:关闭
在事务处理时,需注意一下两点:
- 在事务处理过程发生异常时,进行rollback操作,否则会在下次操作时报错:
Can’t reconnect until invalid transaction is rolled back
- 一般情况下,在一个事务处理完成之后要关闭session,以确保数据操作的准确性。
建议封装上下文方法:
from contextlib import contextmanager
@contextmanager
def session_maker(session=session):
try:
yield session
session.commit()
except:
session.rollback()
raise
finally:
session.close()
调用:
def update_user():
with session_maker() as db_session:
db_session.query(Users).filter_by(name='test2').update({'email': 'test2@qq.com'})
线程安全
session不是线程安全的,并且我们一般session对象都是全局的,那么在多线程情况下,当多个线程共享一个session时,数据处理就会发生错误。
为了保证线程安全,需使用scoped_session方法:
db_session = scoped_session(sessionmaker(bind=engine))
内部原理
session对象包含了三个重要的部分:
- 标识映射(Identity Map)
- 对象的状态 / 状态跟踪
- 事务
标识映射
标识映射是与ORM关联的集合,通过标识映射保证了数据库操作的准确性。
具体的实现原理是:维护一个Python字典(IdentityMap),关联这个Session对象到数据库ID的映射,当应用程序想要获取一个session对象时,若该对象不存在,标识映射会加载该对象并缓存,若该对象已存在,则直接获取。这样的好处是:
- 已经被请求过的session对象缓存下来,不需要连接加载多次,造成额外的开销;
- 避免了数据不一致
状态跟踪
一个Session对象从创建到销毁,依次经历四种状态,分别是:
- Transient:刚new出来的对象,还不在会话中,也没有保存到数据库。
- Pending:transient的对象调用add后,就会变成pending状态,这时会加入sqlalchemy的监管范围,数据并未更新到数据库。
- Persistent:该状态表明数据库里已经记录了该对象,在两种情况下对象处于该状态:一是通过flush()方法刷新pending对象,二是从数据库query()得到对象。
- Detached:在会话中的事务提交之后,所有的对象都将是Detached状态。
所谓的状态跟踪,就是跟踪以上四个状态,保证数据的准确性并在合理的时机丢弃对象以保证合理开销,那么具体是怎么实现的呢?
我们可以看到,只有在pending状态时,对象的内存数据和数据库中的数据不一致,在Persistent状态时,内存数据和数据库数据已经一致,那么此后任意时刻丢弃该对象数据都是可以的,这时就需要找个合适的时机丢弃对象,过早或过晚都有其缺陷。于是,就让垃圾回收器来做决定,在内存不够的时候释放对象,回收内存。
Session对象采用了弱引用机制,所谓弱引用,就是说,在保存了对象的引用的情况下,对象仍然可能被垃圾回收器回收。在某一时刻通过引用访问对象时,对象可能存在也可能不存在,如果对象不存在,就重新从数据库中加载对象。而如果不希望对象被回收,只需要另外保存一个对象的强引用即可 。
session对象包括三个属性:
- new:刚加入会话的对象
- dirty:刚被修改的对象
- deleted:在会话中被删除的对象
三个属性共同的特点就是内存的数据和数据库数据不一致,也就是对象处于pending状态,这也就表明了session保存了所有对象处于pending状态的强引用。
以上。
代码可参照:my github
- Nginx 负载均衡的Cache缓存批量清理的操作记录
- DotNet软件开发框架
- Nginx通过https方式反向代理的简单实现
- 再论IBatisNet + Castle进行项目的开发
- 利用xml轻松读取web.config中的用户自定义节
- 分布式监控系统Zabbix-3.0.3-完整安装记录(3)-监控nginx,php,memcache,Low-level discovery磁盘IO
- python报错问题解决:'ascii' codec can't encode character
- 利用message queue实现aspx与winform通信, 并附完整示例
- 10招步骤保护IIS服务器安全
- Haproxy+Keepalived高可用环境部署梳理(主主和主从模式)
- Android-Universal-Image-Loader 图片异步加载类库的使用
- 工作组模式下专用队列(Private Queue)如何引用远程队列路径
- haproxy反向代理环境部署(http和https代理)
- 网站速度优化模块HttpCompressionModule
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法