【问题标题】:How to do a many-to-many relationship in spring Roo, with attributes within de relationship?Spring Roo 中如何做多对多关系,属性在 de 关系中?
【发布时间】:2011-09-02 16:58:53
【问题描述】:

我一直在研究这个话题,还没有找到任何答案。我正在使用 spring roo,我想知道是否有一种方法可以与该关系中的属性建立多对多关系。例如我有两张表 Employee 和 MedicalEquipment,Employees 可以保留许多设备,设备可以被许多员工保留,但是我想存储这个保留发生的日期。

如果有人可以提供帮助,我将不胜感激。提前致谢!

【问题讨论】:

    标签: many-to-many relationship spring-roo


    【解决方案1】:
    • 为了实现与属性的多对多关系,您需要一个包含附加列的连接表。也就是说,除了 Employee 和 MedicalEquipment,您还需要第三个 EmployeeMedicalEquipment。

    • 关于 JPA,您有两种选择:

      1. 将连接表映射到中间实体
      2. 将连接表映射到组件集合

    前者更复杂,但它允许您进行双向导航(因为它是一个实体,因此可以共享引用),后者在构建和使用它时都更简单,但你不能使用它在实体之间导航(但是,您可以编写查询来检索您需要的对象)

    • Roo 是一个代码生成器,但它缺少 JPA 提供的所有选项,因此您必须编辑 Java 类和测试类。视图的构建也有一些限制,因此您也需要对其进行编辑。

    在我的例子中,我需要创建一个中间实体,因为该表属于一个已经存在的遗留数据库。

    我做了这样的事情:

    entity --class ~.domain.Employee --table T_Employee [etc...]
    field [etc...]
    
    entity --class ~.domain.MedicalEquipment --table T_MedicalEquipment [etc...]
    field [etc...]
    
    entity --class ~.domain.EmployeeMedicalEquipment --table T_Employee_MedicalEquipment --identifierType ~.domain.EmployeeMedicalEquipmentId
    //to store the date this reserve took place.
    field date --fieldName reserveDate --column C_reserveDate [etc...]
    
    //Bidirectional: you´ll need @JoinColumn insertable = false and updatable = false
    field reference --fieldName employee --type ~.domain.Employee --cardinality MANY_TO_ONE 
    
    //Bidirectional: you'll need @JoinColumn insertable = false and updatable = false
    field reference --fieldName medicalEquipment --type ~.MedicalEquipment --cardinality MANY_TO_ONE 
    
    //Join table's composite primary key
    field string --fieldName employeeId --column employee_ID --class ~.domain.EmployeeMedicalEquipmentId [etc...]
    field string --fieldName medicalEquipmentId --column medicalEquipment_ID  --class ~.domain.EmployeeMedicalEquipmentId [etc...]
    
    //Now, it's time to complete the relationship:
    focus --class ~.domain.Employee
    field set --type ~.domain.EmployeeMedicalEquipment --fieldName medicalEquipments --cardinality ONE_TO_MANY --mappedBy employee
    focus --class ~.domain.MedicalEquipment
    field set --type ~.domain.EmployeeMedicalEquipment --fieldName employees --cardinality ONE_TO_MANY --mappedBy medicalEquipment
    

    此外,您需要通过使用构造函数管理关联两侧的集合来保证引用完整性。所以你需要以这种方式编辑类:

    @RooEntity(...
    public class EmployeeMedicalEquipment {
    
    @ManyToOne
    @JoinColumn(name = "employeeId", referencedColumnName = "employeeId", insertable = false, updatable = false)
    private Employee employee;
    
    @ManyToOne
    @JoinColumn(name="medicalEquipmentId", referencedColumnName="medicalEquipmentId", insertable=false, updatable=false)
    private MedicalEquipment medicalEquipment;
    
    /**
     * No-arg constructor for JavaBean tools
     */
    public EmployeeMedicalEquipment() {
    }
    
    
    /**
     * Full constructor, the Employee and MedicalEquipment instances have to have an identifier value, they have to be in detached or persistent state.
     * This constructor takes care of the bidirectional relationship by adding the new instance to the collections on either side of the
     * many-to-many association (added to the collections)
     */
    public EmployeeMedicalEquipment(Employee employee, MedicalEquipment medicalEquipment, Date reserveDate) {
        this.setReserveDate (reserveDate);
    
        this.employee = employee;
        this.medicalEquipment = medicalEquipment;
    
        this.setId(new EmployeeMedicalEquipmentId(employee.getId(),  medicalEquipment.getId());
    
        // If Employee or MedicalEquipment  Guarantee referential integrity
        employee.getMedicalEquipments().add(this);
        medicalEquipment.getEmployees().add(this);
    }
    ...
    
    }
    

    我试图给你一个 Roo 配置的例子。

    您可以在 Manning “Java Persistence with Hibernate”一书中的第 7.2.3 章中找到对 JPA 内容的更好解释。

    注意:如果你使用roo 1.2.1,count查询会生成带有“count(id1, id2)”的SQL,并不是所有的数据库都支持,包括HSQLDB。您可以像这样自定义它:

    ...

    -@RooJpaActiveRecord(identifierType = EmployeeMedicalEquipmentId.class, table = "T_Employee_MedicalEquipment")
    +@RooJpaActiveRecord(identifierType = EmployeeMedicalEquipmentId.class, table = "T_Employee_MedicalEquipment", countMethod="")
    ...
    public class EmployeeMedicalEquipment {
    ...
        // count method initially copied from ActiveRecord aspect
        public static long countEmployeeMedicalEquipments() {
    -       return entityManager().createQuery("SELECT COUNT(o) FROM EmployeeMedicalEquipment o", Long.class).getSingleResult();
    +       return entityManager().createQuery("SELECT COUNT(o.employee) FROM EmployeeMedicalEquipment o", Long.class).getSingleResult();
            }
        }
    

    【讨论】:

    • 非常感谢,这很有帮助。我去看看那本书。我还有一个问题,很抱歉打扰您,我运行了安全设置命令并对其进行了修改,因此存储在数据库中的员工是唯一可以访问该应用程序的人。现在,任何登录的员工都可以选择与储备关联的员工,但是储备应该由当时登录的员工创建,而不是由任何员工创建。你知道如何做到这一点吗?
    【解决方案2】:

    为什么不能有一个单独的实体来表示你们的关系?

    只需引入一个名为 MedicalEquipmentReservation 的新实体,它将包含预订的所有属性以及 Employee 和 Medical Equipment 实体之间的关系。

    请看下面的例子。

    class MedicalEquipmentReservation{  
        //attributes  
        Date reservationStartDate;    
        Date reservationEndDate; 
    
        //relationships
        Employee employee; 
        Set<Equipment> equipments; //assuming more than one equipment can be borrowed at one reservation
    }
    

    Spring Roo 干杯,万事如意!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-08-16
      • 1970-01-01
      • 2011-01-16
      • 1970-01-01
      • 2013-03-05
      • 2011-07-07
      • 1970-01-01
      • 2018-03-17
      相关资源
      最近更新 更多