Hibernate:单表中的父/子关系

时间:2010-03-11 05:56:00

标签: java hibernate orm single-table-inheritance

我几乎看不到任何与Hibernate相关的问题的指针。这适用于使用具有父子的单个数据库表实现继承 与自身的关系。例如:

CREATE TABLE Employee (
  empId BIGINT NOT NULL AUTO_INCREMENT,
  empName VARCHAR(100) NOT NULL,
  managerId BIGINT,
  CONSTRAINT pk_employee PRIMARY KEY (empId)
)

此处, managerId 列可能为null,或者可能指向 Employee 表的另一行。业务规则要求员工了解他的所有报告人,并让他了解他/她的经理。业务规则还允许行具有空 managerId (组织的CEO没有经理)。

我们如何在Hibernate中映射这种关系,标准的多对一关系在这里不起作用?特别是,如果我想实现我的实体不仅作为相应的“Employee”实体类,而且实现多个类,如“Manager”,“Assistant Manager”,“Engineer”等,每个类都继承自“Employee”超级实体类,某些实体具有实际上并不适用于所有的属性,例如“Manager”获取Perks,其他实体不获取(当然,相应的表列将接受null)。

示例代码将不胜感激(我打算使用Hibernate 3注释)。

3 个答案:

答案 0 :(得分:9)

您在此处表达了两个概念:

  1. 继承,您希望将继承层次结构映射到单个表中。
  2. 父母/子女关系。
  3. 要实现1.,您需要使用Hibernate的single table per class hierarchy策略:

    @Entity
    @Inheritance(strategy=InheritanceType.SINGLE_TABLE)
    @DiscriminatorColumn(
        name="emptype",
        discriminatorType=DiscriminatorType.STRING
    )
    public abstract class Employee { ... }
    
    @Entity
    @DiscriminatorValue("MGR")
    public class Manager extends Employee { ... }
    

    要实现2.,您需要在Employee上添加两个自引用关联:

    • 许多员工有零个或一个经理(也是Employee
    • 一名员工有零个或多个报告者

    生成的Employee可能如下所示:

    @Entity
    @Inheritance(strategy=InheritanceType.SINGLE_TABLE)
    @DiscriminatorColumn(
        name="emptype",
        discriminatorType=DiscriminatorType.STRING
    )
    public abstract class Employee { 
    
        ... 
    
        private Employee manager;
        private Set<Employee> reportees = new HashSet<Employee>();
    
        @ManyToOne(optional = true)
        public Employee getManager() {
            return manager;
        }
    
        @OneToMany
        public Set<Employee> getReportees() {
            return reportees;
        }
    
        ...
    }
    

    这将产生以下表格:

    CREATE TABLE EMPLOYEE_EMPLOYEE (
            EMPLOYEE_ID BIGINT NOT NULL,
            REPORTEES_ID BIGINT NOT NULL
        );
    
    CREATE TABLE EMPLOYEE (
            EMPTYPE VARCHAR(31) NOT NULL,
            ID BIGINT NOT NULL,
            NAME VARCHAR(255),
            MANAGER_ID BIGINT
        );
    
    ALTER TABLE EMPLOYEE ADD CONSTRAINT SQL100311183749050 PRIMARY KEY (ID);
    
    ALTER TABLE EMPLOYEE_EMPLOYEE ADD CONSTRAINT SQL100311183356150 PRIMARY KEY (EMPLOYEE_ID, REPORTEES_ID);
    
    ALTER TABLE EMPLOYEE ADD CONSTRAINT FK4AFD4ACE7887BF92 FOREIGN KEY (MANAGER_ID)
        REFERENCES EMPLOYEE (ID);
    
    ALTER TABLE EMPLOYEE_EMPLOYEE ADD CONSTRAINT FKDFD1791F25AA2BE0 FOREIGN KEY (REPORTEES_ID)
        REFERENCES EMPLOYEE (ID);
    
    ALTER TABLE EMPLOYEE_EMPLOYEE ADD CONSTRAINT FKDFD1791F1A4AFCF1 FOREIGN KEY (EMPLOYEE_ID)
        REFERENCES EMPLOYEE (ID);
    

答案 1 :(得分:3)

非常感谢。我创建了我的Employee Entity,如下所示:

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(
    name="EMPLOYEE_TYPE", 
    discriminatorType = DiscriminatorType.STRING
)
@DiscriminatorValue("Employee")
public abstract class Employee {

    @Id 
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name="EMPLOYEE_ID") 
    private Integer empId = null;

    @Column(name="EMPLOYEE_NAME") 
    private String empName = null;

    @Column(name="EMPLOYEE_SECRETARY")
    private String secretary;

    @Column(name="EMPLOYEE_PERKS")
    private int perks;       

    @ManyToOne(targetEntity = Employee.class, optional=true)
@JoinColumn(name="MANAGER_ID", nullable=true)
private Employee manager = null;

    @OneToMany  
    private Set<Employee> reportees = new HashSet<Employee>();

    ...        

    public Set<Employee> getReportees() {
        return reportees;
    } 
}

