Grails - 非空属性仅对并发用户引用空值或瞬态值错误
Posted
技术标签:
【中文标题】Grails - 非空属性仅对并发用户引用空值或瞬态值错误【英文标题】:Grails - not-null property references a null or transient value error only with concurrent users 【发布时间】:2014-12-21 13:28:16 【问题描述】:我们在 Grails 中开发的一个 Web 应用程序最近进入了压力测试阶段,现在我们遇到了这个问题,似乎只有当多个用户(不同的用户帐户)同时执行相同的操作时才会发生时间。这些操作是事务性的,但它们都不是在同一个对象上执行的(正在创建新对象)。我们得到的例外是:
类:org.hibernate.PropertyValueException 消息:非空属性引用空值或瞬态值:xxx.xxx.xxx.domains.PathSections._PathViews_sectionsBackref
这通常是异常的样子,所涉及的域类有一些变化。更糟糕的是,当发生此错误时,涉及列表的所有其他事务操作都将导致相同的错误(不同的域类),无论是否同时完成。然后需要重新启动服务器以临时修复它。以下是相关领域类的一些 sn-ps:
class PathViews extends View
static constraints =
molreports(nullable:true)
molorders(nullable:true)
otherreports(nullable: true)
sections(nullable:true)
addendum(nullable:true)
images(nullable:true)
edits(nullable:true)
List sections
List otherreports
List molreports
List molorders
List images
PathEdits edits
PathSections addendum
int headings
String accid
static auditable = true
static hasMany = [sections: PathSections, otherreports: PathSections, molorders:MolecularOrder, molreports: MolecularReport, images: PathLocalImage]
static mappedBy = [sections: 'pathviews', otherreports: 'reports']
class PathSections extends Sections
static constraints =
pathviews(nullable:true)
reports(nullable:true)
static auditable = true
static belongsTo = [pathviews: PathViews, reports: PathViews]
我最初认为可行的一个临时修复是使大多数事务服务方法同步。这似乎只是使错误更加随机且不可重复。我还仔细检查了其他事情,例如确保 hasMany 关系的子对象在添加到父对象之前未显式保存并使用 domainClass.lock(id) 而不是 domainClass.get(id)。当只有一个用户使用该应用程序时,根本不会发生此问题。
使用的版本:
Grails:2.3.7 休眠:3.6.10.10
任何帮助都将不胜感激,因为我现在快要拔掉我剩下的头发了,如果需要更多信息,请告诉我。
谢谢! 肖恩。
【问题讨论】:
【参考方案1】:您应该做的第一件事是升级到同一次要版本中的最新版本 - 2.3.11。这不太可能有帮助,但你可能会很幸运。
集合很棘手,因为它们被认为是所属域类的属性,但表示为多个数据库记录。所以你有一些有趣的效果,比如无法正确锁定更新实例;给定“作者有很多书”,如果您显式锁定作者实例,则生成的 SQL 类似于select ... from author for update where id=?
。但是您不能锁定书籍,如果可以并且集合更大,您会想要锁定一百万个集合实例吗?因此,您不可避免地会遇到多对多并发问题,其中子项在***实例之间共享,甚至在执行与同步请求重叠的 Ajax 请求时也会遇到并发问题。
这只是数据正确性——在 GORM 中使用集合进行持久性的潜在性能成本相当高。我 4 年前所做的 This talk 现在和当时一样重要,它演示了 1-many 和 many-many 的性能问题,并显示了不使用集合且不受任何影响的代码这些问题。你确实需要做更多的工作,但我认为这是一个很小的代价。
【讨论】:
感谢伯特的建议。不幸的是,更新似乎没有太大帮助,完全重新实现所有集合的开发有点晚。不过,对于下一次修订,这绝对是值得关注的东西。您对我们如何能够及早捕获异常以便我们至少可以避免重启服务器有什么建议吗? 无需同时为每个人做 - 您一次可以转换 1 个集合。这应该只影响几个集合。对于 1-many,如果您的映射生成外键(相对于连接表),则转换相当小;创建一本书不是通过author.addToBooks()
,而是使用明确的author
,例如new Book(title: '...', author: author).save()
。对于只读反向访问,很容易用查询替换集合,例如def books = Book.findAllByAuthor(this)
。无需更改数据库。
如果需要订购商品,在此实施中将如何解决?我是否必须以某种方式手动设置数据库中已经存在的 idx 字段?
是的,这有点工作。如果您查看 Hibernate 方法,它们会从连接表中删除所有内容并重新插入以确保所有 idx 值都是正确的。希望有了领域知识,您可以做一些更聪明的事情 :) 这是两种方法之间的一个差距,我没有过多关注;通常插入顺序不如可用于排序的属性的值重要(例如最后更新) - 如果您不使用 Hibernate,您将如何做到这一点?以上是关于Grails - 非空属性仅对并发用户引用空值或瞬态值错误的主要内容,如果未能解决你的问题,请参考以下文章
Spring 和 Hibernate 错误——非空属性引用空值或瞬态值:com.tharaka.model.Employee.designation