Java Hibernate:单表中的父/子关系
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2422878/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Hibernate: Parent/Child relationship in a single-table
提问by Dchucks
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:
我几乎看不到有关以下与 Hibernate 相关的问题的任何指示。这与使用与自身具有父子关系的单个数据库表来实现继承有关。例如:
CREATE TABLE Employee (
empId BIGINT NOT NULL AUTO_INCREMENT,
empName VARCHAR(100) NOT NULL,
managerId BIGINT,
CONSTRAINT pk_employee PRIMARY KEY (empId)
)
Here, the managerIdcolumn may be null, or may point to another row of the Employeetable. 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).
这里,managerId列可能为空,也可能指向Employee表的另一行。业务规则要求员工了解他的所有报告人,并让他了解他/她的经理。业务规则还允许行的managerId为空(组织的 CEO 没有经理)。
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).
我们如何在 Hibernate 中映射这种关系,标准的多对一关系在这里不起作用?特别是,如果我不仅要实现我的实体作为相应的“员工”实体类,而且要实现多个类,例如“经理”、“助理经理”、“工程师”等,每个类都继承自“员工”超级实体类,某些实体的属性实际上并不适用于所有实体,例如“经理”获得 Perks,其他实体则没有(相应的表列当然会接受 null)。
Example code would be appreciated (I intend to use Hibernate 3 annotations).
示例代码将不胜感激(我打算使用 Hibernate 3 注释)。
采纳答案by Pascal Thivent
You are expressing two concepts here:
你在这里表达了两个概念:
- inheritance and you want to map your inheritance hierarchy in a single table.
- a parent/child relationship.
- 继承并且您希望在单个表中映射您的继承层次结构。
- 父子关系。
To implement 1., you'll need to use Hibernate's single table per class hierarchystrategy:
要实现 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 { ... }
To implement 2., you'll need to add two self-referencing associations on Employee
:
要实现 2.,您需要在 上添加两个自引用关联Employee
:
- many employee have zero or one manager (which is also an
Employee
) - one employee has zero or many reportee(s)
- 许多员工有零个或一个经理(这也是一个
Employee
) - 一名员工有零个或多个报告人
The resulting Employee
may looks like this:
结果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;
}
...
}
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);
回答by Arthur Ronald
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_COLUMNas follows
在这种情况下,每个实体都按 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;
}
And its Children is mapped according to
并且它的 Children 是根据
@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
但是,而不是继承(由于多个实体共享同一个表,您可以看到很多 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;
}
}
Feel free to choice the best approach that fulfill your needs.
随意选择满足您需求的最佳方法。
regards,
问候,
回答by Dchucks
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:
然后我添加了其他没有主体但只有鉴别器列值的实体类,例如 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();
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.
代码运行良好,Hibernate 自己创建了一个列“MANAGER_EMPLOYEE_ID”,用于存储 FK。我指定了 JoinColumn 名称以使其成为“MANAGER_ID”。Hibernate 还创建了一个表 EMPLOYEE_EMPLOYED,但是数据没有保存在那里。
Method getReportees() method returns a null, while getManager() works fine, as expected.
方法 getReportees() 方法返回空值,而 getManager() 可以正常工作,如预期的那样。