有人可以解释一下,当我有一个实体列表和移动到详细信息页面的能力时,如何避免应用程序冻结。 所以,我打开列表,一个sqlalchemy会话开始,然后我打开一个详细页面,另一个去,然后另一个,并且应用程序冻结,因为一个会话阻止另一个。 我不能在整个应用程序中使用一个会话,因为我不能说,通过检查session.dirty,new,deleted属性和应用程序状态处理在表单上编辑的东西变成了脆弱的不可读代码的地狱。
我是否需要实施其他类型的会话处理策略? 我是否需要调整sqlalchemy映射或sql server?
以下是最小的工作示例:
from sqlalchemy import MetaData, Table, Column, FetchedValue, ForeignKey, create_engine
from sqlalchemy.types import BigInteger, String
from sqlalchemy.orm import mapper, relationship, sessionmaker, Session
class Ref(object):
id = None
name = None
id_parent = None
class TableMapper(object):
def __init__(self, metadata, mapped_type):
self._table = None
self._mapped_type = mapped_type
def get_table(self):
return self._table
def set_table(self, table):
assert isinstance(table, Table)
self._table = table
class RefTableMapper(TableMapper):
def __init__(self, metadata):
TableMapper.__init__(self, metadata, Ref)
self.set_table(Table('Ref', metadata,
Column('id', BigInteger,
primary_key = True, nullable = False),
Column('name', String),
Column('id_parent', BigInteger,
ForeignKey('Ref.id'))
))
def map_table(self):
r_parent = relationship(Ref,
uselist = False,
remote_side = [self._table.c.id],
primaryjoin = (
self._table.c.id_parent == self._table.c.id))
mapper(Ref, self._table,
properties = {'parent': r_parent})
return self._table
class Mapper(object):
def __init__(self, url, echo = False):
self._engine = create_engine(url, echo = echo)
self._metadata = MetaData(self._engine)
self._Session = sessionmaker(bind = self._engine, autoflush = False)
ref_t = RefTableMapper(self._metadata).map_table()
def create_session(self):
return self._Session()
if __name__ == '__main__':
mapp = Mapper(r'mssql://username:pwd@Server\SQLEXPRESS/DBName', True)
s = mapp.create_session()
rr = s.query(Ref).all()
s1 = mapp.create_session()
merged = s1.merge(rr)
merged.flush()
s2 = mapp.create_session()
rr1 = s2.query(Ref).all() #application freezes!
答案 0 :(得分:1)
SQL Server的默认隔离模式非常积极地锁定整个表。 (上面的例子看起来好像你发出了UPDATE,然后在前一个事务处于挂起状态时在另一个事务中发出SELECT,尽管session.merge()不接受列表并且上面没有指定表的内容所以很难说。)
无论如何,通常的做法是启用多版本并发控制(SQL服务器将其称为“行版本控制”),以便它能够合理地锁定各个行而不是完整的表:
ALTER DATABASE MyDatabase SET ALLOW_SNAPSHOT_ISOLATION ON
ALTER DATABASE MyDatabase SET READ_COMMITTED_SNAPSHOT ON