编程语言
首页 > 编程语言> > java – 每个实体一个DAO – 如何处理引用?

java – 每个实体一个DAO – 如何处理引用?

作者:互联网

我正在编写一个具有典型两个实体的应用程序:User和UserGroup.后者可能包含前者的一个或多个实例.我有以下(更多/更少)映射:

用户:

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);

不幸的是我得到以下异常:

object references an unsaved transient instance – save the transient
instance before flushing: x.y.z.model.User.group ->
x.y.z.model.UserGroup

我调查了它,我认为这是因为每个DAO实例都分配了不同的entityManager(我检查过 – 每个DAO中的引用与实体管理器不同),而对于用户,entityManager不管理传递的UserGroup实例.

我试图将分配给用户的用户组合并到UserDAO的实体管理器中.有两个问题:

>它仍然不起作用 – 实体管理器想要覆盖现有的UserGroup并且它会异常(显然)
>即使它工作,我最终会为每个相关实体编写合并代码

当使用相同的实体管理器进行查找和持久时,所描述的情况有效.这指出了一个问题:

>我的设计坏了吗?我认为它与this answer中推荐的非常相似.是否应该为所有DAO单独使用EntityManager(否则网络声称)?
>或者是否应该在DAO内完成小组分配?在这种情况下,我最终会在DAO中编写大量代码
>我应该摆脱DAO吗?如果是,如何很好地处理数据访问?
>任何其他解决方案?

我使用Spring作为容器,Hibernate作为JPA实现.

解决方法:

在Spring中,EntityManager的不同实例是正常的.它创建代理,动态使用当前处于事务中的实体管理器(如果存在).否则,将创建一个新的.

问题是你的交易太短了.检索用户组在事务中执行(因为findById方法隐式为@Transactional).但随后事务提交并且组已分离.保存新用户时,它将创建一个失败的新事务,因为用户引用了一个分离的实体.

解决这个问题的方法(以及通常做的事情)是创建一个在单个事务中执行整个操作的方法.只需在服务类中创建该方法(任何Spring管理的组件都可以工作),并使用@Transactional对其进行注释.

标签:java,spring,jpa,entitymanager,spring-orm
来源: https://codeday.me/bug/20190620/1247768.html