Search code examples
hibernategrailsgrails-orm

grails data integrity violation exception and cause?


The definition of Map is

class Map {

    MapCoordinate center
    Integer zoom
    List path

    static hasMany = [path: MapCoordinate]

    static hasOne = [center: MapCoordinate]

    static constraints = {

    }
}

The definition of MapCoordinate is

class MapCoordinate {

    BigDecimal latitude
    BigDecimal longitude


    static belongsTo = [map: Map]

    static mapping = {

        latitude scale: 9
        longitude scale: 9

    }

    static constraints = {
    }
}

This simple script fails with the following exception

Map map = new Map()

def cent = new MapCoordinate(latitude: 0.123, longitude: 0.2424)

map.center = cent

map.zoom = 5

map.save(flush:true, failOnError: true)

Exception

org.springframework.dao.DataIntegrityViolationException: could not insert: [MapCoordinate]; SQL [insert into map_coordinate (version, latitude, longitude, map_id, path_idx) values (?, ?, ?, ?, ?)]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not insert: [MapCoordinate]

Can someone please explain to me what is the cause of this error? I appreciate your help! Thanks!


Solution

  • I think the problem here is that the MapCoordinate has got only one belongsTo definition. On the other hand, you have got hasOne and hasMany within the Map. I suggest you change the structure of your domain classes to the following (including Randall's suggestion). With the mappedBy you make sure grails is able to identify the two relations correctly.

    class Map {
        Integer zoom
        SortedSet path
        static hasMany = [path: MapCoordinate]
        MapCoordinate center
    
        static mappedBy = [path: "mapPath", center: "mapCenter"]
    
        static constraints = {
            center nullable: true
        }
    }
    
    class MapCoordinate implements Comparable {
        BigDecimal latitude
        BigDecimal longitude
    
        static belongsTo = [mapPath: Map, mapCenter: Map]
    
        static mapping = {
            latitude scale: 9
            longitude scale: 9
        }
        static constraints = {
            mapPath nullable: true
            mapCenter nullable: true
        }
    
        int compareTo(other) {
            if (id) {
                id.compareTo(other.id)
            }
            else {
                return -1
            }
        }
    
    }
    

    By adding the SortedSet you should be able to preserve the ordering of the MapCoordinate entries. However, the MapCoordinate class then has to implement the Comparable interface. Eventually, try to change the code as follows:

    Map map = new Map()
    def cent = new MapCoordinate(latitude: 0.123, longitude: 0.2424)
    map.center = cent
    map.zoom = 5
    map.addToPath(new MapCoordinate(latitude: 0.33, longitude: 0.33))
    map.addToPath(new MapCoordinate(latitude: 0.34, longitude: 0.34))
    map.save(flush: true, failOnError: true)