简体   繁体   English

Grails 3-具有复合键的域对象并不总是保存

[英]Grails 3 - domain object with composite key not always saving

We recently upgraded from Grails 2.2.4 to 3.2.2. 我们最近从Grails 2.2.4升级到了3.2.2。 Since then, we've been having issues when trying to persist a newly created object with a composite key. 从那时起,在尝试使用复合键持久存储新创建的对象时,我们一直遇到问题。

Let's say we have a domain object called SomeField. 假设我们有一个名为SomeField的域对象。

class SomeField {

    static belongsTo = [parentClass : SomeParentClass]

    static hasMany = [joinedFields : JoinedField]

    joinedFields fetch:'join', cascade:'all-delete-orphan'

}

There's another domain object called JoinedField which belongsTo one of its two SomeField members. 还有一个名为JoinedField的域对象,该对象属于其两个SomeField成员之一。 It uses both fields to create its composite key. 它使用两个字段来创建其组合键。

class JoinedField {

    SomeField field1
    SomeField field2

    static belongsTo = [field1: SomeField]

    static mapping = {
        id composite: ['field1', 'field2']

        columns {
            field1  column:'F1_ID'
            field2  column:'F2_ID'
        }

        version false
    }

    static contraints = {
        field1(nullable:false)
        field2(nullable:false)
    }

    def getPk = {
        ['field1':field1.id, 'field2':field2.id]
    }
}

Field2 always exists already in the database and gets looked up from there when adding it the JoinedField object. Field2始终已经存在于数据库中,并在其中添加JoinedField对象时从那里查找。

When field1's parent is all new and we save it, field1 is saved and the JoinedField object is persisted with the two keys as expected. 当field1的父项全部是新父项并且我们将其保存时,将保存field1并使用预期的两个键持久保存JoinedField对象。 When field1's parent has already been saved, however, and then the JoinedField object is added to an existing field1 object or a new field1 object and saved, the JoinedField object is not persisted. 但是,如果已保存field1的父级,然后将JoinedField对象添加到现有的field1对象或新的field1对象中并保存,则JoinedField对象不会持久化。

Example: 例:

someParent.addToFields( aRealField1 )

def jf = new JoinedField()
aRealField1.addToJoinedFields( jf )
jf.field2 = SomeField.findById( 1234 )

someParent.save()

So -- if aRealField1 belongs to a parent not yet saved, then 'aRealField1' and 'jf' are persisted when aRealField1's parent is saved. 因此-如果aRealField1属于尚未保存的父项,则保存aRealField1的父项时将保留'aRealField1'和'jf'。 If field1's parent already exists, then 'jf' is not saved when aRealField1's parent is saved, even though aRealField1 does get saved fine. 如果field1的父级已经存在,则即使保存aRealField1也可以,但保存aRealField1的父级时不会保存'jf'。

I assume GORM is not recognizing this object as dirty and needing persistence because it's created from two existing objects, even though it is a new object itself made of those two existing objects. 我认为GORM不会将其识别为脏对象并需要持久性,因为它是由两个现有对象创建的,即使它本身是由这两个现有对象组成的新对象。 But I don't see a way to force this cascading save to happen in this case. 但是,在这种情况下,我看不出有办法强制进行这种级联保存。 It was all working with Grails 2.2.4. 所有这些都与Grails 2.2.4一起使用。

Update: 更新:

It seems that there is a transaction issue at the heart of this. 似乎这是核心问题。 We have a controller for SomeParentClass where the request is received to save the object. 我们有一个SomeParentClass的控制器,在其中接收到保存对象的请求。 That in turn calls a Transactional service class within a 'withTransaction' closure to allow us to roll back the transaction if any errors occur. 依次调用“ withTransaction”闭包内的Transactional服务类,以允许我们在发生任何错误时回滚事务。 The service does numerous saves and we want to keep the ability to roll back the whole set on any error. 该服务可节省大量费用,我们希望保留在发生任何错误时回退整个集合的功能。

If we remove the @Transactional annotation on the service, and the withTransaction closure in the controller, then the JoinedField class above saves just fine. 如果我们删除服务上的@Transactional批注以及控制器中的withTransaction闭包,那么上面的JoinedField类将保存得很好。 However, we then are lacking the rollback of the complete set of changes when an error occurs. 但是,当发生错误时,我们将缺少对完整更改集的回滚。

We have removed the withTransaction in the controller and can handle the rollback in the service when it is still annotated as Transactional, but then the JoinedField object still fails to persist as described. 我们已经删除了控制器中的withTransaction,并且当该服务仍被注释为Transactional时可以处理该服务中的回滚,但是JoinedField对象仍然无法如所描述的那样持久。

Example: Code with rollback only in service is cleanest: 示例:仅在服务中具有回滚的代码是最干净的:

import grails.transaction.Transactional

@Transactional
class BuildObjectService {

    def buildObject(def json) {
        try {
            // build the object from json representation
        }
        catch( Exception e ) {
            transactionStatus.setRollbackOnly()
            throw e
        }
    }
}

Any ideas on why this fails to persist when within a transactional service but does fine when that annotation is removed? 关于为什么在事务服务中无法做到这一点但删除该注释后仍能解决的任何想法?

Resolved this issue by upgrading org.grails:grails-datastore-gorm*, org.grails:grails-datastore-core and org.grails.plugins:hibernate5 to the latest 6.1.6.RELEASE version. 通过将org.grails:grails-datastore-gorm *,org.grails:grails-datastore-core和org.grails.plugins:hibernate5升级到最新的6.1.6.RELEASE版本,解决了此问题。

This replaces the 6.0.3 versions that the org.grails:grails-depenedencies was pulling in. 这将替换org.grails:grails-depenedencies引入的6.0.3版本。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM