【发布时间】:2012-09-16 06:38:17
【问题描述】:
我正在编写一个具有典型两个实体的应用程序:用户和用户组。后者可能包含前者的一个或多个实例。我有以下(更多/更少)映射:
用户:
public class User {
@Id
@GeneratedValue
private long id;
@ManyToOne(cascade = {CascadeType.MERGE})
@JoinColumn(name="GROUP_ID")
private UserGroup group;
public UserGroup getGroup() {
return group;
}
public void setGroup(UserGroup group) {
this.group = group;
}
}
用户组:
public class UserGroup {
@Id
@GeneratedValue
private long id;
@OneToMany(mappedBy="group", cascade = {CascadeType.REMOVE}, targetEntity = User.class)
private Set<User> users;
public void setUsers(Set<User> users) {
this.users = users;
}
}
现在我为这些实体(UserDao 和 UserGroupDao)中的每一个都有一个单独的 DAO 类。我所有的 DAO 都使用 @PersistenceContext 注释注入了 EntityManager,如下所示:
@Transactional
public class SomeDao<T> {
private Class<T> persistentClass;
@PersistenceContext
private EntityManager em;
public T findById(long id) {
return em.find(persistentClass, id);
}
public void save(T entity) {
em.persist(entity);
}
}
使用此布局,我想创建一个新用户并将其分配给现有用户组。我是这样做的:
UserGroup ug = userGroupDao.findById(1);
User u = new User();
u.setName("john");
u.setGroup(ug);
userDao.save(u);
不幸的是,我得到了以下异常:
对象引用了一个未保存的瞬态实例 - 保存瞬态 刷新前的实例:x.y.z.model.User.group -> x.y.z.model.UserGroup
我对其进行了调查,我认为这是因为每个 DAO 实例都有分配了不同的 entityManager(我检查过 - 每个 DAO 中对实体管理器的引用是不同的)并且对于用户 entityManager 不管理通过 UserGroup 实例。
我尝试将分配给用户的用户组合并到 UserDAO 的实体管理器中。这样做有两个问题:
- 它仍然不起作用 - 实体管理器想要覆盖现有的 UserGroup 并且它得到异常(显然)
- 即使它有效,我最终也会为每个相关实体编写合并代码
当 find 和 persist 都是使用同一个实体管理器进行时,所描述的情况有效。这指向一个问题:
- 我的设计有问题吗?我认为它与this answer 中的推荐非常相似。所有 DAO 是否应该有一个 EntityManager(否则网络声称)?
- 还是应该在 DAO 内完成组分配?在这种情况下,我最终会在 DAO 中编写大量代码
- 我应该摆脱 DAO 吗?如果是,如何很好地处理数据访问?
- 还有其他解决方案吗?
我使用 Spring 作为容器,使用 Hibernate 作为 JPA 实现。
【问题讨论】:
-
不管你是否有一个
EntityManager,你当然应该有一个由两个DAO共享的持久性上下文(这自然会涉及一个EntityManager,但它可以用不同的方式实现每个注射部位的实例)。你是如何注入你的EntityManager的?这是什么容器? -
另外,你是如何进行事务划分的?你能显示
UserDao的代码吗? -
我添加了一些关于容器和交易的更多细节。
-
对于 userDao 它只是扩展了 SomeDAO
类 -
我不知道 Spring,但如果
@Transactional意味着对类方法的调用包含在事务中,那么这可能是您的问题。您使用一个事务来查找UserGroup,然后使用另一个事务来保存User。我不知道这会导致您观察到的问题,但这绝对是错误的做法 - 事务应该包含整个工作单元。我强烈建议在同一个事务中进行查找和保存(即使它不能解决问题!)。
标签: java spring jpa entitymanager spring-orm