【问题标题】:JPA merge fails due to duplicate key由于重复键,JPA 合并失败
【发布时间】:2011-02-16 03:26:52
【问题描述】:

我有一个简单的实体 Code,我需要将其持久化到 MySQL 数据库中。

public class Code implements Serializable {

    @Id
    private String key;
    private String description;

    ...getters and setters...
}

用户提供一个完整的文件,其中包含我读取的密钥、描述对,转换为代码对象,然后使用 em.merge(code) 插入单个事务中。该文件通常会有重复的条目,我首先在读取它们时将它们添加到键域上的映射中。

当键仅因大小写而异(例如:XYZ 和 XyZ)时,就会出现问题。当然,我的映射将包含这两个条目,但在合并过程中,MySQL 将这两个键视为相同,并且对合并的调用失败并出现 MySQLIntegrityConstraintViolationException。

我可以通过在读入这些键时将其大写来轻松解决此问题,但我想确切地了解出了什么问题。我得出的结论是 JPA 认为 XYZ 和 XyZ 是不同的键,但 MySQL 认为它们是相同的。因此,当 JPA 检查其已知键列表(或执行任何操作以确定是否需要执行插入或更新)时,它无法找到前一个插入并发出另一个插入,然后失败。这是正确的吗?除了更好地过滤客户端数据之外,还有其他方法吗?

我没有在 Code 类上定义 .equals 或 .hashCode 所以也许这就是问题所在。

【问题讨论】:

    标签: java orm jpa merge persistence


    【解决方案1】:

    我没有在 Code 类上定义 .equals 或 .hashCode 所以也许这就是问题所在。

    嗯,你真的应该,你不想继承 Object 实体的行为。无论您是想使用主键、进行区分大小写的比较,还是使用业务标识,都是另一回事,但您当然不想使用引用相等。您不想要以下实体:

    Code code1 = new Code();
    code1.setKey("abc");
    
    Code code2 = new Code();
    code2.setKey("abc");
    

    被 JPA 视为不同。

    其次,如果您希望能够插入一个以XYZ 为键的实体和另一个以XyZ 为键的实体,那么您应该使用区分大小写的列类型(您可以通过以下方式使varchar 列区分大小写使用binary 属性),否则您将违反主键约束。

    所以,总结一下:

    • 实现equals(和hashCode),决定是否需要对key进行区分大小写的比较。
    • 在数据库级别使用适当的列类型。

    【讨论】:

      【解决方案2】:

      这取决于您的列在 mySQL 中的定义方式。 mySQL 是数据库中的怪人,因为 VARCHAR 和类似的列默认为不区分大小写的匹配。如果您希望 XYZ 和 XyZ 成为不同的合法选项,则需要更改 CREATE TABLE 语句以创建区分大小写的列(有关您的 mySQL 版本,请参阅文档。)

      大概是这样的:

      CREATE TABLE code (
        key VARCHAR(32) BINARY,
        value VARCHAR(32) BINARY
      )
      

      【讨论】:

      • 或者,根据您的 JPA 提供者是谁,您也许可以设置一个 mySQL 方言,以便它在查询本身上指定区分大小写。
      猜你喜欢
      • 2018-08-30
      • 2011-04-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-21
      • 2014-03-19
      • 1970-01-01
      相关资源
      最近更新 更多