2010-07-13 4 views
8

Questa è una domanda di design abbastanza lunga (non eccessivamente complessa) quindi per favore portami con me. Sto cercando di implementare un sistema di gestione personale/ruolo con POJO e JPA. Sono abbastanza nuovo per ORM e questo è principalmente un problema di mappatura.Mapping dell'ereditarietà con JPA/Hibernate

Ho questo funzionamento come POJO e mi trovo a mio agio con l'API a livello di chiamante, ma vorrei ora mapparlo a un database utilizzando JPA o Hibernate in un ambiente Seam.

La mia implementazione si basa sui modelli Decorator (GoF) e Person Role Object (Baumer/Riehle et al). Tutti i ruoli sono codificati in modo rigido e l'aggiunta di nuovi ruoli non è supportata poiché richiederebbe modifiche al codice per estendere il comportamento. Userò i gruppi di utenti per implementare la sicurezza e le autorizzazioni.

C'è un'interfaccia Persona con metodi di gestione dei ruoli come addRole(), removeRole(), hasRole(), getRole(), getRoles(), tra le altre cose. L'implementazione concreta è fornita da una classe PersonImpl.

Esiste un ruolo di classe astratto che implementa anche l'interfaccia Person (per l'equivalenza di sostituzione del decoratore) e una classe RoleImpl che la estende. La classe Role contiene un riferimento a un'istanza di persona, utilizzandola per servire qualsiasi chiamata di metodo/proprietà sull'interfaccia persona, il che significa che tutte le sottoclassi di Role possono gestire l'interfaccia Person. Il costruttore di ruoli prende l'oggetto persona come parametro.

Queste sono le interfacce/classi:

public interface Person { 
    public String getFirstName(); 

    public void setFirstName(String firstName); 
    . 
    . 
    . 
    public boolean isEnabled(); 
    public void setEnabled(boolean enabled); 
    public Set<Role> getRoles(); 
    public Role addRole(Class<? extends Role> roleType); 
    public void removeRole(Class<? extends Role> roleType); 
    public boolean hasRole(Class<? extends Role> roleType); 
    public Role getRole(Class<? extends Role> roleType); 

    public enum Gender {MALE, FEMALE, UNKNOWN}; 
} 

public class PersonImpl implements Person { 
    . 
    . 
    . 
} 

public abstract class Role implements Person { 
protected PersonImpl person; 

    @Transient 
    protected abstract String getRoleName(); 
    protected Role() {} 
    public Role(PersonImpl person) { 
     this.person = person; 
    } 
    public String getFirstName() { 
     return person.getFirstName(); 
    } 
    public void setFirstName(String firstName) { 
     person.setFirstName(firstName); 
    } 
    public Set<Role> getRoles() { 
     return person.getRoles(); 
    } 
    public Role addRole(Class<? extends Role> roleType) { 
     return person.addRole(roleType); 
    } 
    . 
    . 
    . 
} 

public abstract class RoleImpl extends Role { 
    private String roleName; 

    protected RoleImpl() {} 

    public RoleImpl(PersonImpl person) { 
     super(person); 
    } 
    . 
    . 
    . 
} 

public class Employee extends RoleImpl { 
    private Date joiningDate; 
    private Date leavingDate; 
    private double salary; 

    public Employee(PersonImpl person) { 
     super(person); 
    } 
    . 
    . 
    . 
} 

Questo diagramma mostra i rapporti di classe:

(Se non è possibile vedere la linea schema, vederlo qui via yUML)

Vorrei usare queste classi in questo modo:

Person p = new Person("Doe", "John", Person.MALE, ...); 
// assuming Employee extends Role 
Employee e = (Employee)p.addRole(Employee.class); 

Poiché la classe Ruolo implementa anche l'interfaccia persona, posso anche fare:

// assuming Parent extends Role 
Parent parent = new Parent((PersonImpl)p); 

e.addRole(Parent.class); 
e.getDateOfBirth(); // handled by the decorated person class 

