代码之家  ›  专栏  ›  技术社区  ›  wallenborn

一元关系中的阻抗失配问题

  •  4
  • wallenborn  · 技术社区  · 15 年前

    我有一个关于JPA-2.0(提供者是Hibernate)关系及其在Java中的相应管理的问题。假设我有一个部门和一个员工实体:

    @Entity
    public class Department {
      ...
      @OneToMany(mappedBy = "department")
      private Set<Employee> employees = new HashSet<Employee>();
      ...
    }
    
    @Entity
    public class Employee {
      ...
      @ManyToOne(targetEntity = Department.class)
      @JoinColumn
      private Department department;
      ...
    }
    

    现在我知道我必须自己管理Java关系,如下面的单元测试:

    @Transactional
    @Test
    public void testBoth() {
      Department d = new Department();
      Employee e = new Employee();
      e.setDepartment(d);
      d.getEmployees().add(e);
      em.persist(d);
      em.persist(e);
      assertNotNull(em.find(Employee.class, e.getId()).getDepartment());
      assertNotNull(em.find(Department.class, d.getId()).getEmployees());
    }
    

    如果我漏掉任何一个 e.setDepartment(d) d.getEmployees().add(e)

    @Test
    public void testBoth() {
      EntityManager em = emf.createEntityManager();
      em.getTransaction().begin();
      Department d = new Department();
      Employee e = new Employee();
      e.setDepartment(d);
      d.getEmployees().add(e);
      em.persist(d);
      em.persist(e);
      em.getTransaction().commit();
      em.close();
      em = emf.createEntityManager();
      em.getTransaction().begin();
      assertNotNull(em.find(Employee.class, e.getId()).getDepartment());
      assertNotNull(em.find(Department.class, d.getId()).getEmployees());
      em.getTransaction().commit();
      em.close();
    }
    

    我还需要管理关系的双方吗?不,事实证明,我没必要。有了这个修改

    e.setDepartment(d);
    //d.getEmployees().add(e);
    

    这些断言仍然是成功的。但是,如果我只设置另一边:

    //e.setDepartment(d);
    d.getEmployees().add(e);
    

    3 回复  |  直到 15 年前
        1
  •  5
  •   axtavt    15 年前

    JPA中的实体关系有拥有和反向两个方面。数据库更新由拥有方的状态决定。对你来说 Employee mappedBy 属性。

    JPA 2.0 specification :

    关系可以是双向的,也可以是双向的 这种关系有两个拥有者 和一个相反的(非拥有的)边。A 单向关系只有 拥有的一方。公司的所有者 数据库中的关系,如 3.2.4.

    以下规则适用于双向关系:

    • 双向电路的反面 并排使用的mappedBy元素 注释。mappedBy元素 指定中的属性或字段 实体的所有者 关系。
    • 一对多/多对一 双向关系必须是 无法在上指定元素 许多注释。
    • 为了 一对一双向 对应于包含 对应的外键。
    • 为了 多对多双向 任何一方都可能是 拥有方。
        2
  •  7
  •   Pascal Thivent    15 年前

    我不知道你的测试想证明什么,但事实是你 处理双向关联时处理关联的两侧。不这样做是不正确的。句号。

    更新: 虽然axtavt提到的spec参考当然是准确的,但我坚持认为,您肯定必须设置双向关联的两边。不这样做是不正确的,并且第一个持久性上下文中实体之间的关联是错误的 . 这个 JPA wiki book

    object corruption . 从技术上讲,如果您只在关系的拥有端添加/删除数据库,那么数据库将正确更新,但是您的对象模型将不同步,这可能会导致问题。

    换句话说,唯一 安全的

    @Entity
    public class Department {
        ...
        @OneToMany(mappedBy = "department")
        private Set<Employee> employees = new HashSet<Employee>();
        ...
    
        public void addToEmployees(Employee employee) {
            this.employees.add(employee);
            employee.setDepartment(this);
        }
    }
    

    我再说一遍,不这样做是不正确的。您的测试之所以有效,是因为您在一个新的持久性上下文(即非常特殊的情况,而不是一般的情况)中访问数据库,但是代码在许多其他情况下会中断。

        3
  •  2
  •   romanb    15 年前