12

JPA と Hibernate を使用して PostgreSQL で CITEXT データ型を使用するのに問題があります。CITEXT は、大文字と小文字を区別しないテキスト データ型を提供することになっていますが、JPA/Hibernate で使用すると、大文字と小文字を区別しない方法で動作しません。他の誰かがこの問題を抱えているか、それを回避する方法を知っていますか? JDBC の問題について言及されているのを見たことがありますが (ごくわずかですが)、それは少なくとも 1 年前にさかのぼり、あまり明確ではありませんでした。

postgres 9.1 で citext として定義された「ニックネーム」列があります。名前付きクエリを使用して行を見つけることができるかどうかを確認するためのテストを行いました。

create table test(
    nickname citext
)

@NamedQuery(name = "Person.findByNickname", 
            query = "SELECT p 
                     FROM Person p 
                     WHERE p.nickname = :nickname")

ニックネームを DB に挿入します。

insert into test values('testNick')

次に、次のコードを実行します。

String nickname = "testNick";

Query q = em.createNamedQuery("Person.findByNickname");
q.setParameter("nickname", nickname);
if (q.getResultList().isEmpty()) {
    return (false);
}
return (true);

これは 'true' を返します (つまり、データベースには既に 'testNick' があります)。

もし私がこの任務を遂行すれば

String nickname = "testnick"; //(lower case 'N') 

もう一度実行すると、「false」が返されます。

列は CITEXT であるため、再び「true」を返すはずです。つまり、大文字と小文字を区別しないテキストです。

JPA と Hibernate の使用。誰にも考えはありますか?

それまでの間、列を varchar に戻し、小文字の関数インデックスを作成しました。そして、データベース関数を使用して検索するためのネイティブ クエリを作成する必要があります。データベースの抽象化を維持するためにこれを行う必要がない方法があるかどうかを知りたいです。

よろしく。

4

2 に答える 2

15

citextデータベース内で使用する大文字と小文字を区別しない演算子を、他の citext 値とともに提供します。

何が起こっていますか

text推測では、JPA 実装は、パラメーター化されたステートメントを作成するときのように、パラメーターの型を明示的に指定しています。citextは演算子を定義しないため、citext = textPostgreSQL は をキャストcitexttext、大文字と小文字を区別するtext = text演算子を使用します。実際には、との比較citexttextは大文字と小文字が区別されます。

これが私が起こっていると思うことです。ダミーデータが与えられた場合:

regress=# CREATE EXTENSION citext;
regress=# CREATE TABLE citest ( x citext );
regress=# INSERT INTO citest(x) VALUES ('FRED'), ('FrEd');
regress=# SELECT * FROM citest;
  x   
------
 FRED
 FrEd
(2 rows)

... citext と不明な文字列リテラルの比較は、citext=citext大文字と小文字を区別せずに次のように解釈されます。

regress=# SELECT * FROM citest WHERE x = 'FRED';
  x   
------
 FRED
 FrEd
(2 rows)

citext...しかし、 とを明示的にtext型指定されたリテラルと比較すると、citext引数がtextusingcitextの暗黙的なテキストへのキャストに変換され、大文字とtext=text 小文字を区別する比較が行われます。

regress=# SELECT * FROM citest WHERE x = 'FRED'::text;
  x   
------
 FRED
(1 row)

むしろ、Hibernate が行っていることは次のようになります。

regress=# PREPARE blah(text) AS SELECT * FROM citest WHERE x = $1;
PREPARE
regress=# EXECUTE blah('FRED');
  x   
------
 FRED
(1 row)

Hibernatetextは文字列がtext.

つまり、PgJDBC を介して Hibernate を取得し、citextデータ型をクエリのパラメータ型として明示的に指定する必要があります。結果は次のようになります。

regress=# PREPARE blah(citext) AS SELECT * FROM citest WHERE x = $1;
PREPARE
regress=# EXECUTE blah('FRED');
  x   
------
 FRED
 FrEd
(2 rows)

citext準備されたステートメントへの明示的な型パラメーターに注意してください。citext特にPgJDBCは型について何も知らないので、それは...興味深い...行うことになります。PgJDBC のsetObject;を使用する Hibernate 用のカスタム データ型ハンドラーを作成する必要があります。それでも、Java と Pg の間で演算子の一貫性の問題が発生します (以下を参照)。

lower()IMOでは、従来の大文字と小文字を区別するタイプや、ILIKEなどを使用した方がはるかに優れています。

また、Hibernate が、列の大文字と小文字の区別について PgJDBC が伝える内容に依存している可能性もあります。少なくとも 9.2-devel の時点では、PgJDBC はcitext型について何も認識していないため、尋ねられると常に「はい、大文字と小文字が区別されます」と表示されます。

トレース

JPA によって実行される実際のクエリを見ずに、それが起こっていることを確認することは困難です。で設定log_statement = 'all'してみてくださいpostgresql.conf。次にSIGHUP、postmaster を使用するpg_ctl reloadか、Pg を再起動して、変更を有効にします。

テストを再実行し、ログを調べます。に表示されているクエリをテストしてpsql、結果を観察します。何が起こっているのかわからない場合は、質問を更新してください。更新する場合は、Hibernate のバージョンと PgJDBC のバージョンも含めてください。

また、Hibernate が、列の大文字と小文字の区別について PgJDBC が伝える内容に依存している可能性もあります。少なくとも 9.2-devel の時点では、PgJDBC はcitext型について何も認識していないため、尋ねられると常に「はい、大文字と小文字が区別されます」と表示されます。

オペレーターの一貫性の問題

警告:citextタイプは、Hibernate がデータベースから出てきたテキストをどのように処理するかに影響を与えることはできません。String.equalsたとえば、メソッドには影響しません。テキストの大文字と小文字を区別しないようにHibernateに指示する必要があります。それ以外の場合、textまたはプライマリ/外部キーがある場合、Hibernate が keyをvarchar要求する状況が発生する可能性があり、返され、DB が (Hibernate によると) 等しくないキーを返したため、かなり混乱します。求められました。エンティティに-backed 文字列と実装を含めると、同様の奇妙なことが発生します。"FRED""FrEd"citextequalshashCode

@Column残念ながら、JPA は、列が大文字と小文字を区別するかどうかについて、マッピングで注釈属性を指定していないようです。いずれにせよ、 Javaには大文字と小文字を区別しない文字列データ型の概念がないため、JPA が指定したとしても、あまり効果がありません。

citextfor キーを使用したり、 andにcitext値を含めたりしない限り、Hibernate の混乱を避けることができるでしょう。equalshashCode

于 2012-08-20T02:21:02.523 に答える
12

私は将来の読者のために答えています。問題は、JDBC が String パラメータを自動的に varchar にキャストするため、比較で大文字と小文字が区別されることです。この動作は、JDBC 接続パラメータ「stringtype」を「 unspecified 」に設定することで変更できます。

JPA を使用している場合は、データ ソース構成に次のように入力します。

<datasource jndi-name="java:jboss/datasources/testDS"
    pool-name="test" enabled="true"
    use-java-context="true" spy="true">
    <connection-url>jdbc:postgresql://localhost:5432/postgres</connection-url>
    <driver>postgresql</driver>
    <connection-property name="stringtype">unspecified</connection-property>
    <security>
        <user-name>postgres</user-name>
        <password>******</password>
    </security>
</datasource>
于 2014-05-18T12:04:37.753 に答える