当Grails GORM遇上PostGIS

作者: 胡键 | 来源:发表于2018-12-14 21:35 被阅读63次

    由于近期项目性质的缘故,我们用到了PostGIS。又因为我们后端用到的工具比较特殊且在国内相对少见,故有必要写一下。

    先说说我们的工具:

    • grails
    • grails postgresql extensions插件

    知道Grails的人都知道GORM的妙处,并且由于Hibernate现在已经将Hibernate Spatial纳入官方发行包,而后者已经支持PostGIS,按理只需按照文档进行配置就好了。可问题就出在由于使用了grails pg extensions插件,其方言和支持PostGIS的方言有冲突,而我又是一个贪心的人,希望能同时获得两者的好处,自然就得折腾一番。

    好在,问题最终得以解决,现将解决方案随手记下,灌水一篇。

    引入PostGIS的相关依赖

    在build.gralde中,添加如下两行:

    compile "org.hibernate:hibernate-spatial:5.2.17.Final"
    compile "com.vividsolutions:jts:1.13"
    

    注意其版本与 hibernate-core 保持一致。

    修改方言

    根据Hibernate的文档所述,需要采用新的 PostgisDialect 来替代原有的PG方言方能在Domain Class中使用PostGIS的类型。这下问题来了,由于Grails PG Extensions插件本身引入了一些类型扩展,如数组、jsonb等,也重新引入了一个新的方言:net.kaleidos.hibernate.PostgresqlExtensionsDialect。

    如何避免二选一呢?一个偷巧的方法就是:重新定义一个方言。因为PostgresqlExtensionsDialect和PostgisPG94Dialect都扩展了PostgreSQL94Dialect,并且两个方言不过都是对现有Hibernate类型的扩充。既然如此,那就新增加一个方言:

    @CompileStatic
    class Postgis94ExtensionsDialect extends PostgisPG94Dialect {
    
        private static final String SEQUENCE_PER_TABLE = 'dataSource.postgresql.extensions.sequence_per_table'
    
        /**
         * Register postgresql types
         */
        Postgis94ExtensionsDialect() {
            super()
            registerColumnType(Types.ARRAY, 'array')
            registerColumnType(ArrayType.LONG_ARRAY, '_int8')
            registerColumnType(ArrayType.INTEGER_ARRAY, '_int4')
            registerColumnType(ArrayType.ENUM_INTEGER_ARRAY, '_int4')
            registerColumnType(ArrayType.STRING_ARRAY, '_varchar')
            registerColumnType(ArrayType.DOUBLE_ARRAY, '_float8')
            registerColumnType(ArrayType.FLOAT_ARRAY, '_float4')
            registerColumnType(ArrayType.UUID_ARRAY, '_uuid')
            registerColumnType(HstoreMapType.SQLTYPE, 'hstore')
            registerColumnType(JsonMapType.SQLTYPE, 'json')
            registerColumnType(JsonbMapType.SQLTYPE, 'jsonb')
        }
    }
    

    以新的PostgisPG94Dialect为父类就绕过了上面的问题。

    配置

    接下来就简单了,在application.yml中修改Hibernate的方言:

    hibernate:
        dialect: …….Postgis94ExtensionsDialect
    

    既然配置好了,那就测试一下呗。

    测试

    作为一个有自我要求的程序员,当然得写自动化测试,:)

    测试用的Domain Class:

    class MyDomain {
    
        Map kvPair
        String[] strings
        Point location
        LocalDateTime dateCreated
    
        static mapping = {
            kvPair comment: 'Jsonb示例', type: JsonbMapType
            strings comment: '数组示例', type: ArrayType, params: [type: String]
            location comment: '位置信息'
        }
    }
    

    上述的PostGIS会被映射到PostGIS的geometry类型,其他由grails pg extensions插件引入的类型也会被映射到对应的类型。下面是GORM自动生成的数据库表:

    # \d my_domain
                                             Table "public.my_domain"
        Column    |            Type             | Collation | Nullable |                Default
    --------------+-----------------------------+-----------+----------+---------------------------------------
     id           | bigint                      |           | not null | nextval('my_domain_id_seq'::regclass)
     version      | bigint                      |           | not null |
     date_created | timestamp without time zone |           | not null |
     strings      | character varying[]         |           | not null |
     location     | geometry                    |           | not null |
     kv_pair      | jsonb                       |           | not null |
    Indexes:
        "my_domain_pkey" PRIMARY KEY, btree (id)
    

    然后是测试Spec:

    void 'test something'() {
        setup:
        myDomainService.save(new MyDomain(kvPair: [key: 'value']
                , strings: ['1', '2'].toArray()
                , location: new GeometryFactory().createPoint(new Coordinate(10, 5))))
    
        when:
        MyDomain myDomain = MyDomain.list()[0]
    
        then:
        myDomain.dateCreated
        myDomain.kvPair.key == 'value'
        myDomain.strings == ['1', '2']
        myDomain.location.x == 10
        myDomain.location.y == 5
    }
    

    测试结果毫无悬念的通过。

    最后,假如你也用vagrant做开发,可以参考我的这个脚本来做provision。

    相关文章

      网友评论

        本文标题:当Grails GORM遇上PostGIS

        本文链接:https://www.haomeiwen.com/subject/tpdnhqtx.html