This is a fairly lengthy (not overly complex) design question so please bear with me. I'm trying to implement a person/role management system with POJOs and JPA. I'm fairly new to ORM and this is mostly a mapping problem.
I've got this working as POJOs and am comfortable with the caller-level API, but would now like to map it to a database using JPA or Hibernate in a Seam environment.
My implementation is based on the Decorator (GoF) and Person Role Object patterns (Baumer/Riehle et al). All roles are hard-coded and run-time addition of new roles is not supported since it would require code changes to extend behavior. I'd use user groups to implement security and permissions.
There is a Person interface with role management methods such as addRole(), removeRole(), hasRole(), getRole(), getRoles(), among other things. The concrete implementation is provided by a PersonImpl class.
There is an abstract class Role which also implements the Person interface (for decorator substitution equivalence), and a RoleImpl class that extends it. The Role class holds a reference to a person instance, using it to service any method / property calls on the person interface, meaning all subclasses of Role can handle the Person interface. The role constructor takes the person object as a parameter.
These are the 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);
}
.
.
.
}
This diagram shows the class relationships:
(If you can't see the diagram inline, view it here via yUML)
I'd use these classes like so:
Person p = new Person("Doe", "John", Person.MALE, ...);
// assuming Employee extends Role
Employee e = (Employee)p.addRole(Employee.class);
Since the Role class also implements the Person interface, I can also do:
// 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
}
The questions I have are:
(a) Is this implementation needlessly complex and if so what would be a simpler approach? Note that this goes into a non-trivial business application and not a kiddy project, and I think the role subclassing is important to leverage behavior in cases such as Employee, Manager, etc.
(b) How do I map this in JPA/Hibernate?
(c) Can it be mapped so I can also take advantage of Seam Identity Management? (My definition of Role is clearly not analogous to Seam's)
(d) If I go with a table-per-subclass (InheritanceType.JOINED) mapping strategy, I map PersonImpl as PERSONS and RoleImpl as ROLES tables, and map each subclass of RoleImpl (such as Employee and Parent) to their own tables as EMPLOYEES and PARENTS.
I would then have a @ManyToMany relationship between PERSONS and ROLES (on the roles collection in PersonImpl), with the join-table PERSON_ROLES.
Now the problem is that the EMPLOYEES and PARENTS tables, etc. have only the ROLE_ID reference, since the inheritance mapping strategy obviously considers them to be extensions to Roles (ROLES) whereas I need them as addendum to PERSON_ROLES, and require the USER_ID + ROLE_ID key to be resolved properly, or at least the USER_ID.
I would prefer a normalized database with the attendant reliance on extra joins than a denormalized database which will be difficult to maintain and likely bunch up a ton of unused and irrelevant fields, which is why I think table-per-subclass is the way to go.
Or is a table-per-class-hierarchy (InheritanceType.SINGLE_TABLE) with a discriminator column OK (from database maintenance perspective) in this scenario? Note that some roles are likely to have dozens of properties/fields.
(e) Is there a better alternative to this design?
Would very much appreciate any ideas/suggestions.