当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