这是一个相当冗长(并不过于复杂)的设计问题,所以请耐心等待。我正在尝试使用POJO和JPA实现人员/角色管理系统。我是ORM的新手,这主要是一个映射问题。
我已经将它作为POJO工作了,并且对调用者级API感到满意,但现在想在Seam环境中使用JPA或Hibernate将其映射到数据库。
我的实现基于Decorator(GoF)和Person Role Object模式(Baumer / Riehle等人)。所有角色都是硬编码的,并且不支持运行时添加新角色,因为它需要更改代码以扩展行为。我将使用用户组来实现安全性和权限。
有一个Person接口,其中包含角色管理方法,如addRole(),removeRole(),hasRole(),getRole(),getRoles()等。具体实现由PersonImpl类提供。
还有一个抽象类Role,它还实现了Person接口(用于装饰器替换等价),以及一个扩展它的RoleImpl类。 Role类包含对person实例的引用,使用它来为person接口上的任何方法/属性调用提供服务,这意味着Role的所有子类都可以处理Person接口。角色构造函数将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);
}
.
.
.
}
此图显示了班级关系:
(如果您无法看到内联图,请在此处查看via yUML)
我会像这样使用这些类:
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)这种实施是否是不必要的复杂,如果是这样,那么什么是更简单的方法?请注意,这是一个非平凡的业务应用程序而不是kiddy项目,我认为角色子类化对于在Employee,Manager等情况下利用行为很重要。
(b)如何在JPA / Hibernate中映射它?
(c)是否可以映射,以便我也可以利用Seam身份管理? (我对角色的定义显然与Seam不相似)
(d)如果我使用table-per-subclass(InheritanceType.JOINED)映射策略,我将PersonImpl映射为PERSONS,将RoleImpl映射为ROLES表,并将RoleImpl的每个子类(例如Employee和Parent)映射到它们拥有EMPLOYEES和PARENTS的表格。
然后,我将在PERSONS和ROLES(PersonImpl中的角色集合)之间建立@ManyToMany关系,并使用连接表PERSON_ROLES。
现在的问题是EMPLOYEES和PARENTS表等只有ROLE_ID引用,因为继承映射策略显然认为它们是Roles(ROLES)的扩展,而我需要它们作为PERSON_ROLES的附录,并要求要正确解析的USER_ID + ROLE_ID键,或至少是USER_ID。
我更喜欢一个规范化的数据库,伴随着对额外连接的依赖,而不是非规范化的数据库,这将难以维护并且可能会堆积大量未使用和不相关的字段,这就是为什么我认为每个子类的表是要走的路。
或者是一个每个类的表层次结构(InheritanceType.SINGLE_TABLE),在这种情况下带有一个鉴别器列OK(从数据库维护的角度来看)?请注意,某些角色可能包含许多属性/字段。
(e)有更好的替代方案吗?
非常感谢任何想法/建议。
答案 0 :(得分:6)
如上所述
所以请耐心等待我
所以我的回答将基于你所说的
所有角色都是硬编码的......有一个抽象类角色......
如果有一个AbstractRole类,那么我想在AbstractRole类中定义的一些属性由所有子类继承
@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
}
现在员工(父母和学生使用相同的方法)
@Entity
public class Employee extends AbstractRole {
private String specificEmployeeProperty;
// getter's and setter's
}
但是哪种继承策略要使用???
<强> InheritanceType.JOINED 强>
<强> InheritanceType.SINGLE_TABLE 强>
现在让我们看看
有一个角色管理方法的人,比如addRole(),removeRole(),hasRole(),getRole(),getRoles()等等
好吧,如果我看到类似 addRole 的方法,我认为Person与AbstractOole类有@OneToMany或@ManyToMany关系。我知道,我知道......你有@ManyToMany关系,但我真的建议你将@ManyToMany分成@OneToMany - @ManyToOne关系。请参阅here如何
@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
}
最后
是否可以映射,以便我也可以利用Seam身份管理
Seam Identity Management 允许您实现自己的行为,无论是否使用JPA 。
@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
*/
}
}
在 /WEB-INF/components.xml 中定义您的身份验证方法
<security:identity authenticate-method="#{authenticationManager.authenticate}"/>
关于您的映射它取决于业务需求,客户需求等......但我认为它可以更简单,如上所示
祝你好运!