2017-02-05 2 views
0

я создал два простой Grails классов домена V3, где местоположение внедренных типа атрибута в родительском Venue как этогоне может получить каскадные сохранить не стирают на встроенном класс иого работать

import java.time.LocalDate 

class Venue { 

    String name 
    LocalDate dateCreated 
    LocalDate lastVisited 
    LocalDate lastUpdated 
    GeoAddress location 

    static hasOne = [location:GeoAddress] 

    static embedded =['location'] 

    static constraints = { 
     lastVisited nullable:true 
     location nullable:true 
    } 
    static mapping = { 
     location cascade: "all-delete-orphan", lazy:false //eager fetch strategy 

    } 
} 


class GeoAddress { 

    String addressLine1 
    String addressLine2 
    String addressLine3 
    String town 
    String county 
    String country = "UK" 
    String postcode 

    static belongsTo = Venue 

    static constraints = { 
     addressLine1 nullable:true 
     addressLine2 nullable:true 
     addressLine3 nullable:true 
     town   nullable:true 
     county  nullable:true 
     country  nullable:true 
     postcode  nullable:true 
    } 
} 

однако, когда я пишу тест интеграции - я обнаружил, что создание каскада для местоположения не работает (мне нужно сохранить местоположение, которое оно больше не трансформируется, прежде чем перейти в место встречи. Также, когда я запускаю удаление на месте с помощью flush: true enabled и запрос для адреса i получить возвращенный внедренный адрес - я думал с флешем: true, я бы увидел, что мой каскад GeoAddress удаляется, но мой тест завершился неудачно, поскольку я не получаю null при использовании GeoAddress.get (loc.id), поскольку я ожидал

@Integration 
@Rollback 
class VenueIntegrationSpec extends Specification { 
    void "test venue with an address"() { 
     when: "create a venue and an address using transitive save on embedded " 
      GeoAddress address = new GeoAddress (addressLine1: "myhouse", town: "Ipswich", county: "suffolk", postcode : "IP4 2TH") 
      address.save() //have to save first - else Venue save fails 

      Venue v = new Venue (name: "bistro", location: address) 
      def result = v.save() 

     then: "retrieve venue and check its location loaded eagerly " 
      Venue lookupVenue = Venue.get(v.id) 
      GeoAddress loc = lookupVenue.location 
      loc.postcode == "IP4 2TH" 
      loc.town == "Ipswich" 

     when: " we delete the venue, it deletes the embedded location (Address)" 
      v.delete (flush:true) 
      GeoAddress lookupLoc = GeoAddress.get (loc.id) 

     then: "address should disppear" 
      lookupLoc == null 
    } 

Я думал, что настроил это правильно, но ясно, что у меня его нет. может ли кто-нибудь выяснить, почему мои каскадные действия для Venue.save() и delete() не каскадируются в запись моего встроенного местоположения (GeoAddress).

заранее спасибо

ответ

0

Если я правильно

cascade: "all-delete-orphan"

понял это требуется, только если у вас есть hasMany=[something:Something]

В вашем случае это hasOne или GeoAddress location, вероятно, будет лучше если я должен был создать такое отношение. Я знаю, что между ними есть небольшое изменение.

Во всяком случае, вы испытываете все теоретические. Я думаю, вам нужно зафиксировать ошибки для начала, чтобы понять, почему он не каскадировал ожидаемое поведение. так как

if (!v.delete(flush:true) { 
    println "--- ${v.errors}" 
} 

или обернуть его вокруг

блок попытка поймать

. У меня была аналогичная проблема с отношением hasMany, и это было связано с записью, совместно используемой с другими таблицами, из-за настройки базовых отношений hasMany. Хитрость в том, чтобы просто удалить запись из самого объекта:

lookupVenue .removeFromLocation(loc)

Как я сказать, что это было hasMany отношение

+0

Я пробовал вот так - и удалил место до удаления места проведения, затем тест затем проходит. Если я удаляю loc delete, он терпит неудачу. когда: «мы удаляем место встречи, оно удаляет встроенное местоположение (адрес)» loc.delete (flush: true) v.delete (flush: true) if (v.hasErrors()) println "errors: $ v.errors» GeoAddress lookupLoc = GeoAddress.get (loc.id) затем: "адрес должен disppear" Venue.get (v.id) == NULL lookupLoc == NULL –

+0

и если вы изменили' hasOne' в 'GeoAddress location' как определенный объект в классе домена, затем сделал его« location (nullable: true) », он удаляется тогда? – Vahid

+0

как раз на работе в mo - придется сделать небольшую реструктуризацию, чтобы попробовать это - обновит вас, когда я получу первый момент, чтобы сделать это –

0

очень странно и слишком устал, чтобы понять это сейчас. Я попробовал внешний объект и встроенный - см. Приведенную ниже модель.

Я написал слишком новые тесты, которые оба работают, но оригинального теста нет. Я делаю что-то странное - просто не заметил этого. Два новых теста выполняют один и тот же поток - только переменные разные - оба работают. поэтому проблема заключается в первом тесте.

пересмотренного тест

@Integration 
@Rollback 
class VenueIntegrationSpec extends Specification { 

    def setup() { 
    } 

    def cleanup() { 
    } 

    //original test - this fails, have to explicitly delete loc to make it work 
    void "test venue with an address"() { 
     when: "create a venue and an address using transitive save on embedded " 
      GeoAddress address = new GeoAddress (addressLine1: "myhouse", town: "Ipswich", county: "suffolk", postcode : "IP4 2TH") 
      address.save() 
      Venue v = new Venue (name: "bistro", location: address) 
      def result = v.save(flush:true) 

     then: "retrieve venue and check its location loaded eagerly " 
      Venue lookupVenue = Venue.get(v.id) 
      GeoAddress loc = lookupVenue.location 
      loc.postcode == "IP4 2TH" 
      loc.town == "Ipswich" 

     when: " we delete the venue, it deletes the embedded location (Address)" 
      //loc.delete(flush:true) 
      v.delete (flush:true) 
      if (v.hasErrors()) 
       println "errors: $v.errors" 

      GeoAddress lookupLoc = GeoAddress.get (loc.id) 

     then: "address should disppear" 
      Venue.get (v.id) == null 
      lookupLoc == null 
    } 

    //new test - external entity - works 
    void "test with tempLocation"() { 
     when: "" 
      TempLocation temp = new TempLocation(name:"will") 
      Venue v = new Venue (name: "bistro", temp: temp) 
      assert v.save(flush:true) 

      Venue lookupVenue = Venue.get(v.id) 

      TempLocation t = lookupVenue.temp 
      assert t.name == "will" 

      //try delete 
      v.delete (flush:true) 


     then : " retrieve temp" 
      TempLocation.findAll().size() == 0 
    } 

    //new test - reuse embedded entity - works 
    void "test with GeoLocation"() { 
     when: "" 
     GeoAddress a = new GeoAddress(town:"ipswich") 
     Venue v = new Venue (name: "bistro", location: a) 
     assert v.save(flush:true) 

     Venue lookupVenue = Venue.get(v.id) 

     GeoAddress ta = lookupVenue.location 
     assert ta.town == "ipswich" 

     //try delete 
     v.delete (flush:true) 


     then : " retrieve temp" 
     GeoAddress.findAll().size() == 0 
    } 
} 

пересмотрено испытуемого - Место.groovy with emebbed GeoAddress

class Venue { 

    String name 
    LocalDate dateCreated 
    LocalDate lastVisited 
    LocalDate lastUpdated 
    GeoAddress location 
    Collection posts 

    //test behaviour 
    TempLocation temp 

    static hasOne = [location:GeoAddress, temp:TempLocation] 
    static hasMany = [posts:Post] 
    static embedded =['location'] 

    static constraints = { 
     lastVisited nullable:true 
     location nullable:true, unique:true 
     posts  nullable:true 
     temp  nullable:true //remove later 
    } 
    static mapping = { 
     location cascade: "all-delete-orphan", lazy:false, unique:true //eager fetch strategy 
     posts sorted: "desc" 
     temp  cascade: "all-delete-orphan", lazy:false, unique:true //remove later 
    } 
} 


class GeoAddress { 

    String addressLine1 
    String addressLine2 
    String addressLine3 
    String town 
    String county 
    String country = "UK" 
    String postcode 

    static belongsTo = Venue 

    static constraints = { 
     addressLine1 nullable:true 
     addressLine2 nullable:true 
     addressLine3 nullable:true 
     town   nullable:true 
     county  nullable:true 
     country  nullable:true 
     postcode  nullable:true 
    } 
} 

новый внешний вариант адреса/места для взлома. dummed вниз версия geoAddress с таким же beongsTo/ограничения логики

class TempLocation { 

    String name 

    //setup birdiectional one to one, cascade owned on venue 
    static belongsTo = [venue:Venue] 

    static constraints = { 
     name nullable:true 
    } 
} 

будет пытаться повторно прочитать на поезде - не знаю, почему первый тест не удается, но рядом два работают нормально .... спать - слишком устал

0

Я думаю, что это неправильная настройка. Embedded означает, что объект встроен в доменный класс. Обычно это обычное POJO и выходит за пределы папки domainclass (и в папке src/groovy). Все поля встроенного объекта включаются в таблицу объекта внедрения. Ион устанавливает связь между двумя объектами domainclass. Поэтому либо используйте встроенные, либо используйте hasOne, но не используйте оба одновременно.

Кроме того, возникла проблема с каскадом, сохраняющим глубоко вложенные объекты, это решено в 3.2.5.

+0

не уверен, что это что-то еще. Я попробовал версию (см. ниже), ссылаясь на класс домена и один с помощью встроенного. проблема перисистов. но я ничего не сделал. единственная разница, которую я вижу, кажется, заключается в использовании geoAddress, a.save(), прежде чем я перейду в конструктор места. Если я удалю это a.save() и разрешаю переходное сохранение с использованием v.save (flush: true), а затем удалить место - тогда встроенное место исчезнет, ​​и тест будет работать! поэтому, если я действительно сохраняю адрес напрямую, это терпит неудачу, и если я не работаю нормально –

+0

, я выполняю эти тесты на grails 3.2.5, groovy 2.4.7 и java 121 –

+0

Попробуйте добавить GrailsWebMockUtil.bindMockWebRequest() перед тестированием. Связывание вложенных объектов является частью плагина контроллера. См. Вопрос + обсуждение, которое я создал об этом на странице https://github.com/grails/grails-core/issues/10436 –

0

нормально - я читал внимательно и посмотрел на разницу - и это происходит, если я сохранить внедренный GeoAddress, прежде чем я передать конструктору место проведения, как этот (модифицированный простой тест)

, когда я добавить дополнительный a.save(), после создания GeoAddress тест завершится неудачно. если я прокомментирую сохранение и повтор - он отлично работает. Не уверен, что это функция или ошибка. Место проведения должно быть транзитивным, так как у GeoAddress есть stati принадлежит.

//new test - reuse embedded entity - works 
    void "test with GeoLocation"() { 
     when: "" 
     GeoAddress a = new GeoAddress(town:"ipswich") 
     a.save() 
     Venue v = new Venue (name: "bistro", location: a) 
     assert v.save(flush:true) 

     Venue lookupVenue = Venue.get(v.id) 

     GeoAddress ta = lookupVenue.location 
     assert ta.town == "ipswich" 

     //try delete 
     v.delete (flush:true) 


     then : " retrieve temp" 
     GeoAddress.findAll().size() == 0 
} 

если кто может прокомментировать ошибки против функции для меня - то в случае необходимости я могу отправить сообщение об ошибке на проекте Grails, чтобы ее исправить. Else, я просто должен тщательно протестировать и убедиться, что я делаю правильные вещи в своем коде

Смежные вопросы