2010-07-13 45 views
8

Đây là một câu hỏi thiết kế khá dài (không quá phức tạp) vì vậy hãy chịu với tôi. Tôi đang cố gắng triển khai hệ thống quản lý vai trò/người với POJO và JPA. Tôi khá mới với ORM và điều này chủ yếu là một vấn đề lập bản đồ.Lập bản đồ thừa kế với JPA/Hibernate

Tôi đã làm việc này như POJO và cảm thấy thoải mái với API cấp người gọi, nhưng bây giờ muốn ánh xạ nó tới cơ sở dữ liệu bằng JPA hoặc Hibernate trong môi trường Seam.

Việc triển khai của tôi dựa trên Mẫu trang trí (GoF) và Mẫu đối tượng vai trò người (Baumer/Riehle et al). Tất cả các vai trò được mã hóa cứng và bổ sung thời gian chạy của các vai trò mới không được hỗ trợ vì nó sẽ yêu cầu thay đổi mã để mở rộng hành vi. Tôi muốn sử dụng các nhóm người dùng để thực hiện bảo mật và quyền.

Có giao diện Person với các phương thức quản lý vai trò như addRole(), removeRole(), hasRole(), getRole(), getRoles(), trong số những thứ khác. Việc triển khai cụ thể được cung cấp bởi một lớp PersonImpl.

Có một lớp trừu tượng Vai trò cũng thực hiện giao diện Người (cho tương đương thay thế trang trí) và lớp RoleImpl mở rộng nó. Lớp Role giữ một tham chiếu đến cá thể cá nhân, sử dụng nó để phục vụ bất kỳ cuộc gọi phương thức/thuộc tính nào trên giao diện người, nghĩa là tất cả các lớp con của Role có thể xử lý giao diện Person. Hàm tạo vai trò nhận đối tượng person làm tham số.

Đây là những giao diện/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); 
    } 
    . 
    . 
    . 
} 

Biểu đồ này cho thấy mối quan hệ lớp:

(Nếu bạn không thể nhìn thấy inline sơ đồ, xem nó ở đây via yUML)

Tôi muốn sử dụng các lớp này như sau:

Person p = new Person("Doe", "John", Person.MALE, ...); 
// assuming Employee extends Role 
Employee e = (Employee)p.addRole(Employee.class); 

Kể từ khi lớp Vai trò cũng thực hiện các giao diện người, tôi cũng có thể làm:

// 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 
} 

Các câu hỏi tôi có bao gồm:

(a) là thực hiện này phức tạp vô và nếu như vậy những gì sẽ là một cách tiếp cận đơn giản hơn? Lưu ý rằng điều này đi vào một ứng dụng kinh doanh không tầm thường và không phải là một dự án kiddy, và tôi nghĩ rằng vai trò phân lớp là quan trọng để thúc đẩy hành vi trong các trường hợp như Nhân viên, Quản lý, v.v.

(b) Làm cách nào để ánh xạ trong JPA/Hibernate?

(c) Có thể được ánh xạ để tôi cũng có thể tận dụng lợi thế của Quản lý danh tính Seam không? (Định nghĩa của tôi về vai trò rõ ràng là không giống với Seam)

(d) Nếu tôi đi với chiến lược lập bản đồ bảng cho mỗi lớp con (InheritanceType.JOINED), tôi ánh xạ PersonImpl thành PERSONS và RoleImpl dưới dạng bảng ROLES và bản đồ mỗi phân lớp của RoleImpl (chẳng hạn như Employee và Parent) cho các bảng riêng của họ là EMPLOYEES và PARENTS.

Sau đó tôi sẽ có mối quan hệ @ManyToMany giữa PERSONS và ROLES (trong bộ sưu tập vai trò trong PersonImpl), với bảng PERSON_ROLES tham gia.

Bây giờ vấn đề là các bảng EMPLOYEES và PARENTS, vv chỉ có tham chiếu ROLE_ID, vì chiến lược ánh xạ thừa kế rõ ràng coi chúng là các phần mở rộng cho Roles (ROLES) trong khi tôi cần chúng như là phụ lục cho PERSON_ROLES và yêu cầu phím USER_ID + ROLE_ID được giải quyết đúng cách hoặc ít nhất USER_ID.

Tôi thích một cơ sở dữ liệu chuẩn hóa hơn với cơ sở dữ liệu không chuẩn hóa, sẽ khó duy trì và có khả năng chứa rất nhiều trường không sử dụng và không liên quan. con đường để đi.

Hoặc là một hệ thống phân cấp theo từng lớp (InheritanceType.SINGLE_TABLE) với cột phân biệt đối xử OK (từ quan điểm bảo trì cơ sở dữ liệu) trong trường hợp này? Lưu ý rằng một số vai trò có khả năng có hàng chục thuộc tính/trường.

(e) Có cách nào thay thế tốt hơn cho thiết kế này?

Rất nhiều đánh giá cao mọi ý tưởng/đề xuất.

+0

