2010-07-13 53 views
8

Esta es una pregunta de diseño bastante larga (no demasiado compleja), por favor tenga paciencia conmigo. Estoy tratando de implementar un sistema de gestión de personas/roles con POJOs y JPA. Soy bastante nuevo en ORM y esto es principalmente un problema de mapeo.Mapeo de herencia con JPA/Hibernate

Tengo esto funcionando como POJOs y me siento cómodo con la API de nivel de llamada, pero ahora me gustaría asignarlo a una base de datos usando JPA o Hibernate en un entorno Seam.

Mi implementación se basa en los patrones Decorator (GoF) y Person Role Object (Baumer/Riehle et al). Todos los roles están codificados y la adición de nuevas funciones en tiempo de ejecución no es compatible ya que requeriría cambios de código para extender el comportamiento. Usaría grupos de usuarios para implementar seguridad y permisos.

Hay una interfaz de persona con métodos de administración de roles como addRole(), removeRole(), hasRole(), getRole(), getRoles(), entre otras cosas. La implementación concreta es proporcionada por una clase PersonImpl.

Existe una clase de clase abstracta que también implementa la interfaz de persona (para la equivalencia de sustitución de decorador) y una clase RoleImpl que la amplía. La clase Role contiene una referencia a una instancia de persona, usándola para atender cualquier llamada de método/propiedad en la interfaz de persona, lo que significa que todas las subclases de Role pueden manejar la interfaz de Persona. El constructor de roles toma el objeto persona como un parámetro.

Estas son las interfaces/clases:

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

Este diagrama muestra las relaciones de clase:

(Si no puede ver el diagrama en línea, ver aquí) via yUML

Utilizaría estas clases como lo siguiente:

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

Desde la clase, clases, también implementa la interfaz persona, también puedo hacer:

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

las preguntas que tengo son:

(a) ¿Es esta aplicación innecesariamente complejo y si es así lo haría ser un enfoque más simple? Tenga en cuenta que esto entra en una aplicación comercial no trivial y no en un proyecto kiddy, y creo que la función de subclases es importante para aprovechar el comportamiento en casos como Employee, Manager, etc.

(b) ¿Cómo mapeo esto? en JPA/Hibernate?

(c) ¿Se puede mapear para que yo también pueda aprovechar Seam Identity Management? (Mi definición de Rol claramente no es análoga a la de Seam)

(d) Si voy con una estrategia de mapeo de tabla por subclase (InheritanceType.JOINED), hago un mapa de PersonImpl como PERSONS y RoleImpl como tablas ROLES, y trazo cada subclase de RoleImpl (como Employee y Parent) en sus propias tablas como EMPLOYEES y PARENTS.

Tendría una relación @ManyToMany entre PERSONAS y ROLES (en la colección de roles en PersonImpl), con la tabla de combinación PERSON_ROLES.

Ahora el problema es que las tablas EMPLOYEES y PARENTS, etc. solo tienen la referencia ROLE_ID, ya que la estrategia de mapeo de herencia las considera obviamente como extensiones a Roles (ROLES) mientras que las necesito como adición a PERSON_ROLES, y requieren la clave USER_ID + ROLE_ID se resolverá correctamente, o al menos USER_ID.

Preferiría una base de datos normalizada con la dependencia de extra uniones extra que una base de datos desnormalizada que será difícil de mantener y probablemente junte una tonelada de campos no utilizados e irrelevantes, por lo que creo que la tabla por subclase es el camino a seguir.

¿O es una tabla por jerarquía de clases (InheritanceType.SINGLE_TABLE) con una columna de discriminador OK (desde la perspectiva de mantenimiento de la base de datos) en este escenario? Tenga en cuenta que es probable que algunos roles tengan docenas de propiedades/campos.

(e) ¿Hay una mejor alternativa a este diseño?

Agradecería cualquier idea/sugerencia.

+0

Si cada objeto de rol se aplica a un objeto de una sola persona, como lo implica su código, ¿por qué hay una relación @ManyToMany entre PERSONAS y ROLES? ¿No es @OneToMany? –

+0

Una persona puede tener múltiples roles. Cada rol puede tener varias personas. Creo que el punto de Arthur de @OneToMany por un lado y @ManyToOne por el otro puede tener algún mérito. Tengo que comprobar ... –

Respuesta

6

Como se ha dicho

Así que por favor tengan paciencia conmigo

Así que mi respuesta se basará en lo que ha dicho

Todos los papeles están codificadas ... Hay una clase abstracta Role ...

If There is a AbstractRole clase, por lo que supongo que algunas propiedades definidas en la clase AbstractRole son heredados por todas las subclases

@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 

} 

Ahora Empleado (El mismo enfoque utilizado por los Padres y Estudiantes)

@Entity 
public class Employee extends AbstractRole { 

    private String specificEmployeeProperty; 

    // getter's and setter's 

} 

Pero lo que la estrategia de heredar Para usar ???

InheritanceType.JOINED

  • subclases tienen un mapeo complejo: muchas propiedades, otras relaciones como @OneToMany, @OneToOne, @ManyToMany y así sucesivamente
  • Si utiliza Hibernate como proveedor JPA, mantenga en mente, no admite la columna de discriminador. Ver here
  • Una consulta simple UNION TODA la tabla definida para cada subclase. Tal vez solo quiera recuperar una sola subclase y verá problemas de rendimiento. Here va una solución.

InheritanceType.SINGLE_TABLE

  • subclases tienen una correspondencia sencilla.No tantas propiedades y cantidad mínima de otras relaciones
  • resolución de clase concreta en tiempo de ejecución
  • Se trata de una gran cantidad de columnas anulables una vez que todas las subclases comparten la misma tabla
  • Rendimiento más rápido

Ahora vamos a ver

hay una persona con los métodos de administración de funciones tales como addRole(), removeRole(), hasRole(), getRole(), getRoles(), entre otras cosas

Bueno, si veo algo así como addRole método, supongo persona tiene cualquier @OneToMany o relación con la clase @ManyToMany AbstractRole. Lo sé, lo sé ... Tienes @ManyToMany relación, pero realmente consejo divide tu @ManyToMany en @OneToMany - @ManyToOne relación. Ver here cómo

@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 

} 

Y finalmente

Puede ser mapeado así que también puede tomar ventaja de la costura de Gestión de Identidad Gestión

costura Identidad le permite implementar su propio comportamiento, independientemente de usar JPA o no.

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

} 

Y definir la Autentificación método en el /WEB-INF/components.xml

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

Acerca de su asignación Depende requisito de negocio, las necesidades del cliente y así sucesivamente ... pero Piensa que puede ser más simple como se muestra arriba

¡Buena suerte!

+0

He añadido fragmentos de código que muestran las definiciones de mi interfaz/clase. Tiene razón al suponer que hay una colección de Roles en persona. Revisará su respuesta detalladamente antes de revertir. Gracias. –

+0

@ alan-p Consulte http://stackoverflow.com/questions/2443790/2468185#2468185 cómo implementamos un escenario similar. Un par de notas: No usaría Person como una interfaz implementada por AbstractRole. Creo que tu modelo podría ser más simple. ¿Por qué no utilizar Person como un POJO simple en lugar de usarlo como interfaz? Entiendo que puede tener un sistema de gestión de persona/rol complejo, pero que el código sea más simple. Si es posible, utilice el enfoque de diseño basado en el dominio. Consulte http://stackoverflow.com/questions/2597219/2598168#2598168 –

+0

. No he podido hacerlo del todo, pero creo que muchos de los puntos que ha planteado son válidos y han sido útiles, así que estoy aceptando esta respuesta. Gracias. –