Наследование, отображающееся с JPA/Hibernate

Это - довольно длинное (не чрезмерно сложный) вопрос о дизайне, поэтому терпите меня. Я пытаюсь реализовать систему управления человека/роли с POJOs и JPA. Я довольно плохо знаком с ORM, и это - главным образом отображающаяся проблема.

У меня есть эта работа POJOs, и довольно уровнем вызывающей стороны API, но теперь хотел бы отобразить его на базу данных с помощью JPA или Быть в спящем режиме в среде Шва.

Моя реализация основана на Декораторе (GoF) и Ролевых шаблонах Объекта Человека (Baumer/Riehle и др.). Все роли трудно кодируются, и добавление во время выполнения новых ролей не поддерживается, так как оно потребовало бы, чтобы изменения кода расширили поведение. Я использовал бы группы пользователей для реализации безопасности и полномочий.

Существует интерфейс Person с ролевыми методами управления, такими как addRole (), removeRole (), hasRole (), getRole (), getRoles (), среди прочего. Конкретная реализация обеспечивается классом PersonImpl.

Существует Роль абстрактного класса, которая также реализует интерфейс Person (для эквивалентности замены декоратора), и класс RoleImpl, который расширяет его. Ролевой класс содержит ссылку на экземпляр человека, с помощью него для обслуживания любого метода / свойство обращается к интерфейсу человека, означая, что все подклассы Роли могут обработать интерфейс Person. Ролевой конструктор берет объект человека в качестве параметра.

Это интерфейсы/классы:

public interface Person {
    public String getFirstName();

    public void setFirstName(String firstName);
    .
    .
    .
    public boolean isEnabled();
    public void setEnabled(boolean enabled);
    public Set getRoles();
    public Role addRole(Class roleType);
    public void removeRole(Class roleType);
    public boolean hasRole(Class roleType);
    public Role getRole(Class 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 getRoles() {
        return person.getRoles();
    }
    public Role addRole(Class 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);

Так как Ролевой класс также реализует интерфейс 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. ПРИСОЕДИНЕННЫЙ), отображающаяся стратегия, я отображаю PersonImpl как ЛЮДИ и RoleImpl как таблицы ROLES, и отображаю каждый подкласс RoleImpl (такого как Сотрудник и Родитель) к их собственным таблицам как СОТРУДНИКИ и РОДИТЕЛИ.

У меня затем были бы @ManyToMany отношения между ЛЮДЬМИ и РОЛЯМИ (на ролевом наборе в PersonImpl) с объединяющей таблицей PERSON_ROLES.

Теперь проблема состоит в том, что таблицы EMPLOYEES и PARENTS, и т.д. имейте только ссылку ROLE_ID, так как стратегия отображения наследования, очевидно, полагает, что они расширения Ролей (РОЛИ), тогда как мне нужны они как приложение к PERSON_ROLES, и требовать, чтобы USER_ID + ключ ROLE_ID был разрешен правильно, или по крайней мере USER_ID.

Я предпочел бы нормализованную базу данных с сопутствующей уверенностью в дополнительных соединениях, чем денормализованная база данных, которую будет трудно поддержать и вероятно сгруппировать выше на тонну неиспользованных и несоответствующих полей, которая является, почему я думаю, что таблица на подкласс является способом пойти.

Или table-per-class-hierarchy (InheritanceType. SINGLE_TABLE) со столбцом OK различителя (с точки зрения сопровождения базы данных) в этом сценарии? Обратите внимание, что некоторые роли, вероятно, будут иметь десятки свойств/полей.

(e) Существует ли лучшая альтернатива этому дизайну?

Очень ценил бы любые идеи/предложения.

8
задан Community 8 February 2017 в 14:28
поделиться

1 ответ

Как было сказано

Поэтому, пожалуйста, потерпите меня

Поэтому мой ответ будет основан на том, что вы сказали

Все роли жестко закодированы.... Есть абстрактный класс Role...

Если есть класс 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

}

Теперь Employee (тот же подход, что и в Parent и Student)

@Entity
public class Employee extends AbstractRole {

    private String specificEmployeeProperty;

    // getter's and setter's

}

Но какую стратегию наследования использовать?

InheritanceType.JOINED

  • Подклассы имеют сложное отображение: много свойств, другие отношения как @OneToMany, @OneToOne, @ManyToMany и так далее
  • Если вы используете Hibernate как JPA провайдер, имейте в виду, что он не поддерживает столбец дискриминатора. См. здесь
  • Простой запрос объединит все таблицы, определенные для каждого подкласса. Возможно, вы просто хотите получить только один подкласс, и вы увидите проблемы с производительностью. Вот обходной путь.

InheritanceType.SINGLE_TABLE

  • Подклассы имеют простое отображение. Не так много свойств и минимальное количество других отношений
  • Разрешение конкретного класса во время выполнения
  • Он имеет дело с большим количеством nullable столбцов, когда все подклассы разделяют одну и ту же таблицу
  • Более высокая производительность

Теперь давайте посмотрим

Есть Person с методами управления ролями, такими как addRole(), removeRole(), hasRole(), getRole(), getRoles(), среди прочего

Ну, если я вижу что-то вроде addRole метода, я предполагаю, что Person имеет либо @OneToMany, либо @ManyToMany отношения с классом AbstractRole. Я знаю, я знаю... У вас есть отношения @ManyToMany, но я очень советую вам разделить ваши @ManyToMany на @OneToMany - @ManyToOne отношения. Смотрите здесь как это сделать

@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 Identity Management

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

}

И определите ваш authenticate-метод в /WEB-INF/components.xml

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

О вашем отображении Это зависит от бизнес-требований, потребностей клиента и так далее... но я думаю, что это может быть проще, как показано выше

Удачи!

6
ответ дан 5 December 2019 в 22:16
поделиться
Другие вопросы по тегам:

Похожие вопросы: