2010-03-11 31 views
8

Apenas veo ningún puntero sobre el siguiente problema relacionado con Hibernate. Esto se refiere a la implementación de herencia utilizando una sola tabla de base de datos con una relación padre-hijo consigo misma. Por ejemplo:Hibernar: relación padre/hijo en una sola tabla

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

Aquí, la columna de la ManagerID puede ser nulo o puede apuntar a otra fila de la Empleado tabla. La regla empresarial requiere que el Empleado sepa de todos sus informados y que sepa sobre su gerente. Las reglas comerciales también permiten que las filas tengan nulo managerId (el CEO de la organización no tiene un administrador).

¿Cómo mapeamos esta relación en Hibernate, la relación estándar de muchos a uno no funciona aquí? Especialmente, si quiero implementar mis Entidades no solo como una clase de Entidad de "Empleado" correspondiente, sino varias clases, como "Gerente", "Asistente de Gerente", "Ingeniero", etc., heredando cada una de la clase de super entidad "Empleado" , alguna entidad que tenga atributos que no se apliquen realmente a todos, por ejemplo, "Gerente" obtiene Beneficios, otros no (la columna de la tabla correspondiente aceptaría nulo, por supuesto).

Se apreciará el código de ejemplo (tengo la intención de utilizar las anotaciones de Hibernate 3).

+0

Dupe: http://stackoverflow.com/questions/1862457/hibernate3-self-referencing-objects – hobodave

+0

@hobodave: Agradecería tener un ejemplo de código que utiliza Anotaciones (en Hibernate 3), esa es la intención. ¿Estarías de acuerdo? – dchucks

Respuesta

9

que están expresando dos conceptos aquí:

  1. herencia y que desea asignar la jerarquía de herencia en una sola tabla.
  2. relación entre padres e hijos.

1. Para poner en práctica, tendrá que utilizar la estrategia de Hibernate single table per class hierarchy:

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

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

Implementar 2., Tendrá que añadir dos asociaciones de autorreferencia en Employee:

  • muchos empleados tienen cero o un gerente (que también es un Employee) empleado
  • uno tiene cero o muchos reportee (s)

El Employee mayo resultante se parece a esto:

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

    ... 
} 

Y esto se traduciría en las siguientes tablas:

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); 
+0

Estoy confundido Pascal, si su única tabla de herencia ¿Necesito solo un join Table Employee_Employee? – dchucks

+0

¿También cómo correlao la columna MANAGER_ID? – dchucks

+0

@Dee Hmm ... ¿Qué? :) No entiendo la primera pregunta. Employee_Employee es para la asociación de autorreferencia OneToMany. ¿Es esto lo que quieres decir? –

1

No estoy seguro de que realmente quiere, pero yo creo que usted quiere una tabla por jerarquía de clases

En ese caso, cada Entidad está ordenada por un DISCRIMINATOR_COLUMN de la siguiente manera

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

} 

Y sus hijos están mapeados según

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

    // Manager properties goes here   
    ... 
} 

En orden t o prueba, vamos a hacer lo siguiente

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

Pero, en lugar de la herencia (que se puede ver una gran cantidad de columnas nulas debido a más de una entidad comparten la misma mesa - cuando se utiliza la estrategia InheritanceType.SINGLE_TABLE), su modelo sería mejor como sigue

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

} 

Siéntase libre de elegir el mejor enfoque que cumpla sus necesidades.

cordiales,

3

Gracias a ton guys. He creado mi entidad Employee de la siguiente manera:

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

Luego añade otras clases de entidad, sin cuerpo, pero sólo discriminador valores de las columnas, como el Administrador, CEO y AsstManager. Elegí dejar que Hibernate creara la mesa para mí. Lo que sigue es el programa principal:

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

El código funciona bien, Hibernate estaba creando una columna "MANAGER_EMPLOYEE_ID" por su propia cuenta donde se almacena el FK. Especifiqué el nombre de JoinColumn para convertirlo en "MANAGER_ID". Hibernate también crea una tabla EMPLOYEE_EMPLOYED, sin embargo, los datos no se conservan allí.

Método El método getReportees() devuelve un valor nulo, mientras que getManager() funciona correctamente, como se esperaba.

Cuestiones relacionadas