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

标签 java spring jpa entitymanager spring-orm

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

用户:

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 注释注入(inject)了 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 的实体管理器中。这有两个问题:

  • 它仍然不起作用 - 实体管理器想要覆盖现有的用户组并且它得到异常(显然)
  • 即使可行,我最终也会为每个相关实体编写合并代码

当查找和持久化都使用相同的实体管理器时,描述的案例有效。这指向一个问题:

  • 我的设计有问题吗?我认为它与 this answer 中推荐的非常相似.所有 DAO 都应该有一个 EntityManager(网络上另有说法)吗?
  • 还是应该在 DAO 内部完成组分配?在这种情况下,我最终会在 DAO 中编写大量代码
  • 我应该摆脱 DAO 吗?如果是,如何很好地处理数据访问?
  • 还有其他解决方案吗?

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

最佳答案

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

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

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

关于java - 每个实体一个 DAO——如何处理引用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12571194/

相关文章:

java - 如何使用 spring boot 和 rabbitmq 启动 hello world js 应用程序

java - 使用 JPA 从 child 获取 parent

java - "The matching wildcard is strict, but no declaration can be found for element ' http '"错误

java - 在 Tomcat 上部署 Spring Boot 应用程序时如何打开 Angular 网页?

java - Spring Jpa 服务调用与 Jpa 查询

hibernate - 如何配置 Spring 使 JPA(Hibernate)和 JDBC(JdbcTemplate 或 MyBatis)共享同一个事务

java - 如何将字符串形式的图像(从android捕获并使用arraylist传递)转换为blob并将其保存在mysql数据库中

java - 从 ActionPerformed 调用方法并在主线程上运行它

java - 关于 websocket 的说明

java - 尽管套接字从未手动关闭,但由于SocketException而导致DTLS握手失败