Это довольно длинный (не слишком сложный) вопрос дизайна, поэтому, пожалуйста, потерпите меня.Я пытаюсь внедрить систему управления людьми / ролями с POJO и JPA.Я довольно новичок в ORM, и это в основном проблема с отображением.
У меня это работает как POJO, и я доволен API уровня вызывающего абонента, но теперь хотел бы сопоставить его с базой данных, используяJPA или Hibernate в среде Seam.
Моя реализация основана на шаблонах Decorator (GoF) и Person Role Object (Baumer / Riehle et al).Все роли жестко запрограммированы, и добавление новых ролей во время выполнения не поддерживается, поскольку для расширения поведения потребуются изменения кода.Я бы использовал группы пользователей для реализации безопасности и разрешений.
Существует интерфейс 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);
}
.
.
.
}
На этой диаграмме показаны отношения классов:
(Если вы не видите диаграмму в строке, посмотрите ее здесь через 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) Является ли эта реализация излишне сложной, и если да, то чтобудет более простой подход?Обратите внимание, что это относится к нетривиальному бизнес-приложению, а не к детскому проекту, и я думаю, что подклассы ролей важны для использования поведения в таких случаях, как сотрудник, менеджер и т. Д.
(b) Как мнеотобразить это в JPA / Hibernate?
(c) Можно ли сопоставить его, чтобы я также мог воспользоваться преимуществами управления идентификацией шва?(Мое определение роли явно не аналогично определению Шва)
(d) Если я выберу стратегию отображения таблицы на подкласс (InheritanceType.JOINED), я сопоставлю PersonImpl как PERSONS и RoleImpl как таблицы ROLES,и сопоставить каждый подкласс RoleImpl (например, Employee и Parent) с их собственными таблицами, такими как EMPLOYEES и PARENTS.
Я бы тогда имел отношение @ManyToMany между PERSONS и ROLES (для коллекции ролей в PersonImpl), ссоединительный стол PERSON_ROLES.
Теперь проблема в том, что таблицы EMPLOYEES, PARENTS и т. Д. Имеют только ссылку ROLE_ID, поскольку стратегия отображения наследования, очевидно, рассматривает их как расширения ролей (ROLES), тогда как они нужны мне как дополнение к PERSON_ROLES,и требовать правильного разрешения ключа USER_ID + ROLE_ID или, по крайней мере, USER_ID.
Я бы предпочел нормализованную базу данных с зависимостью оператора от дополнительных объединений, чем денормализованную базу данных, которую будет сложно поддерживать и, вероятно, связатьдо тонны неиспользуемых и нерелевантных полей, поэтому я думаю, что путь к таблице для подкласса - это путь.
Или таблица для иерархии классов (InheritanceType.SINGLE_TABLE) со столбцом дискриминатораХорошо (с точки зрения обслуживания базы данных) в этом сценарии?Обратите внимание, что некоторые роли могут иметь десятки свойств / полей.
(e) Есть ли лучшая альтернатива этому проекту?
Буду очень признателен за любые идеи / предложения.