Hibernate: отношения между родителями и детьми в одной таблице - PullRequest
8 голосов
/ 11 марта 2010

Я почти не вижу указателей на следующую проблему, связанную с Hibernate. Это относится к реализации наследования с использованием одной таблицы базы данных с parent-child отношение к себе. Например:

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

Здесь столбец managerId может быть пустым или указывать на другую строку таблицы Employee . Бизнес-правило требует, чтобы Сотрудник знал обо всех своих репортажах и чтобы он знал о своем менеджере. Бизнес-правила также допускают, чтобы строки имели нулевой managerId (у руководителя организации нет менеджера).

Как мы отображаем эти отношения в Hibernate, стандартные отношения многие-к-одному здесь не работают? Особенно, если я хочу реализовать свои сущности не только как соответствующий класс сущностей «Сотрудник», но скорее как несколько классов, таких как «Менеджер», «Помощник руководителя», «Инженер» и т. Д., Каждый из которых наследуется от класса супер сущностей «Сотрудник» некоторые объекты, имеющие атрибуты, которые на самом деле не применяются ко всем, например, «Менеджер», получают привилегии, другие - нет (соответствующий столбец таблицы, конечно, принимает ноль).

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

Ответы [ 3 ]

9 голосов
/ 11 марта 2010

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

  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)
  • один сотрудник имеет 0 или более репортеров (ов)

Получившийся 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);
3 голосов
/ 12 марта 2010

Спасибо огромное, ребята. Я создал свою сущность сотрудника следующим образом:

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

Затем я добавил другие классы сущностей без тела, а только значения столбцов Discriminator, например 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 () возвращает ноль, а getManager () работает нормально, как и ожидалось.

1 голос
/ 11 марта 2010

Я не уверен, что вы действительно хотите, но я думаю, что вы хотите Таблица для иерархии классов

В этом случае каждая сущность сортируется по 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;
    }

}

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

С уважением,

...