Postgresql CITEXT数据类型问题JPA Hibernate

Postgresql CITEXT数据类型问题JPA Hibernate,hibernate,postgresql,jpa,postgresql-9.1,Hibernate,Postgresql,Jpa,Postgresql 9.1,我在使用JPA和Hibernate使用PostgreSQL中的CITEXT数据类型时遇到困难。CITEXT应该提供不区分大小写的文本数据类型,但当与JPA/Hibernate一起使用时,它的行为不区分大小写。有没有其他人有过这个问题,或者知道解决这个问题的方法?我已经看到一些关于JDBC问题的提及(但非常、非常少),但这至少要追溯到一年前,并不是很清楚 在postgres 9.1中,我有一个“昵称”列定义为citext。我只是做了一个测试,看看它是否可以使用命名查询找到一行,如下所示: crea

我在使用JPA和Hibernate使用PostgreSQL中的CITEXT数据类型时遇到困难。CITEXT应该提供不区分大小写的文本数据类型,但当与JPA/Hibernate一起使用时,它的行为不区分大小写。有没有其他人有过这个问题,或者知道解决这个问题的方法?我已经看到一些关于JDBC问题的提及(但非常、非常少),但这至少要追溯到一年前,并不是很清楚

在postgres 9.1中,我有一个“昵称”列定义为citext。我只是做了一个测试,看看它是否可以使用命名查询找到一行,如下所示:

create table test(
    nickname citext
)

@NamedQuery(name = "Person.findByNickname", 
            query = "SELECT p 
                     FROM Person p 
                     WHERE p.nickname = :nickname")
在数据库中插入昵称:

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”。i、 e.不区分大小写的文本

使用JPA和Hibernate。有人有什么想法吗

同时,我将该列改回varchar,并为小写创建了一个函数索引。我现在必须创建一个本机查询来使用数据库函数进行搜索。我想知道是否有一种方法我不能这样做来维护数据库抽象


注意。

citext
提供了不区分大小写的运算符,可与其他citext值一起在数据库中使用

发生了什么事 猜测一下,您的JPA实现在创建参数化语句时显式地将参数类型指定为
text
citext
没有定义
citext=text
运算符,因此PostgreSQL将
citext
强制转换为
text
,并使用区分大小写的
text=text
运算符。实际上,将
citext
text
进行比较是区分大小写的

以下是我认为正在发生的事情。给定虚拟数据:

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
的隐式转换为text,将
citext
参数转换为
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)
其中绑定参数时将类型指定为
text
,因为Hibernate“知道”字符串是
text

换句话说,您需要让Hibernate通过PgJDBC显式指定
citext
数据类型作为查询的参数类型,结果如下:

regress=# PREPARE blah(citext) AS SELECT * FROM citest WHERE x = $1;
PREPARE
regress=# EXECUTE blah('FRED');
  x   
------
 FRED
 FrEd
(2 rows)
注意准备好的语句中的显式
citext
type参数。那将是。。。有趣的。。。尤其是因为PgJDBC对
citext
类型一无所知。您必须为Hibernate编写一个自定义数据类型处理程序,使用PgJDBC的
setObject
;即使这样,Java和Pg之间也会存在操作员一致性问题(见下文)

在我看来,使用传统的区分大小写的类型和
lower()
ILIKE
等会更好

Hibernate也可能依赖PgJDBC告诉它的列大小写敏感度。至少在9.2版本中,devel PgJDBC对
citext
类型一无所知,所以当被问到时,它总是说“是的,区分大小写”

追踪 如果看不到JPA运行的实际查询,就很难确定发生了什么。尝试在
postgresql.conf
中设置
log\u语句='all'
。然后
SIGHUP
邮政局长,使用
pg_ctl reload
,或重新启动pg以使更改生效

重新运行测试并检查日志。测试您在
psql
中看到的查询以观察结果。如果你不确定发生了什么,用他们更新你的问题。如果更新,还包括Hibernate版本和PgJDBC版本

Hibernate也可能依赖PgJDBC告诉它的列大小写敏感度。至少在9.2版本中,devel PgJDBC对
citext
类型一无所知,所以当被问到时,它总是说“是的,区分大小写”

操作员一致性困难 警告:一旦文本从数据库中出来,
citext
类型不能影响Hibernate如何处理文本。例如,它不会对
字符串.equals
方法产生任何影响。您需要告诉Hibernate您希望它将文本视为不区分大小写的。否则,如果您有一个
text
varchar
主键/外键,您可能会遇到这样的情况:Hibernate请求键
“FRED”
,它会返回
“FRED”
,并且非常混乱,因为DB返回的键与它请求的键不相等(根据Hibernate)。如果在实体中的
equals
hashCode
实现中包含
citext
支持的字符串,则会出现类似的异常情况

不幸的是,JPA似乎没有在
@Column
映射中指定注释属性,以确定该列是否区分大小写。Java,所以即使JPA指定了它,它也不会有很多好处


只要你不使用
citext
作为键,或者在
equals
hashCode
中包含
citext
值,你可能会避免把Hibernate弄得太混乱。我的回答是为了将来的读者。问题是JDBC自动将字符串参数强制转换为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>