【问题标题】:Hibernate: Parent/Child relationship in a single-tableHibernate:单表中的父/子关系
【发布时间】:2010-03-11 05:55:59
【问题描述】:

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

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

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

我们如何在 Hibernate 中映射这种关系,标准的多对一关系在这里不起作用?特别是,如果我不仅想将我的实体实现为相应的“员工”实体类,还想实现多个类,例如“经理”、“助理经理”、“工程师”等,每个类都继承自“员工”超级实体类, 某些实体的属性实际上并不适用于所有人,例如“经理”获得 Perks,而其他实体则没有(相应的表格列当然会接受 null)。

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

【问题讨论】:

标签: java hibernate orm single-table-inheritance


【解决方案1】:

你在这里表达了两个概念:

  1. 继承,并且您希望将继承层次结构映射到单个表中。
  2. 父/子关系。

要实现 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);

【讨论】:

  • 我很困惑帕斯卡,如果它的单表继承我需要一个联接表Employee_Employee吗?
  • 另外我如何映射 MANAGER_ID 列?
  • @Dee 嗯...什么? :) 我没有得到第一个问题。 Employee_Employee 用于 OneToMany 自引用关联。这是你的意思吗?
  • @Dee 然后 MANAGER_ID 列是为 @ManyToOne 关联生成的默认列。如果要更改它,请使用 @JoinColumn 注释 (java.sun.com/javaee/6/docs/api/javax/persistence/ManyToOne.html)
  • @pascal 通过在“reportees”变量上提供“mappedBy”注释,我们可以避免需要映射表。该代码现在可以在单个表中正常工作。我现在正在研究如何让 getReportees() 列出报告对象的分层列表(例如:CEO 可能需要向他报告的所有经理和报告经理的助理经理的列表):) 谢谢。跨度>
【解决方案2】:

非常感谢大家。我按如下方式创建了我的员工实体:

@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;
    } 
}

然后我添加了其他没有正文但只有鉴别器列值的实体类,例如 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() 按预期工作正常。

【讨论】:

    【解决方案3】:

    我不确定你是否真的想要,但我认为你想要一个每个类层次结构的表

    在这种情况下,每个实体都按 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();
    

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

    @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;
        }
    
    }
    

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

    问候,

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-05-26
      • 1970-01-01
      • 1970-01-01
      • 2015-01-02
      • 1970-01-01
      • 2010-11-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多