我有一个简单的实体代码,我需要将其保存到 MySQL 数据库中。
public class Code implements Serializable {
@Id
private String key;
private String description;
...getters and setters...
}
用户提供了一个包含 key 和描述对的文件,我读取这些文件,将其转换为代码对象,然后使用 em.merge(code) 将其插入到单个事务中。该文件通常会有重复的条目,我通过首先将它们添加到在我读入它们时在关键字段上键入的映射来处理这些条目。
当键仅因大小写不同(例如:XYZ 和 XyZ)时,就会出现问题。当然,我的映射将包含两个条目,但在合并过程中,MySQL 认为这两个键相同,因此合并调用失败并出现 MySQLIntegrityConstraintViolationException。
我可以通过在读入时将键大写来轻松解决此问题,但我想确切地了解出了什么问题。我得出的结论是 JPA 认为 XYZ 和 XyZ 是不同的键,但 MySQL 认为它们是相同的。因此,当 JPA 检查其已知键列表(或执行任何操作以确定是否需要执行插入或更新)时,它无法找到先前的插入并发出另一个插入失败。这是正确的吗?除了更好地过滤客户端数据之外,还有什么解决办法吗?
我没有在 Code 类上定义 .equals 或 .hashCode,所以这可能是问题所在。
最佳答案
I haven't defined .equals or .hashCode on the Code class so perhaps this is the problem.
嗯,你真的应该,你不想继承 Object
实体的行为。无论您是想使用主键、进行区分大小写的比较还是使用业务身份都是另一回事,但您肯定不想使用引用相等性。您不需要以下实体:
Code code1 = new Code();
code1.setKey("abc");
Code code2 = new Code();
code2.setKey("abc");
被 JPA 认为是不同的。
其次,如果您希望能够插入一个以 XYZ
为键的实体和另一个以 XyZ
为键的实体,那么您应该使用区分大小写的列类型(您可以使用 binary
属性使 varchar
列区分大小写),否则您将违反主键约束。
总结一下:
- 实现
equals
(和hashCode
),决定是否需要对key
进行区分大小写的比较。 - 在数据库级别使用适当的列类型。
关于java - 由于重复键,JPA 合并失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2776942/