问题
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