1

我知道有很多关于此的类似帖子,但我找不到我的问题的明确答案。

为了使其尽可能简单,假设我有这样一个实体:

@Entity
public class Person implements Serializable {
  @Id
  private Long id;     // PK

  private String name; // business key

  /* getters and setters */

  /* 
     override equals() and hashCode() 
     to use the **name** field 
   */
}

所以,id是PK,name是业务关键。假设我得到一个名字列表,可能有重复,我想存储。如果我只是为每个名称创建一个对象,并让 JPA 使其持久化,那么我的最终表格将包含重复的名称 - 不可接受。

我的问题是您认为最好的方法是什么,考虑到我在下面描述的替代方案和(特别欢迎)您自己的替代方案。

可能的解决方案1:检查实体管理器

在创建新的人员对象之前,请检查是否已管理具有相同人员名称的对象。 问题:实体管理器只能通过PK查询。有什么我不知道的解决方法吗?

可能的解决方案2:通过查询查找对象

Query query = em.createQuery("SELECT p FROM Person p WHERE p.name = ...");
List<Person> list = query.getResultList();

问题:如果请求的对象已经加载到 em 中,这还会从数据库中获取吗?如果是这样,由于解析查询,我想如果非常频繁地执行它仍然不是很有效?

可能的解决方案 3:保留单独的字典

这是可能的,因为 equals() 和 hashCode() 被覆盖以使用 field name

Map<String,Person> personDict = new HashMap<String,Person>();
for(String n : incomingNames) {
  Person p = personDict.get(n);
  if (p == null) {
    p = new Person();
    p.setName(n);
    em.persist(p);
    personDict.put(n,p);
  }
  // do something with it
}

问题 1:为大型集合浪费内存,因为这本质上是实体管理器所做的(虽然不完全是!)

问题 2:假设我有一个更复杂的模式,并且在初始编写后,我的应用程序被关闭,再次启动,并且需要重新加载数据库。如果所有表都显式加载到 em 中,那么我可以轻松地重新填充字典(每个实体一个),但如果我使用延迟提取和/或级联读取,那么就不是那么容易了。

我最近开始使用 JPA(我使用 EclipseLink),所以也许我在这里遗漏了一些基本的东西,因为这个问题似乎归结为一种非常常见的使用模式。

请赐教!

4

2 回答 2

0

确保该字段可以(正确)用作键的唯一方法是在其上创建唯一约束。您可以使用@UniqueConstraint(columnNames="name")或使用@Column(unique = true).

在尝试插入重复键时,EntityManager(实际上是 DB)将引发异常。这种情况也适用于手动设置的主键。

防止异常的唯一方法是对键进行选择并检查它是否存在。

于 2012-08-31T05:20:53.553 回答
0

我能想到的最佳解决方案非常简单,使用唯一约束

@Entity
@UniqueConstraint(columnNames="name")
public class Person implements Serializable {
    @Id
    private Long id;     // PK

    private String name; // business key
}
于 2012-08-30T17:04:15.133 回答