由于近期项目性质的缘故,我们用到了 PostGIS。又因为我们后端用到的工具比较特殊且在国内相对少见,故有必要写一下。
先说说我们的工具:
知道 Grails 的人都知道 GORM 的妙处,并且由于 Hibernate 现在已经将 Hibernate Spatial 纳入官方发行包,而后者已经支持 PostGIS,按理只需按照文档进行配置就好了。可问题就出在由于使用了 grails pg extensions 插件,其方言和支持 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。
觉得有帮助的话,不妨考虑购买付费文章来支持我们 🙂 :
付费文章