Grails v3.3.9 GORM defect - one to many with mappedBy, updates can be made to fail

☆樱花仙子☆ 提交于 2019-12-24 19:29:55

问题


Grails v3.3.9 - impacts live/dev and integration test

My scenario is that I had an integration test that kept failing when building a manually created record and one found from DB, but works if you link two manually created records in the test.

I have been trying a thousand tweaks and make sure GORM was doing what it said in docs.

I've pared my code right back and built something simpler that looks structurally like my real stuff but is based on the Airport/Flights model in the GORM user guide (see section 5.1.2 in user guide)

Declare abstract base class like this

Airport.groovy

abstract class Airport{

    String name
    Collection<? extends Flight> outboundFlights = []
    Collection<? extends Flight>  inboundFlights = []

    static hasMany = [outboundFlights:Flight, inboundFlights:Flight]

    static mappedBy = [outboundFlights: 'departureAirport', inboundFlights: 'arrivalAirport']

    static constraints = {
        outboundFlights nullable:true
        inboundFlights nullable:true
    }

    static mapping = {
        tablePerHierarchy false  //multiple tables+joins
    }
}

The declare a concrete class that extends this, that you can use:

UkAirport.groovy

class UkAirport extends Airport {

    String shortCode = "dont know"

    static constraints = {
    }
}

Declare Flight entity with two properties of essentially the same type

class Flight<A extends Airport> {

    String flightCode = "unassigned"

    A departureAirport
    A arrivalAirport

    //setup belongs to
    static belongsTo = [departureAirport:Airport, arrivalAirport:Airport]

    static constraints = {
    }
}

Now here's the integration test; there's a couple of things to note.

1) I precreate an UkAirport in setup (no flights declared for it) 2) I create two manual UkAirports in the actual test - Gatwick and Stansted and save these 3) I set up two flights - one from Gatwick to Stansted, and flight2 from Gatwick to pre-saved Manchester that I find by doing UkAirport.get()

Here's the weird thing. If I run this test as shown here - it will pass because I assert that there are no existing flights to Manchester (assert manchester.inboundFlights.size() == 0).

However if you comment that assert out, and re-run, the test will fail and it will declare that manchester.inboundFlights.size() equals 2 -

It's the same if I'm using the debugger - if stop and look at Manchester before I do the flights2.save() the test will work - if you set the break point after the flights2.save() the manchester.inboundFlights shows 2 entries which is wrong.

class AirportIntegrationSpec extends Specification {

    def setup() {

        Airport manchester = new UkAirport (name:"Manchester", shortCode:"MAN")
        manchester.save(failOnError:true)

    }

    def cleanup() {
    }

    void "two airports and one flight "() {

        given :

        Airport gatwick = new UkAirport (name:"Gatwick", shortCode:"LGW")
        Airport stansted = new UkAirport (name:"Stansted", shortCode:"STN")
        Airport manchester = UkAirport.get(1L)
        assert manchester.shortCode == "MAN"

        gatwick.save ()
        stansted.save ()

        when:
        Flight flight = new Flight<UkAirport>(flightCode:"F123-LGW-STN")
        gatwick.addToOutboundFlights(flight)
        stansted.addToInboundFlights(flight)
        flight.save (failOnError:true)
        assert gatwick.outboundFlights.size() == 1
        assert gatwick.inboundFlights.size() == 0
        assert stansted.outboundFlights.size() == 0
        assert stansted.inboundFlights.size() == 1


        // -all ok until we get to here  - if you evaluate manchester before you use it - test works
        //if you comment out and use before you evaluate it fails !
        assert manchester.inboundFlights.size() == 0  //comment out makes test fail !
        Flight flight2 = new Flight<UkAirport>(flightCode:"F789-LGW-MAN")
        gatwick.addToOutboundFlights(flight2)
        manchester.addToInboundFlights(flight2)
        flight2.save (failOnError:true)
        assert gatwick.outboundFlights.size() == 2
        assert manchester.inboundFlights.size() == 1



        then :""
        flight.id == 1
        flight2.id == 2
        gatwick.inboundFlights.size() == 0
        gatwick.outboundFlights.size() == 2
        stansted.outboundFlights.size() == 0
        stansted.inboundFlights.size() == 1
        flight.departureAirport.name == "Gatwick"
        flight.arrivalAirport.name == "Stansted"

        flight2.departureAirport.name == "Gatwick"
        flight2.arrivalAirport.name == "Manchester"


    }
}

Could someone confirm that this really is a defect and I'll get it raised (the temporary work around in code - is to access the entity selected from the db - before you then build any relationships around it).

My Github project is here and integration test for all this is here.

This is my previous related Stack Overflow question - which I'll update as its essentially boiled down to the above.

来源:https://stackoverflow.com/questions/54466290/grails-v3-3-9-gorm-defect-one-to-many-with-mappedby-updates-can-be-made-to-fa

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!