这是一个相当冗长(并不是过于复杂)的设计问题,请耐心听我讲解。我正在尝试使用POJO和JPA实现人员/角色管理系统。我对ORM比较陌生,这主要是一个映射问题。
我已经将其作为POJO工作,并且对调用者级别的API感到满意,但现在想在Seam环境中使用JPA或Hibernate将其映射到数据库。
我的实现基于装饰器(GoF)和人员角色对象模式(Baumer/Riehle等)。所有角色都是硬编码的,不支持运行时添加新角色,因为这需要更改代码扩展行为。我将使用用户组来实现安全性和权限。
有一个Person接口,其中包含角色管理方法,例如addRole()、removeRole()、hasRole()、getRole()、getRoles()等。具体实现由PersonImpl类提供。
还有一个抽象类Role,它也实现了Person接口(用于装饰器替换等价性),以及扩展它的RoleImpl类。Role类保存对person实例的引用,使用它来服务于任何在person接口上的方法/属性调用,这意味着Role的所有子类都可以处理Person接口。role构造函数将person对象作为参数。
这些是接口/类:
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);
}
.
.
.
}
这个图表展示了类之间的关系: (如果您无法在此处看到图表,请在此处查看)
我会这样使用这些类:
Person p = new Person("Doe", "John", Person.MALE, ...);
// assuming Employee extends Role
Employee e = (Employee)p.addRole(Employee.class);
由于Role类还实现了Person接口,因此我也可以执行以下操作:
// 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
}
我有以下问题:
(a) 这个实现是否过于复杂,如果是的话,有什么更简单的方法?请注意,这是一个非平凡的商业应用程序,而不是一个儿童项目,我认为子类化角色在员工、经理等情况下利用行为很重要。
(b) 如何在JPA / Hibernate中映射它?
(c) 它是否可以映射,以便我也可以利用Seam身份管理?(我的角色定义显然与Seam的角色定义不同)
(d) 如果我采用表格-每个子类(InheritanceType.JOINED)映射策略,我将PersonImpl映射为PERSONS表,RoleImpl映射为ROLES表,并将RoleImpl的每个子类(例如Employee和Parent)映射到自己的表(如EMPLOYEES和PARENTS)。
然后,在PERSONS和ROLES之间(在PersonImpl的roles集合上)有一个@ManyToMany关系,使用join-table PERSON_ROLES。
现在的问题是,EMPLOYEES和PARENTS表只有ROLE_ID引用,因为继承映射策略显然认为它们是Roles(ROLES)的扩展,而我需要它们作为PERSON_ROLES的附录,并且需要正确解析USER_ID + ROLE_ID键,或者至少是USER_ID。
我更喜欢规范化的数据库,依赖于额外的连接,而不是难以维护且可能会堆积大量未使用和无关的字段的非规范化数据库,这就是为什么我认为表格-每个子类是正确的方法。
或者,在这种情况下,表格-类层次结构(InheritanceType.SINGLE_TABLE)是否有更好的选择?请注意,某些角色可能具有数十个属性/字段。
(e) 这个设计是否有更好的替代方案?
非常感谢任何想法/建议。