Nếu mỗi đối tượng vai trò áp dụng cho đối tượng một người, như mã của bạn ngụ ý, thì tại sao có mối quan hệ @ManyToMany giữa PERSONS và ROLES? Phải không @OneToMany? –

+0

Một người có thể có nhiều vai trò. Mỗi vai trò có thể có nhiều người. Tôi nghĩ điểm Arthur của @OneToMany ở một bên và @ManyToOne ở bên kia có thể có một số công đức. Phải kiểm tra ... –

Trả lời

6

Như đã nói

Vì vậy, xin vui lòng chịu với tôi

Vì vậy, câu trả lời của tôi sẽ dựa trên những gì bạn nói

Tất cả các vai trò được mã hóa cứng ... Có một lớp học trừu tượng Vai trò ...

Nếu có AbstractRole lớp, vì vậy tôi cho rằng một số tính chất quy định tại AbstractRole lớp được thừa kế bởi tất cả các lớp con

@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 

} 

Bây giờ nhân viên (Tương tự phương pháp được sử dụng bởi phụ huynh và sinh viên)

@Entity 
public class Employee extends AbstractRole { 

    private String specificEmployeeProperty; 

    // getter's and setter's 

} 

Nhưng mà kế thừa chiến lược Để sử dụng ???

InheritanceType.JOINED

  • lớp con có một ánh xạ phức tạp: nhiều tài sản, các mối quan hệ khác như @OneToMany, @OneToOne, @ManyToMany và vân vân
  • Nếu bạn sử dụng Hibernate là nhà cung cấp JPA, giữ trong tâm trí nó không hỗ trợ cột phân biệt đối xử. Xem here
  • Truy vấn đơn giản sẽ UNION TẤT CẢ tất cả Bảng được xác định cho từng phân lớp. Có lẽ bạn chỉ muốn truy xuất chỉ một lớp con và bạn sẽ thấy các vấn đề hiệu suất. Here giải quyết vấn đề.

InheritanceType.SINGLE_TABLE

  • lớp con có một bản đồ đơn giản.Không phải như vậy nhiều tài sản và số tiền tối thiểu của các mối quan hệ khác
  • độ phân giải của lớp bê tông tại thời gian chạy
  • Nó liên quan đến rất nhiều cột nullable một lần tất cả các lớp con chia sẻ cùng một bảng
  • nhanh hơn hiệu suất

Bây giờ chúng ta hãy xem

có một Người có phương pháp quản lý vai trò như addRole(), removeRole(), hasRole(), getRole(), getRoles(), trong số những thứ khác

01.235.

Vâng, nếu tôi thấy một cái gì đó như phương pháp addRole, tôi giả sử Người có mối quan hệ @OneToMany hoặc @ManyToMany với lớp AbstractRole. Tôi biết, tôi biết ... Bạn có mối quan hệ @ManyToMany nhưng tôi thực sự khuyên bạn chia @ManyToMany của mình thành @OneToMany - mối quan hệ @ManyToOne. Xem here cách Để

@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 

} 

Và cuối cùng

Nó có thể được ánh xạ vì vậy tôi cũng có thể tận dụng Seam Identity Management

Seam nhận dạng quản lý cho phép bạn thực hiện hành vi của mình bất kể sử dụng JPA hoặc không phải là.

@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 
      */ 
    } 

} 

Và xác định xác thực-phương pháp của bạn trong /WEB-INF/components.xml

<security:identity authenticate-method="#{authenticationManager.authenticate}"/> 

Về bản đồ của bạn Nó phụ thuộc vào yêu cầu kinh doanh, nhu cầu khách hàng và vân vân ... nhưng tôi Hãy nghĩ rằng nó có thể đơn giản hơn như được hiển thị ở trên

Chúc may mắn!

+0

Tôi đã thêm đoạn mã hiển thị định nghĩa lớp/giao diện của mình. Bạn có quyền giả định rằng có một bộ sưu tập cho vai trò trong cá nhân. Sẽ xem xét chi tiết câu trả lời của bạn trước khi hoàn nguyên. Cảm ơn. –

+0

@ alan-p Xem http://stackoverflow.com/questions/2443790/2468185#2468185 cách chúng tôi triển khai kịch bản tương tự. Một vài lưu ý: Tôi sẽ không sử dụng Person như một giao diện được triển khai bởi AbstractRole. Tôi nghĩ mô hình của bạn có thể đơn giản hơn. Tại sao không sử dụng Person làm POJO đơn giản thay vì sử dụng nó như một giao diện ??? Tôi hiểu rằng bạn có thể có một hệ thống quản lý vai trò/người phức tạp, nhưng giữ mã đơn giản hơn. Nếu có thể, hãy sử dụng phương pháp tiếp cận theo thiết kế theo miền Xem http://stackoverflow.com/questions/2597219/2598168#2598168 –

+0

Tôi chưa từng làm tròn nhưng tôi nghĩ rằng rất nhiều điểm bạn đã nêu là hợp lệ và đã được hữu ích vì vậy tôi chấp nhận câu trả lời này. Cảm ơn. –