2010-03-11 2 views
8

Non vedo quasi alcun puntatore sul seguente problema relativo a Hibernate. Questo riguarda l'implementazione dell'ereditarietà utilizzando una singola tabella di database con una relazione genitore-figlio con se stessa. Ad esempio:Hibernate: relazione padre/figlio in una tabella singola

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

Qui, il ManagerID colonna può essere nullo, o può indicare un'altra riga della Dipendente tavolo. La regola aziendale richiede che il Dipendente sia a conoscenza di tutte le sue segnalazioni e che sappia del proprio manager. Le regole aziendali consentono inoltre alle righe di avere gestore n. (l'amministratore delegato dell'organizzazione non ha un manager).

Come mappare questa relazione in Hibernate, la relazione standard molti-a-uno non funziona qui? In particolare, se voglio implementare le mie Entità non solo come classe Entity "Employee" corrispondente, ma piuttosto classi multiple, come "Manager", "Assistant Manager", "Engineer" ecc., Ognuna delle quali eredita dalla classe super entity "Dipendente" , alcune entità che hanno attributi che non si applicano effettivamente a tutti, ad esempio "Manager" ottiene Perks, altri no (la colonna della tabella corrispondente accetta null ovviamente).

Il codice di esempio sarebbe apprezzato (ho intenzione di utilizzare le annotazioni di Hibernate 3).

+0

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

+0

@hobodave: Mi piacerebbe avere un esempio di codice che utilizza Annotazioni (su Hibernate 3), questa è l'intenzione. Sei d'accordo? – dchucks

risposta

9

si sta esprimendo due concetti qui:

  1. eredità e si desidera mappare la gerarchia eredità in una singola tabella.
  2. una relazione genitore/figlio.

Per implementare 1., è necessario utilizzare la strategia di 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 { ... } 

Per implementare 2., È necessario aggiungere due associazioni autoreferenziali on Employee:

  • molti dipendente avere zero o un manager (che è anche un Employee)
  • un impiegato ha zero o molti reportee (s)

La risultante Employee maggio si presenta così:

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

    ... 
} 

E questo si tradurrebbe in tabelle seguenti:

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

Sono confuso Pascal, se la sua ereditarietà a tabella singola ho bisogno di un join Table Employee_Employee? – dchucks

+0

Inoltre, come si mappa la colonna MANAGER_ID? – dchucks

+0

@Dee Hmm ... Cosa? :) Non capisco la prima domanda. Employee_Employee è per l'associazione OneToMany autoreferenziale. È ciò che intendi? –

1

io non sono sicuro di che si vuole veramente, ma penso che si desidera un tabella per gerarchia di classi

In tal caso, ogni entità viene ordinato da un DISCRIMINATOR_COLUMN come segue

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

} 

E i suoi bambini sono mappati secondo

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

    // Manager properties goes here   
    ... 
} 

In ordine t o di prova, facciamo il seguente

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

Ma, invece di eredità (che potete vedere un sacco di colonna NULL a causa di più di una società condividono lo stesso tavolo - quando si usa la strategia InheritanceType.SINGLE_TABLE), il tuo modello sarebbe meglio come segue

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

} 

Sentitevi liberi di scegliere l'approccio migliore che soddisfano le vostre esigenze.

saluti,

3

Grazie mille ragazzi. Ho creato il mio Entity Impiegato come segue:

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

Ho poi aggiunto altre classi di entità senza corpo, ma solo discriminatori valori colonne, come Manager, CEO e AsstManager. Ho scelto di lasciare che Hibernate creasse il tavolo per me. Di seguito è riportato il programma principale:

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

Il codice viene eseguito bene, Hibernate stava creando una colonna "MANAGER_EMPLOYEE_ID" sul proprio dove memorizza l'FK. Ho specificato il nome JoinColumn per renderlo "MANAGER_ID". Hibernate crea anche una tabella EMPLOYEE_EMPLOYED, tuttavia i dati non vengono mantenuti lì.

Il metodo getReportees() restituisce un valore null, mentre getManager() funziona correttamente, come previsto.