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.
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? –
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 ... –