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