Hibernate:单表中的父/子关系

发布于 2024-08-25 02:24:24 字数 661 浏览 9 评论 0原文

我几乎没有看到任何关于以下与 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 中映射这种关系,标准的多对一关系在这里不起作用?特别是,如果我不仅想将我的实体实现为相应的“Employee”实体类,而是多个类,例如“Manager”,“Assistant Manager”,“Engineer”等,每个类都继承自“Employee”超级实体类,某些实体的属性实际上并不适用于所有人,例如“经理”获得Perks,其他实体则没有(相应的表列当然会接受null)。

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

I hardly see any pointer on the following problem related to Hibernate. This pertains to implementing inheritance using a single database table with a parent-child
relationship to itself. For example:

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

Here, the managerId column may be null, or may point to another row of the Employee table. Business rule requires the Employee to know about all his reportees and for him to know about his/her manager. The business rules also allow rows to have null managerId (the CEO of the organisation doesn't have a manager).

How do we map this relationship in Hibernate, standard many-to-one relationship doesn't work here? Especially, if I want to implement my Entities not only as a corresponding "Employee" Entity class, but rather multiple classes, such as "Manager", "Assistant Manager", "Engineer" etc, each inheriting from "Employee" super entity class, some entity having attributes that don't actually apply to all, for example "Manager" gets Perks, others don't (the corresponding table column would accept null of course).

Example code would be appreciated (I intend to use Hibernate 3 annotations).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

逆蝶 2024-09-01 02:24:24

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

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

要实现 1.,您需要使用 Hibernate 的 单表每个类层次结构策略:

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

You are expressing two concepts here:

  1. inheritance and you want to map your inheritance hierarchy in a single table.
  2. a parent/child relationship.

To implement 1., you'll need to use Hibernate's single table per class hierarchy strategy:

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

@Entity
@DiscriminatorValue("MGR")
public class Manager extends Employee { ... }

To implement 2., you'll need to add two self-referencing associations on Employee:

  • many employee have zero or one manager (which is also an Employee)
  • one employee has zero or many reportee(s)

The resulting Employee may looks like this:

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

    ...
}

And this would result in the following tables:

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);
说好的呢 2024-09-01 02:24:24

非常感谢你们。我创建了我的员工实体,如下所示:

@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() 工作正常,如预期的那样。

Thanks a ton guys. I created my Employee Entity as follows:

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

I then added other Entity classes with no body but just Discriminator columns values, such as Manager, CEO and AsstManager. I chose to let Hibernate create the table for me. Following is the main program:

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();

The code runs fine, Hibernate was creating a column "MANAGER_EMPLOYEE_ID" on its own where it stores the FK. I specified the JoinColumn name to make it "MANAGER_ID". Hibernate also creates a table EMPLOYEE_EMPLOYED, however the data is not being persisted there.

Method getReportees() method returns a null, while getManager() works fine, as expected.

执手闯天涯 2024-09-01 02:24:24

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

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

}

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

问候,

I am not sure about you really want, but i think you want a Table per class hierarchy

In that case, each Entity is sorted by a DISCRIMINATOR_COLUMN as follows

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

}

And its Children is mapped according to

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

    // Manager properties goes here        
     ...
}

In order to test, let's do the following

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();

But, instead of inheritance (which you can see a lot of NULL column due to more than one Entity share the same table - when using InheritanceType.SINGLE_TABLE strategy), your model would be better as follows

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

}

Feel free to choice the best approach that fulfill your needs.

regards,

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文