JPA 2.0通过业务密钥检索实体



我知道有很多类似的帖子,但我找不到一个明确的答案来解决我的问题。

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

@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()被重写以使用字段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),所以也许我在这里缺少了一些基本的东西,因为这个问题似乎可以归结为一种非常常见的使用模式。

请开导我!

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

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

确保字段可以(正确)用作键的唯一方法是在字段上创建一个唯一的约束。您可以使用@UniqueConstraint(columnNames="name")@Column(unique = true)来执行此操作。

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

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

最新更新