citext
提供在数据库中使用的不区分大小写的运算符,以及其他 citext 值。
发生了什么
text
猜测一下,您的 JPA 实现在创建参数化语句时明确指定参数的类型。citext
没有定义citext = text
运算符,因此 PostgreSQL 将citext
to转换为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
usingcitext
的隐式转换为文本,然后进行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)
其中类型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
准备好的语句的显式类型参数。那将是……有趣的……做,特别是因为 PgJDBC 对类型一无所知citext
。您必须为使用 PgJDBC 的 Hibernate 编写自定义数据类型处理程序setObject
;即便如此,您也会在 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
或varchar
主/外键,您可能会遇到 Hibernate 要求该键的情况"FRED"
,它会"FrEd"
返回,并且非常困惑,因为数据库返回的键不等于 - 根据 Hibernate - 它的一个被要求。如果您在实体citext
中包含 -backed 字符串equals
和hashCode
实现,则会发生类似的怪事。
不幸的是,JPA 似乎没有在@Column
映射中指定列是否区分大小写的注释属性。Java无论如何都没有不区分大小写的字符串数据类型的概念,因此即使 JPA 确实指定了它也不会有很多好处。
只要您不使用citext
for 键或在and中包含citext
值,您可能会避免过度混淆 Hibernate 。equals
hashCode