У меня очень странная проблема с @Resource
аннотированными классами Домена и автоматически сгенерированным RestfulController
для указанных классов. В частности, моя сущность Domain Product
имеет свойство Collection hasMany
parts
в типе Part
, и я пытаюсь использовать PUT для обновления parts
в экземпляре Product
, и обновление не работает.
Моя содержащаяся в домене сущность Product
, которая определяется следующим образом:
package com.test
import grails.rest.Resource
@Resource(uri='/rest/product', formats=['json', 'xml'])
class Product
{
Integer completed // Completed quantity
Set<Part> parts // Each requested product has a list of requested parts
static hasMany = [parts: Part]
static constraints = {
parts minSize: 1
}
}
, а определение моей части следующее:
package com.test
import grails.rest.Resource
@Resource(uri='/rest/part', formats=['json', 'xml'])
class Part
{
String identifier // Each part must have an identifier
Double weight // How much does each unit of this product part weigh
Product product // The product request that this part request belongs to
static belongsTo = [product: Product]
static constraints = {
}
}
В моем BootStrap.groovy Я уже создал две части, каждая для двух продуктов (всего 4 части).
package broken.put.array.property
import com.test.Product
import com.test.Part
class BootStrap {
def init = { servletContext ->
Product product = Product.findOrSaveWhere(completed: 4)
product.addToParts(new Part(product: product, weight: 1237, identifier: "part1").save(failOnError: true, flush: true))
product.addToParts(new Part(product: product, weight: 1236, identifier: "part2").save(failOnError: true, flush: true))
product.save(failOnError: true, flush: true)
Product product2 = Product.findOrSaveWhere(completed: 3)
product2.addToParts(new Part(product: product2, weight: 1235, identifier: "part3").save(failOnError: true, flush: true))
product2.addToParts(new Part(product: product2, weight: 1234, identifier: "part4").save(failOnError: true, flush: true))
product2.save(failOnError: true, flush: true)
}
def destroy = {
}
}
Итак, после запуска приложения я делаю следующее:
$ curl -X PUT "http://localhost:8080/product/1" -H "Content-Type:application/json" -d '{"parts" : [{ "id": 1 }], "completed": 3}'
, чтобы обновить свойство parts
Collection Product
с идентификатором 1 из 2 частей в1 частьПо какой-то причине обновление всегда заканчивается тем, что на самом деле не изменяет свойство parts вообще, как видно из ответа JSON:
{"id":1,"parts":[{"id":2},{"id":1}],"completed":3}
Поэтому я решил отладить это, добавив в мою @Resource
аннотацию *Атрибут 1030 *, который указывает на мой собственный контроллер, где я могу сделать println
. Я скопировал тело метода update()
из стандартного RestfulController и добавил в него операторы println, чтобы выяснить, где может быть проблема. После некоторых экспериментов я узнал, что после установки instance.properties
в getObjectToBind()
коллекция parts
внутри по-прежнему имеет старые значения (объекты Part с ID 1 и 2) вместо того, что я отправила в теле JSONPUT (один объект Part с идентификатором 1).
Но я обнаружил еще кое-что очень удивительное, если бы поместил оператор println
, выводящий instance.properties
ДО строки, где он установлен на getObjectToBind()
, обновление PUT
неожиданно работает с моим предполагаемым поведением. Ничего кроме этого места не подойдет. И это должно быть печать содержимого instance.properties
.
Поскольку я понимаю, что это граничит с voodoo, я создал проект изолированного тестового примера в виде zip-файла, который я загрузил в TinyUpload . Моя версия Grails 3.3.6. Хотелось бы услышать от любого, кто когда-либо видел это, или кто может помочь мне понять, почему это происходит. Все, что вам нужно сделать, это разархивировать zip-файл в папку и запустить его с помощью grails dev run-app. В текущем состоянии кода строка println
(строка 42) закомментирована в src/main/groovy/com/test/ReactAdminRestfulController.groovy
, поэтому вы можете видеть то, что я описал выше. Но раскомментирование этой строки и перезапуск приложения приведут к правильному поведению.