当Grails GORM遇上PostGIS

由于近期项目性质的缘故,我们用到了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。

转载自:https://blog.csdn.net/weixin_34337265/article/details/88200704

You may also like...