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