// assuming Manager extends Employee extends Role 
Manager m = (Manager)p.getRole(Manager); 

if (m.hasRole(Employee.class) { 
    // true since Manager derives from Employee 
} 

Le domande che ho sono:

(a) Se questa implementazione inutilmente complesso e in caso affermativo quali sarebbero essere un approccio più semplice? Si noti che questo va in un'applicazione non banale business e non un progetto di Kiddy, e credo che la creazione di sottoclassi ruolo è importante sfruttare il comportamento in casi come dipendenti, manager, ecc

(b) Come faccio a mappare questo in JPA/Hibernate?

(c) Può essere mappato in modo da poter usufruire anche di Seam Identity Management? (La mia definizione di ruolo non è chiaramente analoga a quella di Seam)

(d) Se vado con una strategia di mappatura table-per-subclass (InheritanceType.JOINED), mappo PersonImpl come PERSONS e RoleImpl come tabelle ROLES e mappa ogni sottoclasse di RoleImpl (come Employee e Parent) alle proprie tabelle come DIPENDENTI e GENITORI.

Avrei quindi una relazione @ManyToMany tra PERSONS e ROLES (nella raccolta ruoli in PersonImpl), con la tabella di join PERSON_ROLES.

Ora il problema è che le tabelle IMPIEGATI e GENITORI, ecc. Hanno solo il riferimento ROLE_ID, poiché la strategia di mappatura dell'ereditari le considera ovviamente estensioni dei ruoli (ROLES) mentre io ho bisogno di esse come addendum a PERSON_ROLES e richiedono la chiave USER_ID + ROLE_ID da risolvere correttamente o almeno USER_ID.

Preferirei un database normalizzato con la dipendenza da ulteriori join rispetto a un database denormalizzato che sarà difficile da mantenere e probabilmente raggrupperà una tonnellata di campi inutilizzati e irrilevanti, motivo per cui penso che la tabella per sottoclasse sia la strada da percorrere

Oppure una gerarchia table-per-class (InheritanceType.SINGLE_TABLE) con una colonna discriminante OK (dal punto di vista della manutenzione del database) in questo scenario? Si noti che alcuni ruoli hanno probabilmente dozzine di proprietà/campi.

(e) Esiste un'alternativa migliore a questo progetto?

Apprezzeremmo molto qualsiasi idea/suggerimento.

+0

Se ogni oggetto ruolo si applica a un oggetto singola persona, come dice il codice, allora perché c'è un rapporto @ManyToMany tra le persone ei ruoli? Non è @OneToMany? –

+0

Una persona può avere più ruoli. Ogni ruolo può avere più persone. Penso che il punto di Arthur di @OneToMany su un lato e @ManyToOne sull'altro possa avere qualche merito. Devo controllare ... –

risposta

6

Come detto

Quindi, per favore abbiate pazienza con me

Quindi la mia risposta sarà basato su quello che hai detto

Tutti i ruoli sono hard-coded ... C'è una classe astratta Role ...

Se c'è un abstractRole di classe, quindi suppongo che alcune proprietà definite in AbstractRole classe vengono ereditate da tutti sottoclassi

@Entity 
/** 
    * Which strategy should we use ??? keep reading 
    */ 
@Inheritance 
public abstract class AbstractRole implements Serializable { 

    private Integer id; 

    private String commonProperty; 
    private String otherCommonProperty; 
    private String anotherCommonProperty; 

    @Id 
    @GeneratedValue 
    public Integer getId() { 
     return this.id; 
    } 

    // getter's and setter's 

} 

Ora dipendenti (lo stesso approccio usato da Parent and Student)

@Entity 
public class Employee extends AbstractRole { 

    private String specificEmployeeProperty; 

    // getter's and setter's 

} 

Ma quale strategia di ereditarietà Per utilizzare ???

InheritanceType.JOINED

  • sottoclassi hanno una mappatura complessa: molte proprietà, altre relazioni come @OneToMany, @OneToOne, @ManyToMany e così via
  • Se si utilizza Hibernate come provider JPA, tenere in mente non supporta la colonna discriminator. Vedi here
  • Una query semplice unirà TUTTA tutta la tabella definita per ogni sottoclasse. Forse vuoi solo recuperare una sottoclasse e vedrai problemi di prestazioni. Here risolve il problema.

InheritanceType.SINGLE_TABLE

  • sottoclassi hanno una semplice mappatura.Non così tante proprietà e quantità minima di altre relazioni
  • risoluzione di classe concreta in fase di esecuzione
  • Si tratta di un sacco di colonne nullable una volta che tutte le sottoclassi condividono la stessa tabella
  • Prestazioni più veloci

Ora vediamo

c'è una persona con i metodi di gestione dei ruoli, come addRole(), removeRole(), hasRole(), getRole(), getRoles(), tra le altre cose

01.235.

Beh, se vedo qualcosa di simile addRole metodo, suppongo persona ha o @OneToMany o relazione @ManyToMany con classe AbstractRole. Lo so, lo so ... Hai una relazione @ManyToMany ma ho davvero un consiglio hai diviso il tuo @ManyToMany in @OneToMany - la relazione @ManyToOne. Vedere here Come

@Entity 
public class Person implements Serializable { 

    private Integer id; 

    private List<AbstractRole> roleList; 

    @Id 
    @GeneratedValue 
    public Integer getId() { 
     return this.id; 
    } 

    @OneToMany 
    public List<AbstractRole> getRoleList() { 
     return this.roleList; 
    } 

    // getter's and setter's 

} 

E infine

Può essere mappato in modo da poter usufruire del Seam Identity Management

Seam Identity Management consente di implementare il proprio comportamento a prescindere di utilizzare JPA o non.

@Name("authenticationManager") 
public class AuthenticationManager { 

    /** 
    * Starting with Seam 2.1+, you should use Credentials instead of Identity 
    * To collect your username and password 
    * 
    * Your JSF Form should looks like 
    * 
    * <h:inputText value="#{credentials.username}"/> 
    * <h:inputSecret value="#{credentials.password}"/> 
    */ 
    private @In org.jboss.seam.security.Credentials credentials; 

    /** 
     * Login method 
     *  must take no arguments 
     *  must return a boolean indicating whether the credentials could be verified 
     * 
     * method name does not mind 
     */ 
    public boolean authenticate() { 
     /** 
      * Logic To verify whether credentials is valid goes here 
      */ 
    } 

} 

e definire il metodo authenticate-in /WEB-INF/components.xml

<security:identity authenticate-method="#{authenticationManager.authenticate}"/> 

Chi la mappatura Dipende requisito aziendale, le esigenze dei clienti e così via ... ma io credo che possa essere più semplice come illustrato sopra

Buona fortuna!

+0

Ho aggiunto snippet di codice che mostrano le mie definizioni di interfaccia/classe. Hai ragione nel presumere che esista una raccolta per ruoli in persona. Passerà in rassegna la tua risposta in dettaglio prima di tornare. Grazie. –

+0

@ alan-p Vedi http://stackoverflow.com/questions/2443790/2468185#2468185 come implementiamo uno scenario simile. Un paio di note: non utilizzerei Person come interfaccia implementata da AbstractRole. Penso che il tuo modello potrebbe essere più semplice. Perché non usare Person come un semplice POJO invece di usarlo come interfaccia ??? Capisco si può avere un sistema di gestione persona/ruolo complesso, ma mantenere il codice più semplice. Se possibile, l'approccio all'uso del dominio-driven-design Vedere http://stackoverflow.com/questions/2597219/2598168#2598168 –

+0

non ho ottenuto intorno a fare questo completamente, ma penso che molti dei punti che sollevate sono valide e sono stato utile così sto accettando questa risposta. Grazie. –