Недавно мы обновили 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
}
}
}
Любые идеи о том, почему это не сохраняется в транзакционной службе, но прекрасно работает, когда эта аннотация удаляется?