Skip to content

Instantly share code, notes, and snippets.

@nobeans
Created October 15, 2015 07:31
Show Gist options
  • Save nobeans/7eece52d558b31a0fdc2 to your computer and use it in GitHub Desktop.
Save nobeans/7eece52d558b31a0fdc2 to your computer and use it in GitHub Desktop.
GORMでFloat[]を使うには(PostgreSQL)

GORMでFloat[]を使うには(PostgreSQL)

問題

何も対策せずにドメインクラスにFloat[]型のプロパティを定義すると、RDBMS上ではバイナリ型(BLOB型)としてマッピングされてしまう。

具体的には、配列をJava的にシリアライズしたバイト列が格納される。 GORMを通してアクセスする分には、ロード時に自動的にデシリアライズされるので、一見特に問題がないようにみえる。

しかし、これはあまりよろしくない。 直接SQLで条件指定や集計等などが100%不可能であるし、GroovySQLなどで取り出した場合に、デシリアライズの手間がかかってしまう。

解決策

PostgreSQL Extensionプラグインを使う

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側で独自にデシリアライズする

何らかの(主に政治的)制約によりプラグインを入れるのが難しい場合、Groovy SQL側で独自にデシリアライズすることで一応クリアできる。

(参考) 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]
        }
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment