2010-07-13 6 views
8

Il s'agit d'une question de conception assez longue (pas trop compliquée), alors s'il vous plaît gardez-moi. J'essaie de mettre en place un système de gestion des personnes/rôles avec les POJO et JPA. Je suis assez nouveau à ORM et c'est surtout un problème de cartographie. J'ai cela fonctionne comme POJOs et je suis à l'aise avec l'API au niveau de l'appelant, mais je voudrais maintenant le mapper à une base de données en utilisant JPA ou Hibernate dans un environnement Seam. Mon implémentation est basée sur les modèles Décorateur (GoF) et Objet de rôle de personne (Baumer/Riehle et al.). Tous les rôles sont codés en dur et l'ajout de nouveaux rôles au moment de l'exécution n'est pas pris en charge car il nécessiterait des modifications de code pour étendre le comportement. J'utiliserais des groupes d'utilisateurs pour implémenter la sécurité et les permissions.Mappage d'héritage avec JPA/Hibernate

Il existe une interface Person avec des méthodes de gestion de rôle telles que addRole(), removeRole(), hasRole(), getRole(), getRoles(), entre autres choses. L'implémentation concrète est fournie par une classe PersonImpl.

Il existe un rôle de classe abstraite qui implémente également l'interface Person (pour l'équivalence de substitution du décorateur) et une classe RoleImpl qui l'étend. La classe Role contient une référence à une instance de personne, l'utilisant pour gérer les appels de méthode/propriété sur l'interface de personne, ce qui signifie que toutes les sous-classes de Role peuvent gérer l'interface Person. Le constructeur de rôle prend l'objet person en paramètre.

Ce sont les interfaces/classes:

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); 
    } 
    . 
    . 
    . 
} 

Ce diagramme montre les rapports de classe:

(Si vous ne pouvez pas voir la ligne du diagramme, voir ici via yUML)

J'utiliserais ces classes comme suit:

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

Depuis la classe de rôle implémente également l'interface de personne, je peux aussi faire:

// 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 
} 

Les questions que je sont:

(a) Est-ce la mise en œuvre inutilement complexe et si oui quelle serait être une approche plus simple? Notez que cela va dans une application métier non triviale et pas un projet kiddy, et je pense que le rôle de sous-classe est important pour tirer parti des comportements dans des cas tels que Employé, Gestionnaire, etc.

(b) Comment mapper ceci? dans JPA/Hibernate?

(c) Peut-il être mappé pour que je puisse également profiter de Seam Identity Management? (D) Si j'utilise une stratégie de mappage table par sous-classe (InheritanceType.JOINED), je mappe PersonImpl en tant que tables PERSON et RoleImpl en tant que tables ROLES, et mappe chaque sous-classe de RoleImpl (comme Employee et Parent) à leurs propres tables comme EMPLOYEES et PARENTS.

J'aurais alors une relation @ManyToMany entre PERSONS et ROLES (sur la collection de rôles dans PersonImpl), avec la table de jointure PERSON_ROLES.Maintenant le problème est que les tables EMPLOYEES et PARENTS, etc. ont seulement la référence ROLE_ID, puisque la stratégie de mapping d'héritage les considère évidemment comme des extensions de ROLES (ROLES) alors que j'en ai besoin comme addenda à PERSON_ROLES, et exige la clé USER_ID + ROLE_ID à résoudre correctement, ou au moins le USER_ID. Je préférerais une base de données normalisée avec une dépendance sur les jointures supplémentaires qu'une base de données dénormalisée qui sera difficile à maintenir et qui regroupera probablement une tonne de champs inutilisés et non pertinents, c'est pourquoi je pense que la table par sous-classe est le chemin à parcourir.

Ou est une hiérarchie de table par classe (InheritanceType.SINGLE_TABLE) avec une colonne de discriminant OK (du point de vue de la maintenance de la base de données) dans ce scénario? Notez que certains rôles sont susceptibles d'avoir des dizaines de propriétés/champs. (E) Y a-t-il une meilleure alternative à cette conception?

Apprécierait beaucoup des idées/suggestions.

+0

