【问题标题】:GORM read only columnsGORM 只读列
【发布时间】:2014-12-18 12:00:55
【问题描述】:

我们的大多数表都有一个或多个由数据库设置的列,或者通过触发器,或者我们希望使用数据库默认值(这要求在插入或更新中根本不发送字段)

这包括以 dB 为单位设置的交易日期(因此所有时间都由单一来源非常准确地标记时间戳,而不依赖于任意服务器或 PC 上时间的准确性。

第二个非常常见的用例是说如果客户记录有他的地址和最后登录字段。最后登录字段(和失败登录次数)由系统的另一部分(例如,由网站服务器)设置。例如,当操作员或客户编辑他们的地址时,GORM 提供的当前过于简单的 CRUD 系统会覆盖这样的字段。这是因为 GORM 在其 update 和 insert 语句中包含每个字段,即使它为 null,或者它没有被更改。

我们需要一种方法来清除插入和更新中的字段,但仍要在读取调用中使用它。 IE。一个真正的“只读”属性。

我们试过这个:

    failedLoggins editable: false, attributes: [readonly:true]

这对生成的 SQL 没有影响(甚至不会影响脚手架的 UI - 它在创建和编辑时仍然可以编辑,至少在 grails 2.4.4 中,但那是另一回事了)

当我们确实想明确写入其中一个字段时,例如登录失败的次数,我们将求助于使用嵌入式 SQL。

我看到了这个帖子:http://grails.1312388.n4.nabble.com/Read-Only-columns-td1390025.html

它问了完全相同的问题,但只给出了一个解决方案,就是这个插件:

http://grails.org/plugin/extended-gorm-mappings

很遗憾,该插件自 2010 年以来一直没有更新,仅适用于 1.3。我们需要一些适用于 2.4.4 的东西。

任何具有多个编辑独立字段的系统的grails应用程序都需要这样的东西,或者进行广泛的锁定(这通常是不可能的)。

例如操作员打开客户详细信息进行编辑,编辑可编辑的内容(例如地址),然后操作员无法登录网站(不同的 grails 或非 grails 应用程序),然后操作员保存玩家详细信息。如果保存包含 numberOfFailedLogins 字段,则系统将失败。如果打开播放器详细信息进行编辑锁定播放器,则播放器将无法登录,因为更新“lastLoggedIn”或“numFailedLogins”将由于锁定而无法写入。解决方案非常简单 - 只读列。另一种方法是将每个只读类型字段放在他们自己的表中,但这将是站不住脚的(并导致数百个单字段表)

或者我们回到使用没有此类问题并完全控制的 MyBatis。可悲的是,没有很好的 gails 的 mybatis 插件。

【问题讨论】:

  • 只读列的另一个好用例是,如果您有一个一对多,即一侧的列表。在这种情况下,通过设置它的 belongsTo 字段来添加一个元素似乎是不对的,因为它没有说明它在列表中的顺序。将belongsTo 设置为只读会很好,因此您只能在hasMany 端使用addToX() 方法。不幸的是,用抛出异常的方法覆盖 belongsTo 设置器会破坏 GORM 本身。

标签: grails triggers grails-orm


【解决方案1】:

您可以将derived properties 用于字符串和数字属性:

class Batch {
    String name
    Integer timesRun

    static mapping = {
        timesRun formula: 'times_run' //times_run is a column in the "batch" table
    }
}

在上面的代码中,timesRun 将从数据库中读取,但在插入和更新中被忽略,因为 Hibernate 认为该列是计算的。

更新了示例,因为原始示例可能具有误导性

【讨论】:

  • 非常感谢您的回复。不幸的是,我不相信计算列地址或解决这两个用例 - 我们需要找到一种方法,不将某些列放在插入和更新调用 SQL 中,而是将它们包含在读取调用中。
  • 那么简单计算列和只读列有什么区别?
  • 只读列是数据库中的一组(例如,通过触发器)。例如创建日期或余额。它们是为了安全和性能而在数据库本身中计算和写入的(想想成千上万的并发用户、许多组件(不是所有的圣杯)以及处理大量现金的系统)。计算列是完全不同的东西。
  • 也许你误解了我的例子(复制一个列对我来说是最简单的测试,但它可能会误导你):你可以将 Hibernate 未知的列放入formula,例如你的failedLogins 列它不必出现在域类的其他任何地方(但它确实必须存在于数据库中,显然)。根据您的要求,它将在选择时被读取,但不受插入或更新的影响。
【解决方案2】:

这可能没有具体回答您的问题,但您可以使用 dynamicUpdates 告诉 GORM 仅更新在当前会话期间已更改的域对象的属性。因此,只要您不更改代码中的“只读”属性,它就不会在 Grails 生成的 SQL 更新语句中设置。为了增加安全性,您可以覆盖(和 noop)setter,以便您的代码永远不会更改该属性。

https://grails.github.io/grails-doc/latest/ref/Database%20Mapping/dynamicUpdate.html

dynamicUpdates 的一个缺点是它可能会降低 Hibernate 查询缓存的用处。但是,似乎一些 Grails/Hibernate 专家建议您无论如何都禁用查询缓存(至少在旧版本的 Grails 中)。不确定 Grails 2.4+ 是否如此

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-10-06
    • 1970-01-01
    • 1970-01-01
    • 2018-04-28
    • 2018-04-08
    • 1970-01-01
    • 2016-06-14
    • 2010-12-27
    相关资源
    最近更新 更多