何も対策せずにドメインクラスにFloat[]
型のプロパティを定義すると、RDBMS上ではバイナリ型(BLOB型)としてマッピングされてしまう。
具体的には、配列をJava的にシリアライズしたバイト列が格納される。 GORMを通してアクセスする分には、ロード時に自動的にデシリアライズされるので、一見特に問題がないようにみえる。
しかし、これはあまりよろしくない。 直接SQLで条件指定や集計等などが100%不可能であるし、GroovySQLなどで取り出した場合に、デシリアライズの手間がかかってしまう。
PostgreSQLのARRAY型を活用すれば、PostgreSQL的には正しくFloatの配列、のように扱える。 生SQLやGroovy SQLでも素直に処理ができる。
しかし、Hibernateに独自の型定義が必要になってしまう。
そこでPostgreSQL Extensionプラグインを使うと、その辺を一通り賄ってくれるので、簡単に対応できる。
https://github.com/kaleidos/grails-postgresql-extensions
-
BuildConfig.groovyに追加する
compile 'org.grails.plugins:postgresql-extensions:4.6.1'
-
DataSource.groovyにdialectを追加する
dialect = net.kaleidos.hibernate.PostgresqlExtensionsDialect
-
ドメインクラスのmappingでArrayType指定する
import net.kaleidos.hibernate.usertype.ArrayType class MyDomain { Float[] floatArrayMappedAsArrayProp static mapping = { floatArrayMappedAsArrayProp type: ArrayType, params: [type: Float]
何らかの(主に政治的)制約によりプラグインを入れるのが難しい場合、Groovy SQL側で独自にデシリアライズすることで一応クリアできる。
// http://qiita.com/saba1024/items/6c64b31fcc0ca220ac52
@GrabConfig(systemClassLoader=true)
@Grab(group='postgresql', module='postgresql', version='9.1-901.jdbc4')
import groovy.sql.Sql
def dbServer = 'localhost'
def dbName = 'sample_dev'
def dbPort = '5432'
def url = "jdbc:postgresql://${dbServer}:${dbPort}/${dbName}"
def user = 'postgres'
def password = ''
def driver = 'org.postgresql.Driver'
def sql = Sql.newInstance(url, user, password, driver)
// 配列型の値を取得する
sql.eachRow("select float_array_mapped_as_array_prop from my_domain") { row ->
println row.float_array_mapped_as_array_prop.array[0]
println row.float_array_mapped_as_array_prop.array[1]
println row.float_array_mapped_as_array_prop.array[2]
}
// シリアライズされたFloat[]を自力でデシリアライズする
sql.eachRow("select float_array_prop from my_domain") { row ->
new ByteArrayInputStream(row.float_array_prop).withObjectInputStream { ois ->
ois.eachObject {
println it[0]
println it[1]
println it[2]
}
}
}