Si chaque objet de rôle s'applique à un objet individuel, comme votre code l'indique, alors pourquoi existe-t-il une relation @ManyToMany entre PERSONNES et ROLES? N'est-ce pas @OneToMany? –

+0

Une personne peut avoir plusieurs rôles. Chaque rôle peut avoir plusieurs personnes. Je pense que le point d'Arthur @OneToMany d'un côté et @ManyToOne de l'autre peut avoir un certain mérite. Je dois vérifier ... –

Répondre

6

Comme dit

Alors s'il vous plaît garder avec moi

Donc ma réponse sera basée sur ce que vous avez dit

Tous les rôles sont codés en dur ... Il est une classe abstraite Rôle ...

S'il existe un AbstractRole classe, donc je suppose que certaines propriétés définies dans la classe AbstractRole sont hérités par tous les sous-classes

@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 

} 

maintenant employé (approche même utilisée par les parents et étudiants)

@Entity 
public class Employee extends AbstractRole { 

    private String specificEmployeeProperty; 

    // getter's and setter's 

} 

Mais quelle stratégie héritage à utiliser ???

InheritanceType.JOINED

  • ont une cartographie sous-classes complexe: de nombreuses propriétés, d'autres relations que @OneToMany, @OneToOne, @ManyToMany et ainsi de suite
  • Si vous utilisez Hibernate en tant que fournisseur JPA, garder à l'esprit, il ne supporte pas la colonne discriminante. Voir here
  • Une simple requête ALLERA TOUT tout le tableau défini pour chaque sous-classe. Peut-être que vous voulez juste récupérer une seule sous-classe et vous verrez des problèmes de performance. Here va une solution de contournement.

InheritanceType.SINGLE_TABLE

  • ont une cartographie sous-classes simple.Pas tant de propriétés et quantité minimale d'autres relations
  • résolution de classe concrète lors de l'exécution
  • Il traite avec beaucoup de colonnes nullables une fois que toutes les sous-classes partagent la même table
  • Performances plus rapides

maintenant nous allons voir

Il y a une personne avec des méthodes de gestion des rôles tels que addRole(), removeRole(), hasRole(), getRole(), getRoles(), entre autres

Eh bien, si je vois quelque chose comme méthode addRole, je suppose que personne a soit @OneToMany ou relation @ManyToMany avec classe AbstractRole. Je sais, je sais ... Vous avez la relation @ManyToMany mais je conseille vraiment vous divisez votre @ManyToMany en relation @OneToMany - @ManyToOne. Voir here Comment

@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 

} 

Et enfin

Peut-il être mis en correspondance donc je peux aussi profiter de Seam Identity Management

Seam Identity Management permet d'implémenter votre propre comportement quel que soit d'utiliser JPA ou 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 
      */ 
    } 

} 

et définir votre Authentifier méthode dans /WEB-INF/components.xml

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

À propos de votre cartographie Cela dépend de besoins de l'entreprise, les besoins des clients et ainsi de suite ... mais je Pensez que cela peut être plus simple comme montré ci-dessus

Bonne chance!

+0

J'ai ajouté des extraits de code montrant mes définitions d'interface/classe. Vous avez raison de supposer qu'il existe une collection pour les rôles en personne. Traitera votre réponse en détail avant de revenir en arrière. Merci. –

+0

@ alan-p Voir http://stackoverflow.com/questions/2443790/2468185#2468185 comment nous implémentons un scénario similaire. Quelques notes: je n'utiliserais pas Person comme interface implémentée par AbstractRole. Je pense que votre modèle pourrait être plus simple. Pourquoi ne pas utiliser Person comme un simple POJO au lieu de l'utiliser comme interface? Je comprends que vous pouvez avoir un système de gestion de personne/rôle complexe, mais gardez le code plus simple. Si possible, utilisez une approche de conception axée sur le domaine Voir http://stackoverflow.com/questions/2597219/2598168#2598168 –

+0

Je ne suis pas encore arrivé à faire cela complètement, mais je pense que beaucoup des points que vous avez soulevés sont valides et ont été utiles, donc j'accepte cette réponse. Merci. –

Questions connexes