微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

SQLAlchemy:如何避免 ORM 缓存和 DB

如何解决SQLAlchemy:如何避免 ORM 缓存和 DB

这是我的问题的简化版本。我有一个程序query.py

import time
from models import Ball,session

time.sleep(1)
r = session.query(Ball).filter(Ball.color=='red').first()
print(f'Red ball color is {r.color}')
time.sleep(2)
b = session.query(Ball).filter(Ball.color=='blue').first()
print(f'Blue ball color is {b.color}')
print(f'Red ball id is {r.id},blue ball id is {b.id}')

当我与 query.py包括在下面)同时运行 modify.py 时,我得到以下输出

$ python modify.py &! python query.py
Red ball color is red                                                                                                                                                   
Blue ball color is red                                                                                                                                                  
Red ball id is 1,blue ball id is 1                                                                                                                  

问题是蓝球是红色的!

这里是models.py内容

import sqlalchemy as sa
import sqlalchemy.orm as sao
import sqlalchemy.ext.declarative as SAEd

Base = SAEd.declarative_base()

class Ball(Base):
    __tablename__ = 'ball'
    id = sa.Column(sa.Integer,primary_key=True)
    color = sa.Column(sa.String)

engine = sa.create_engine('sqlite:///test.db')
Base.Metadata.create_all(engine)
session = sao.Session(engine)

这里是modify.py

import time
from models import Ball,session

session.query(Ball).delete()
b = Ball(color='red')
session.add(b)
session.commit()
time.sleep(2)
b.color = 'blue'
session.add(b)
session.commit()

我觉得很奇怪,我的数据库查询(看到最新的数据库状态)和通过我的数据库查询sqlAlchemy 标识映射返回的对象(它是陈旧的,首先反映数据库状态)之间出现不一致有问题的行被读取的时间)。我知道在每次查询之前在 query.py 进程中重新启动我的事务会使身份映射中的缓存对象无效,并导致此处的蓝球变为蓝色,但那是不可能的。

如果蓝球是蓝色的——即如果数据库查询和它返回的对象一致——或者如果蓝球查询返回 None——即如果并发数据库修改不是,我会很高兴在查询事务中可见。但是我好像卡在了中间。

解决方法

似乎潜在的问题是 Python 中默认的 SQLite 支持有问题,而 SQLAlchemy 故意继承了这种有问题的行为。我最终想出了如何获得两种可能的正确行为,即要么使blue ball blue,通过在不取消当前事务的情况下使身份映射/缓存无效,或者通过在适当隔离的事务中运行 None 使对蓝色球的查询返回 query.py

实现隔离/使蓝球查询返回None

我发现通过将 isolation_level=<level> 传递给 create_engine 来设置隔离级别没有任何效果,即这并没有提供一种方法来使 query.py 在一个事务中运行与 modify.py 的写入隔离,其中对蓝色球的查询将返回 None。在阅读了 isolation levels in SQLite 之后,似乎将隔离级别设置为 SERIALIZABLE 应该可以实现这一点,但事实并非如此。但是,the SQLAlchemy documentation warns 默认情况下 SQLite 事务已损坏:

数据库锁定行为/并发部分中,我们提到了 pysqlite 驱动程序的各种问题,这些问题阻止了 SQLite 的几个功能正常工作。 pysqlite DBAPI 驱动程序有几个长期存在的错误,这些错误会影响其事务行为的正确性。在其默认操作模式下,诸如 SERIALIZABLE 隔离、事务性 DDL 和 SAVEPOINT 支持等 SQLite 功能不起作用,为了使用这些功能,必须采取变通办法。

该页面继续建议使事务正常运行的变通方法,这些变通方法对我有用。即在models.py的底部添加以下内容,实现了蓝球查询返回None的隔离行为:

@sa.event.listens_for(engine,"connect")
def do_connect(dbapi_connection,connection_record):
    # disable pysqlite's emitting of the BEGIN statement entirely.
    # also stops it from emitting COMMIT before any DDL.
    dbapi_connection.isolation_level = None

@sa.event.listens_for(engine,"begin")
def do_begin(conn):
    # emit our own BEGIN
    conn.exec_driver_sql("BEGIN")

随着这个变化,输出变成

$ python modify.py &! python query.py
Red ball color is red                                                                                                                                                   
Traceback (most recent call last):                                                                                                                                      
  File "query.py",line 10,in <module>
    print(f'Blue ball color is {b.color}')
AttributeError: 'NoneType' object has no attribute 'color'

modify.py 中使球变为蓝色的 DB 写入在 query.py 中的(隐式)事务中不可见。

使查询和返回的对象一致/使蓝球变蓝

另一方面,要获得蓝色球为蓝色的行为,在每次查询之前使用 session.expire_all() 使缓存/身份映射无效就足够了。即,将 query.py 更改为以下工作:

import time
from models import Ball,session

time.sleep(1)
r = session.query(Ball).filter(Ball.color=='red').first()
print(f'Red ball color is {r.color}')
time.sleep(2)
# Added this line:
session.expire_all()
b = session.query(Ball).filter(Ball.color=='blue').first()
print(f'Blue ball color is {b.color}')
print(f'Red ball id is {r.id},blue ball id is {b.id}')

随着这个变化,输出变成

$ python modify.py &! python query.py                                                                                                                                
Red ball color is red                                                                                                                                                   
Blue ball color is blue                                                                                                                                                 
Red ball id is 1,blue ball id is 1

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?