2010-07-13 224 views
8

这是一个相当长的(不是过于复杂的)设计问题,请耐心等待。我试图通过POJO和JPA实施人员/角色管理系统。我对ORM相当陌生,这主要是映射问题。JPA/Hibernate的继承映射

我已经得到了这个工作作为POJO和我舒适与呼叫者级别的API,但现在想使用JPA将其映射到数据库或在Seam环境中休眠。

我的实现基于Decorator(GoF)和Person Role Object模式(堡盟/ Riehle等人)。所有角色都是硬编码的,并且不支持运行时添加新角色,因为它需要更改代码以扩展行为。我会使用用户组来实现安全性和权限。

有一个Person接口,包含诸如addRole(),removeRole(),hasRole(),getRole(),getRoles()等角色管理方法。具体实现由PersonImpl类提供。

有一个抽象类Role也实现了Person接口(用于装饰器替代等价)以及一个扩展它的RoleImpl类。 Role类持有对个人实例的引用,用它来为人员界面上的任何方法/属性调用提供服务,这意味着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); 

由于角色类也实现了个人界面,我也可以这样做:

// 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项目,我认为角色子类化对于员工,经理等情况下的行为很重要。

(b)如何映射此在JPA /休眠?

(c)它可以被映射,所以我也可以利用Seam身份管理? (我的角色定义显然不像Seam's)

(d)如果我使用每个子类的继承类型(InheritanceType.JOINED)映射策略,我将PersonImpl映射为PERSONS和RoleImpl作为ROLES表,并映射RoleImpl的每个子类(如Employee和Parent)作为EMPLOYEES和PARENTS自己的表。

我将不得不(对角色的集合中PersonImpl的)人员和角色之间的一个@ManyToMany关系,联接表PERSON_ROLES。

现在的问题是,雇员和父母表等只有ROLE_ID引用,因为继承映射策略显然认为它们是角色(ROLES)的扩展,而我需要它们作为PERSON_ROLES的附录,并且需要USER_ID + ROLE_ID键被正确解析,或至少USER_ID。

我宁愿一个规范化的数据库,因为它依赖于额外的连接而不是一个非规范化的数据库,而这个数据库很难维护并且可能会聚集大量未使用和不相关的字段,这就是为什么我认为每个子类的表是要走的路。

或者在此场景中带有鉴别器列OK(从数据库维护角度看)的每个类的层次结构(InheritanceType.SINGLE_TABLE)?请注意,某些角色可能会有几十个属性/字段。 (e)这种设计有更好的选择吗?

非常感谢任何想法/建议。

+0

如果每个角色对象都适用于单人对象,如代码所示,那么为什么人和角色之间存在@ManyToMany关系?是不是@OneToMany? – 2010-07-18 18:28:16

+0

一个人可能有多个角色。每个角色可能有多个人。我认为亚瑟在@OneToMany的一面和@ManyToOne在另一面的观点可能有一些优点。必须检查... – 2010-07-19 06:28:41

回答

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

  • 子类有一个复杂的映射:许多特性,如@OneToMany,@OneToOne,@ManyToMany其他关系等
  • 如果你使用Hibernate作为JPA提供者,保留记住它不支持鉴别器列。请参阅here
  • 一个简单的查询将为每个子类定义的UNION ALL全部表。也许你只想检索一个子类,你会看到性能问题。 Here解决方法。

InheritanceType.SINGLE_TABLE

  • 子类具有简单的映射。没有那么多的性关系等的最小量在运行时具体类的
  • 分辨率
  • 它曾经有很多空列的涉及所有子类共享同一个表现在
  • 更快的性能

让我们来看看

有与角色管理的方法,如addRole(),removeRole(),hasRole(),getRole(人),getRoles(),除其他事项外

那么,如果我看到类似addRole方法,我想人有@OneToMany或@ManyToMany与AbstractRole类的关系。我知道,我知道...你有@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的身份管理无论让你实现你自己的行为使用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}"/> 

关于你的映射这取决于业务需求,客户需求等等...但我认为它可以更简单,如上所示

好运气!

+0

我已经添加了显示我的界面/类定义的代码片段。你是对的,假设有一个人的角色集合。 在恢复之前会详细回答您的回复。谢谢。 – 2010-07-14 10:58:50

+0

@ alan-p请参阅http://stackoverflow.com/questions/2443790/2468185#2468185我们如何实现类似的场景。几个注释:我不会使用Person作为AbstractRole实现的接口。我认为你的模型可能更简单。为什么不使用Person作为简单的POJO而不是将其用作界面?我知道你可以有一个复杂的人员/角色管理系统,但保持代码更简单。如果可能的话,使用域驱动设计方法请参阅http://stackoverflow.com/questions/2597219/2598168#2598168 – 2010-07-14 16:40:39

+0

我还没有完全做到这一点,但我认为你提出的许多要点是有效的,有帮助,所以我接受了这个答案。谢谢。 – 2010-10-24 20:38:31