Впадать в спящий режим:Связь родитель/потомок в одной таблице
-
19-09-2019 - |
Вопрос
Я почти не вижу никаких указаний на следующую проблему, связанную с гибернацией.Это относится к реализации наследования с использованием одной таблицы базы данных с родительско-дочерним отношением к самой себе.Например:
CREATE TABLE Employee (
empId BIGINT NOT NULL AUTO_INCREMENT,
empName VARCHAR(100) NOT NULL,
managerId BIGINT,
CONSTRAINT pk_employee PRIMARY KEY (empId)
)
Здесь, в Идентификатор менеджера столбец может быть нулевым или может указывать на другую строку Сотрудник таблица.Бизнес-правило требует, чтобы Сотрудник знал обо всех своих подчиненных, а также чтобы он знал о своем руководителе.Бизнес - правила также позволяют строкам иметь значение null Идентификатор менеджера (у генерального директора организации нет менеджера).
Как мы сопоставляем это отношение в режиме гибернации, стандартное отношение "многие к одному" здесь не работает?Особенно, если я хочу реализовать свои сущности не только как соответствующий класс сущностей "Employee", но и как несколько классов, таких как "Manager", "Assistant Manager", "Engineer" и т.д., Каждый из которых наследуется от класса супер-сущностей "Employee", причем некоторые сущности имеют атрибуты, которые на самом деле применимы не ко всем, например, "Manager" получает льготы, другие - нет (соответствующий столбец таблицы, конечно, примет значение null).
Буду признателен за пример кода (я намерен использовать аннотации Hibernate 3).
Решение
Вы выражаете здесь две концепции:
- наследование и вы хотите отобразить свою иерархию наследования в одной таблице.
- отношения родитель/потомок.
Чтобы реализовать 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);
Другие советы
Огромное спасибо, ребята.Я создал свою сущность Employee следующим образом:
@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".В режиме гибернации также создается таблица EMPLOYEE_EMPLOYED, однако данные в ней не сохраняются.
Метод getReportees() возвращает значение null, в то время как getManager() работает нормально, как и ожидалось.
Я не уверен в том, чего вы действительно хотите, но я думаю, что вы хотите Таблица для каждой иерархии классов
В таком случае, каждый объект сортируется по столбцу 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) ваша модель была бы лучше следующей
@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;
}
}
Не стесняйтесь выбирать наилучший подход, который удовлетворит ваши потребности.
с уважением,