Search code examples
grailsgrails-orm

grails: dont save similar domain object again, use present object


The problem:
I have "route" domain, that has stations (domains).
If I save routes, I should save stations too. (used cascade: 'all' mapping)
But, if station with the same name already exists in DB, I want to use it, and don't create such station anymore.
so the route, I save, must save only new stations.

example:
domain:

class Route {
    String name
    List<Station> stations

    static mapping = {
        stations cascade: 'all', lazy: false
    }
    static hasMany = [
        stations: Station
    ]
}

class Station {
   String name
}

controller/service:

def route = new Route()
route.stations.add(new Station("stationOne"))
route.stations.add(new Station("stationTwo"))
route.save()
//now in db there are 2 stations. 

//now create new route with 2 stations, with one similar to already saved ('stationOne').
def route2 = new Route()
route2.stations.add(new Station("stationOne")) //<-- this one i want to be replaced
                                               // with the inDB one, if i save the route
                                               // in DB must be only one "stationOne"
                                               // and every route must point to it,
                                               // not own "stationOne", if the route saved
route2.stations.add(new Station("stationThree"))

route2.save()
//now i wish in DB there are only 3 stations.
//and route2 has both from DB. And the reference (in list) from route2 to "stationOne"
//inMemory object is now replaced with reference to inDB station object.

i could write code, like "replaceWithDBStationReferences(route)"
But my project is enough big, for testing such things in code.
is it possible to define this somewhere in domain? or any other solutions?


Solution

  • You can make use of the findOrCreate dynamic methods:

    route2.stations.add(Station.findOrCreateByName("stationOne"))
    

    findOrCreateByName is similar to findByName except that where the plain finder would return null (if nothing is found) the findOrCreate will create a new transient instance based on the query parameters, in this case a new Station(name:'stationOne').

    There's also findOrSaveBy... which does the same but also saves the new instance before returning it.