Hibernate: отношение родитель / ребенок в одной таблице

я почти не вижу указателя на следующую проблему, связанную с 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, или может указывать на другую строку сотрудник таблица. Бизнес-правило требует работник, чтобы знать о всех его reportees и для него, чтобы знать о его/ее руководителем. Бизнес-правила также разрешить строкам иметь null значению managerid (у генерального директора организации нет менеджера).

Как мы сопоставляем эти отношения в спящем режиме, стандартные отношения "многие к одному" здесь не работают? Особенно, если я хочу реализовать свои сущности не только как соответствующий класс сущности "сотрудник", но и несколько классов, таких как "менеджер", "помощник менеджера"," инженер "и т. д., Каждый из которых наследуется от класса супер сущности "сотрудник", некоторые сущности имеют атрибуты, которые не на самом деле применяются ко всем, например, "менеджер" получает льготы, другие нет (соответствующий столбец таблицы будет принимать null, конечно).

пример кода будет оценен (я намерен использовать аннотации Hibernate 3).

3 ответов


вы выражаете здесь два понятия:

  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)
  • один сотрудник имеет ноль или много reportee(s)

в результате 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". Hibernate также создает таблицу 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 strategy), ваша модель будет лучше следующим образом

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

}

не стесняйтесь выбирать лучший подход, который удовлетворит ваши потребности.

С уважением,