【发布时间】:2018-01-15 18:37:04
【问题描述】:
详细地说,大多数关系数据库都有数据库约束的概念。这是 Postgres documentation on constraints。 Datomic 提供了哪些工具来约束数据或在存储的数据上维护一组不变量?
【问题讨论】:
标签: database clojure transactions datomic
详细地说,大多数关系数据库都有数据库约束的概念。这是 Postgres documentation on constraints。 Datomic 提供了哪些工具来约束数据或在存储的数据上维护一组不变量?
【问题讨论】:
标签: database clojure transactions datomic
编辑 2019-06-28: 由于 0.9.5927(Datomic On-Prem)/480-8770(Datomic Cloud),Datomic 通过 Attribute Predicates、@987654322 支持更精细的写入时间验证@ 和 Entity Predicates。这使得大部分初始答案无效或不相关。
特别是,注意实体谓词接受数据库值作为参数,因此它们实际上可以强制跨多个实体的不变量。
默认情况下,Datomic 仅对可以写入的数据实施非常有限的一组约束,主要包括:
:db.type/string 的属性
[:my.app/id "fjdsklfjsl"]lookup-ref 未解析为现有实体,则[:db/add [:my.app/id "fjdsklfjsl"] :my.app/content 42] 之类的操作将失败:db/add 2 个不同的值用于同一实体-属性对。(我可能忘记了一些,如果有,请发表评论。)
特别是在撰写本文时,没有内置方法可以将自定义验证或“外键”约束添加到给定属性。
但是,将Transaction Functions 和speculative writes (a.k.a db.with()) 结合使用可以为您提供一种强制执行任意不变量的强大方法。例如,您可以将事务包装在一个事务函数中,该函数使用db.with() 推测性地应用该事务,然后搜索推测性结果以查找不变的违规,如果找到则抛出异常。您甚至可以通过在 Datalog 中实现“搜索不变违规”部分来使此事务功能非常通用。
以下是 API 的示例:
[:myapp.fns/checking-invariants
;; a description of the invariant
{:query
[:find ?message ?user-id
:in $db-before $db-after ?tx-data ?tempids ?user-id
:where
[$db-before ?user :myapp.user/id ?user-id]
[$db-before ?user :myapp.user/email ?email-before]
[$db-after ?user :myapp.user/email ?email-after]
[(not= ?email-before ?email-after)]
[(ground "A user may not change her email") ?message]]
:inputs ["user-id-12342141"]}
;; the wrapped transaction
[[:db/add 125315815291 :myapp.user/email "hello.world@yopmail.com"]
[:db/add 125315815291 :myapp.user/name "Foo Bar"]]]
这是:myapp.fns/checking-invariants 的(未经测试的)实现:
{:db/ident :myapp.fns/checking-invariants,
:db/fn #db/fn{:lang :clojure,
:imports [],
:requires [[datomic.api :as d]],
:params [db invariant-q tx],
:code
(let [{:keys [query inputs]} invariants-q
{:keys [db-before db-after tx-data tempids]}
(d/with db tx)]
(when-some [violations (apply d/q query
db-before db-after tx-data tempids
inputs)]
(throw (ex-info
"Transaction would violate invariants."
{:tx tx
:violations violations
:t (d/basis-t db-before)})))
tx)}}
限制:
db.invoke() 在对等节点上执行验证
【讨论】:
一种方法是使用修改数据并在修改期间进行约束验证的事务函数:http://docs.datomic.com/database-functions.html#uses-for-transaction-functions
【讨论】: