Point by point.
我有一个遗留的数据库,我必须为它写一些数据处理的工具。使用Mapper模式,没有ORM/ActiveRecord风格,使我在写查询时和ActiveRecord一样容易。它是在漂亮的可组合对象上操作的,类似于SQL条款,屏蔽了SQL注入。
对象是 "被动 "的,允许更多的灵活性/统一性:复杂连接的结果是一个命名的元组,正如一个简单选择的结果一样。没有需要关心的身份,没有具有相同身份的缓存对象。
所有的更新都是明确的;不是 "保存 "一些在其他地方改变的状态,没有钩子在
.save()
上运行,等等。这使得高效的批量更新变得微不足道,而不用担心是否有正确的数据被发送到数据库中。在我的案例中,这两点都是好处。在一般情况下,"这取决于"。例如,我不得不在插入后手动获取数据库生成的ID。明确地运行这个查询是一个有点额外的工作。在我的情况下,能够在一次查询中完成,而不是每条记录一次,是一个巨大的福音。
SQLAlchemy有一个分层设计,允许你访问较低的 "映射器 "层,即使你在上层ORM层声明东西,并且通常在上面操作。例如,在Django中,如果/如果仍有可能的话,这就不那么直接了当了。
在这个例子中,"资源库 "看起来像一个建好的级别
以上
the 'mapper'. The repository could have been built on top of plain DBAPI, but the mapper makes a few things simpler, like nicer parameter binding, named tuples for the result sets, and a wrapper 以上 plain SQL with composable, reusable parts.
映射器还提供了一定程度的数据库独立性。例如,SQL Server和Postgres有不同的方法来连接字符串;映射器提供了一个统一的接口。
你把你的
select
写在你使用它的地方。如果你有一个在不同环境下不断重复使用的选择,你可以把它放到一个方法或函数中。大多数选择都有一个用途,并且是当场建立的。
SQLAlchemy设计的一个很好的特点是,你可以很容易地存储条件和整个
where
子句,并在选择/更新/删除语句中重复使用它们。
Question.query.filter_by(text = text).all()
使用隐式交易。
db.session.query(Question).filter(Question.text == text).all()
使用显式事务。
明确的事务让你对DML感到放心。它们对
select
也很重要,当你查询一个快速变化的数据库,并希望你的几个相关的
select
看到相同的一致状态。
我通常在
sessionmaker
周围写一个微不足道的包装器,并这样写东西。
with my_database.transaction() as trans:
records = trans.query(...)
updated = trans.execute(...).rowcount
# Here the transaction commits if all went well.
当我确定知道没有任何DML应该在这个块中运行时,我使用.readonly_transaction()
,它总是回滚。
在很多情况下,隐式事务是可以的。Django允许你用@transaction.atomic
来装饰一个方法,并有一个半显式的事务控制,在99%的情况下足够了。但有时你需要更精细的粒度。