カスタム検索
ラベル Oracle の投稿を表示しています。 すべての投稿を表示
ラベル Oracle の投稿を表示しています。 すべての投稿を表示

2008年3月3日月曜日

OracleとCLOBとGrailsと

OracleのCLOB型はデフォルトでDISTINCT出来なくなっています(パフォーマンスの問題?)
なのでGrailsからクエリを直接実行しようとした場合は注意が必要です。
DISTINCTしなければいいのかというと、そういうわけでもなく、 like は使えるが = は使えないという非常に微妙な状態になります。(Oracle8ではlikeもNGっぽいです)
以下にテストした結果、OKなケースとNGなケースをまとめます。

以下の形のドメインクラス

class Book {
Author author
String title
}


class Author {
static mapping = {
clob type:'text'
}

String clob
}


以下のクエリを実行します。

SELECT DISTINCT a from Author as a // NG

from Author as a WHERE a.clob like '%Clob%' // OK

from Author as a WHERE a.clob = '%Clob%' // NG

SELECT DISTINCT b from Book as b WHERE b.author.clob like '%Clob%' // OK

SELECT DISTINCT b from Book as b WHERE b.author.clob = 'Clob' // NG


Oracleが嫌いになりました :p

2008年2月27日水曜日

GrailsでOracle、CLOB型

OracleではString型はVARCHAR2の4000文字が最大となります。
これ以上長い文字列をいれようと思った場合はCLOB(わけのわからん互換性の低いいやーな感じの型)などを使うのですが、これはStringではないのでStringと同じようには動作しません。
(likeや一致などでエラーになったり)

で、Grails上で使う場合ですが、CLOB型のフィールドを作るにはドメインのmappingに指定してやれば作る事が出来ます。


class Book {
static mapping = {
text type:'clob'
}

String text
}


はい、ですが、上にも書いたとおりCLOB型はStringではないので、検索などを実行するとエラーになります。
CLOBとStringをキャストしてやる必要があるのですが、ここら辺のDBの非互換性を制御するためにあるのがHibernateのDialectというやつです。
GrailsでDialectを設定するには conf/DataSource.groovy を編集します。
dataSourceの部分に dialect を追加してやります。


dataSource {
pooled = false
dialect = org.hibernate.dialect.Oracle9Dialect.class
driverClassName = "oracle.jdbc.OracleDriver"
username = "user"
password = "pass"
}


[2008.03.03] 追記 ---------------------------------------------------
dialectは指定しなくてもいけるみたいです。
クエリ自体は最適化されてるっぽいのでDBを固定出来る場合は書いておいた方がいいかも
-----------------------------------------------------------------

これで検索(Book.findAllByTextLike("妄想") 等)を実行してみると、、、おおっと!エラー
Dialectで方言を吸収してくれるのですが、CLOBの宣言のままだとHibernateは「CLOBとして使いたいんだろうなーこいつ」と判断してくれます。
なので宣言を text にします。


class Book {
static mapping = {
text type:'text'
}

String text
}


これでHibernateは「あー、Oracleで長文使いたいんだねー、じゃあDBの型はCLOBにしとくけどStringとして使えるようにしとくよー」と脳内会話が繰り広げられ無事解決です。

また、このFieldTypeの変換部分は、org.hibernate.types.UserType を継承したUserTypeを作成し、mapping の type にクラス名を指定する事で自分で作成する事も出来るようです。試してませんが :)