【问题标题】:Database independent string comparison with JPA与 JPA 的数据库独立字符串比较
【发布时间】:2011-11-28 08:59:12
【问题描述】:

我正在使用 JPA(Hibernate 作为提供者)和底层 MySQL 数据库。

我有一张包含德国所有街道名称的表格。每个街道名称都有一个唯一的编号。对于一项任务,我必须找出名称的编号。为此,我编写了一个 JPQL 查询,如下所示

"SELECT s FROM Strassencode s WHERE s.name = :name"

不幸的是,德国的街道名称只有大小写字母不同,例如“Am kleinen Feld”和“Am Kleinen Feld”。显然其中一个是语法错误的,但作为街道名称,两种拼写都是允许的,并且它们都有不同的数字。

当我将 JPQL 查询发送到数据库时,我正在使用

query.getSingleResult();

因为事实上,表中的每个街道名称都是唯一的。但是对于底层的 MySQL 数据库,我会得到一个NonUniqueResultException,因为默认情况下,mySQL 数据库会进行不区分大小写的比较。强制使用 mySQL 的常用方法是编写类似

的查询
SELECT 'abc' LIKE BINARY 'ABC';

如mySQL参考手册11.5.1章节所述,但JPQL中没有对应的关键字。

我的第一次尝试是在课堂上注释字段name

@Column(nullable = false, columnDefinition = "varbinary(128)")

这样注释,数据库会自动进行区分大小写的比较,因为其中一个字符串是二进制的。但是当我使用这个解决方案时,当我从数据库中读取名称并将它们写入文件时遇到了麻烦,因为像 ä、ö、ü 这样的字母会被错误地解释。

我知道,name 字段应该获得一个 uniqueConstraint,这是我的目标之一,但这只有在数据库将进行区分大小写的字符串比较时才有可能。

有没有解决方案,我可以配置 JPA 进行区分大小写的字符串比较,而无需手动微调数据库?

【问题讨论】:

    标签: java mysql jpa jpql


    【解决方案1】:

    为了保持“独立”,就像您对数据库和 JPA 提供者所说的那样,我会避免使用 getSingleResult() 并获取 list() 并在内存中匹配名称。可能你会得到不止一个,但不是 100 个或更多。

    另一种方法是将名称标准化(修剪为小写)保存在新字段中。

    【讨论】:

    • 当我对街道名称进行规范化时,我就有了更多的事实,即两个名称是否相等?标准化的“Am kleinen Feld”和“Am Kleinen Feld”都会将它们变为“amkleinenfeld”,但仍然有不同的数字。
    • 您说“表中的每个街道名称都是唯一的”。有一条街道你可以写成“Milkyway”或“Milky Way”,但你的数据库只有其中一个。当“银河”在您的数据库中时,您不能再添加“银河”,对吧?
    • 你的例子是可能的,因为一个版本包含一个空格,另一个没有。但是当“银河”已经在里面时,我希望可以插入“银河”,但不能插入另一个“银河”。当我查询一个版本时,我只想收到一个结果。换句话说,我希望该列的行为类似于 Java 中的 StringsSet,其中 "Milky Way".equals("Milky way")false
    【解决方案2】:

    似乎没有办法配置 JPA 来解决这个问题。但我发现,不仅可以在表级别设置排序规则,还可以为整个数据库设置它,如Reference Manual 12.1.10 CREATE DATABASE SYNTAX9.1.3.2. Database Character Set and Collation 中所述

    CREATE {DATABASE | SCHEMA} [IF NOT EXISTS] db_name
    [create_specification] ...
    
    create_specification:
        [DEFAULT] CHARACTER SET [=] charset_name
      | [DEFAULT] COLLATE [=] collation_name
    

    我只需要创建数据库:

    CREATE DATABASE db_name CHARACTER SET latin1 COLLATE latin1_general_cs;
    

    有了这个,我可以在字段 name 上放置一个 uniqueConstraint 并插入“Am kleinen Feld”和“Am Kleinen Feld”,当我查询其中一个时,我只会收到一个。

    不过,谢谢你的帮助

    【讨论】:

      【解决方案3】:

      您还编写了一个SQL 查询。这就像一个普通的查询:

      Query sqlQuery = session.createSqlQuery("select * from CITY where name like binary :name').addString('name', name);
      

      注意,代码大致完成了。

      【讨论】:

      • 使用本机查询,我不再独立于数据库,还是您的建议符合 ANSI?这对我来说不是“很高兴”。可能是,如果必须在几个月内切换到 Oracle 数据库,并且我不想在必要时更改代码。
      • 那我会去 PeterMmm 推荐。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-07-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多