我知道有很多关于此的类似帖子,但我找不到我的问题的明确答案。
为了使其尽可能简单,假设我有这样一个实体:
@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),所以也许我在这里遗漏了一些基本的东西,因为这个问题似乎归结为一种非常常见的使用模式。
请赐教!