2010-07-13 126 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