Отображение наследования с помощью JPA / Hibernate - PullRequest
8 голосов
/ 13 июля 2010

Это довольно длинный (не слишком сложный) вопрос дизайна, поэтому, пожалуйста, потерпите меня.Я пытаюсь внедрить систему управления людьми / ролями с 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);
    }
    .
    .
    .
}

На этой диаграмме показаны отношения классов:

image

(Если вы не видите диаграмму в строке, посмотрите ее здесь через 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) Есть ли лучшая альтернатива этому проекту?

Буду очень признателен за любые идеи / предложения.

1 Ответ

6 голосов
/ 14 июля 2010

Как сказал

Так что, пожалуйста, потерпите меня

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

Все роли жестко запрограммированы ... Существует абстрактный класс Роль ...

Если существует класс 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, имейте в виду, что он не поддерживает столбец дискриминатора. Смотрите здесь
  • Простой запрос ОБЪЕДИНЯЕТ ВСЕ все таблицы, определенные для каждого подкласса. Может быть, вы просто хотите получить только один подкласс, и вы увидите проблемы с производительностью. Здесь идет обходной путь.

InheritanceType.SINGLE_TABLE

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

Теперь посмотрим

Есть человек с такими методами управления ролями, как 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 позволяет вам реализовать собственное поведение независимо от того, используется 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}"/>

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

Удачи!

...