假设我有实体,其中嵌入了实体。 例如(请忽略缺失的注释,getter / setter等):
@Entity
class User {
private String userId;
private Set<UserOperation> userOperations;
}
@Entity
class UserOperation {
private String someString;
// This is nested referenced entity
private User user;
}
假设我想插入新的UserOperation,我拥有的只是userId。
我可以这样做:
// We just create new user. There is no interaction to get existing from DB. Totally only 1 insert
User user = new User();
user.setId("someId")
UserOperation uOp = new UserOperation();
uOp.setUser(user);
uOp.setSomeString("just op");
em.persist(uOp);
或者我应该只是这样:
// We retrieve existing user. There is interaction to get it from DB. Totally 1 select and 1 insert
User user = em.find("someId")
UserOperation uOp = new UserOperation();
uOp.setUser(user);
uOp.setSomeString("just op");
em.persist(uOp);
这样做的正确方法是什么?
因为从DB角度来看userOperation表只有String用户引用,所以ID应该足够了。 Java需要一个对象。 致电&#34;新用户&#34;我想避免,现有用户的属性被刷新(因为它们都未设置)或JPA试图插入新用户和操作因主键冲突而失败。
欢迎一些例子。
答案 0 :(得分:1)
对于您的用例,EntityManager中特别有getReference()方法。它为您提供了id的实体对象,但是不访问DB来创建它。因此,最好的解决方案是稍微修改一下你的第二个解决方案:
// We retrieve a stub user for given id. There is no interaction with DB
User user = em.getReference("someId", User.class);
UserOperation uOp = new UserOperation();
uOp.setUser(user);
uOp.setSomeString("just op");
em.persist(uOp);
<强>解释强>
getReference()与find()具有相同的逻辑含义,但它会调用DB。结果是它不检查DB表中是否存在具有给定id的行,并且您获得的对象尚未包含数据。但是,在调用get方法时,该对象完全能够加载附加数据。因此,即使通过getReference()检索对象也完全可用 - 实际上它的工作方式与延迟加载相同。
对您的第一个解决方案的旁注:
第一个解决方案不起作用,因为它会创建一个新的实体user
然后在将实体存储到数据库(如果它是级联的)时它会失败(持久化总是调用insert并且它会尝试插入用户使用与DB中存在的相同的ID,或者如果用户不是UserOperation
,则会失败。要解决此问题,您需要在致电em.merge(user)
之前致电 em.persist(userOperation)
。但同样,这会以与em.find()
相同的方式调用选择数据库。
答案 1 :(得分:0)
执行此操作的最佳方法是使用第二个示例。我们应该总是尝试直接从db使用实际对象。仅使用数据库参考将会更糟糕。
现在具体谈到Hibernate,使用整个对象更有意义,特别是因为Hibernate的级联,可以和将来(如果设置了级联)更新你持久存储到数据库的子实体。 / p>
好吧,我不得不承认,总是从数据库中获取对象可能会导致一些性能问题,尤其是在数据库获取大量数据之后,因此实现良好且连贯的模型实体始终很重要,并且还要跟踪数据库从您的应用程序中点击,并尝试保持生成的查询较少。
例如,您自己的示例(第二个)干净且易于理解,我会坚持使用这种方法,因为它非常简单。
希望它可以解决你的问题:)