Grails 3 — объект домена с составным ключом не всегда сохраняется

Недавно мы обновили Grails 2.2.4 до 3.2.2. С тех пор у нас возникли проблемы при попытке сохранить вновь созданный объект с составным ключом.

Допустим, у нас есть объект домена с именем SomeField.

class SomeField {

    static belongsTo = [parentClass : SomeParentClass]

    static hasMany = [joinedFields : JoinedField]

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

}

Существует еще один объект домена с именем JoinedField, который принадлежит одному из двух членов SomeField. Он использует оба поля для создания составного ключа.

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 всегда уже существует в базе данных и просматривается оттуда при добавлении объекта JoinedField.

Когда родитель field1 полностью новый и мы сохраняем его, field1 сохраняется, а объект JoinedField сохраняется с двумя ключами, как и ожидалось. Однако если родительский объект field1 уже сохранен, а затем объект JoinedField добавляется к существующему объекту field1 или новому объекту field1 и сохраняется, объект JoinedField не сохраняется.

Пример:

someParent.addToFields( aRealField1 )

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

someParent.save()

Итак, если aRealField1 принадлежит еще не сохраненному родителю, то 'aRealField1' и 'jf' сохраняются при сохранении родителя aRealField1. Если родитель field1 уже существует, то 'jf' не сохраняется при сохранении родителя aRealField1, даже если aRealField1 сохраняется нормально.

Я предполагаю, что GORM не распознает этот объект как грязный и нуждающийся в сохранении, потому что он создан из двух существующих объектов, хотя сам он является новым объектом, состоящим из этих двух существующих объектов. Но я не вижу способа заставить это каскадное сохранение произойти в этом случае. Все это работало с Grails 2.2.4.

Обновление:

Похоже, что в основе этого лежит проблема с транзакцией. У нас есть контроллер для SomeParentClass, куда поступает запрос на сохранение объекта. Это, в свою очередь, вызывает класс службы Transactional внутри замыкания withTransaction, чтобы мы могли откатить транзакцию в случае возникновения каких-либо ошибок. Сервис делает множество сохранений, и мы хотим сохранить возможность отката всего набора при любой ошибке.

Если мы удалим аннотацию @Transactional в службе и закрытие withTransaction в контроллере, то класс JoinedField, описанный выше, сохранится очень хорошо. Однако нам тогда не хватает отката полного набора изменений при возникновении ошибки.

Мы удалили withTransaction в контроллере и можем обработать откат в службе, когда он по-прежнему помечен как Transactional, но тогда объект JoinedField по-прежнему не сохраняется, как описано.

Пример: Код с откатом только в сервисе самый чистый:

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
        }
    }
}

Любые идеи о том, почему это не сохраняется в транзакционной службе, но прекрасно работает, когда эта аннотация удаляется?


person d.gamms    schedule 14.08.2017    source источник


Ответы (1)


Эта проблема решена путем обновления org.grails:grails-datastore-gorm*, org.grails:grails-datastore-core и org.grails.plugins:hibernate5 до последней версии 6.1.6.RELEASE.

Это заменяет версии 6.0.3, которые загружал org.grails:grails-dependencies.

person d.gamms    schedule 17.08.2017