然后我添加了其他没有正文的实体类,只添加了Discriminator列值,例如Manager,CEO和AsstManager。我选择让Hibernate为我创建表。以下是主要计划:

SessionFactory sessionFactory = HibernateUtil.sessionFactory;
Session session = sessionFactory.openSession();
Transaction newTrans = session.beginTransaction();

CEO empCeo = new CEO();
empCeo.setEmpName("Mr CEO");
empCeo.setSecretary("Ms Lily");

Manager empMgr = new Manager();
empMgr.setEmpName("Mr Manager1");
empMgr.setPerks(1000);
empMgr.setManager(empCeo);

Manager empMgr1 = new Manager();
empMgr1.setEmpName("Mr Manager2");
empMgr1.setPerks(2000);
empMgr1.setManager(empCeo);

AsstManager asstMgr = new AsstManager();
asstMgr.setEmpName("Mr Asst Manager");
asstMgr.setManager(empMgr);

session.save(empCeo);
session.save(empMgr);
session.save(empMgr1);
session.save(asstMgr);
newTrans.commit();

System.out.println("Mr Manager1's manager is : "
        + empMgr.getManager().getEmpName());
System.out.println("CEO's manager is : " + empCeo.getManager());
System.out.println("Asst Manager's manager is : " + asstMgr.getManager());
System.out.println("Persons Reporting to CEO: " + empCeo.getReportees());

session.clear();
session.close();

代码运行正常,Hibernate自己创建了一个“MANAGER_EMPLOYEE_ID”列,用于存储FK。我指定了JoinColumn名称使其成为“MANAGER_ID”。 Hibernate还创建了一个表EMPLOYEE_EMPLOYED,但数据没有保留在那里。

方法getReportees()方法返回null,而getManager()正常工作,正如预期的那样。

答案 2 :(得分:1)

我不确定你真的想要,但我认为你想要一个每个班级的表格

在这种情况下,每个实体按DISCRIMINATOR_COLUMN 排序如下

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(
    name="EMPLOYEE_TYPE", 
    discriminatorType = DiscriminatorType.STRING
)
@DiscriminatorValue("EMPLOYEE")
public class Employee {

    @Id @GeneratedValue
    @Column(name="EMPLOYEE_ID") 
    private Integer id = null;

}

其子女按照

绘制
@Entity
@DiscriminatorValue("MANAGER")
public class Manager extends Employee {

    // Manager properties goes here        
     ...
}

为了测试,让我们执行以下操作

SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
Session session = sessionFactory.openSession();

/*
insert 
into
    Employee
    (EMPLOYEE_TYPE) 
values
    ('EMPLOYEE')
*/
session.save(new Employee());

/*
insert 
into
    Employee
    (EMPLOYEE_TYPE) 
values
    ('MANAGER')
*/
session.save(new Manager());

session.clear();
session.close();

但是,使用InheritanceType.SINGLE_TABLE策略,而不是继承(由于多个实体共享同一个表,你可以看到很多NULL列),你的模型会更好,如下所示

@Entity
public class Employee { 

    private Employee manager;
    private List<Employee> reporteeList = new ArrayList<Employee>();

    /**
    * optional=true
    * because of an Employee could not have a Manager
    * CEO, for instance, do not have a Manager
    */  
    @ManyToOne(optional=true)
    public Employee getManager() {
        return manager;
    }

    @OneToMany
    public List<Employee> getReporteeList() {
        return reporteeList;
    }

}

随意选择满足您需求的最佳方法。

的问候,