JPA иерархический запрос с ограничением на детей - PullRequest
0 голосов
/ 08 октября 2018

Я пытаюсь создать функциональность рейтинга для клиентов.

Каждый клиент имеет одну роль.Каждый клиент может использовать несколько приложений, но по одному за раз.

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

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

Поэтому у меня есть следующая структура в БД для представления требований:

CREATE TABLE T_APPLICATION (
  ID   NUMBER(19, 0) NOT NULL,
  NAME VARCHAR2(64) NOT NULL,

  PRIMARY KEY (ID)
);

CREATE TABLE T_ROLE (
  ID   NUMBER(19, 0) NOT NULL,
  NAME VARCHAR2(64) NOT NULL,

  PRIMARY KEY (ID)
);

CREATE TABLE T_RATING_CATEGORY (
    ID        NUMBER(19, 0) NOT NULL,
    NAME      VARCHAR2(128) NOT NULL,
    PARENT_ID NUMBER(19, 0),

    PRIMARY KEY (ID),
    FOREIGN KEY (PARENT_ID) REFERENCES T_RATING_CATEGORY (ID) ON DELETE CASCADE
);

CREATE TABLE T_RATING_CATEGORY_2_APP_ROLE (
    CATEGORY_ID    NUMBER(19, 0) NOT NULL,
    ROLE_ID        NUMBER(19, 0) NOT NULL,
    APPLICATION_ID NUMBER(19, 0) NOT NULL,

    FOREIGN KEY (CATEGORY_ID) REFERENCES T_RATING_CATEGORY (ID) ON DELETE CASCADE,
    FOREIGN KEY (ROLE_ID) REFERENCES T_ROLE (ID) ON DELETE CASCADE,
    FOREIGN KEY (APPLICATION_ID) REFERENCES T_APPLICATION (ID) ON DELETE CASCADE,

    PRIMARY KEY (CATEGORY_ID, ROLE_ID, APPLICATION_ID)
);

Запрос Oracle SQL для получения иерархии категорий для оценки с ограничением по Приложениюи роль выглядит следующим образом:

SELECT c.*, t.* FROM T_RATING_CATEGORY c
JOIN T_RATING_CATEGORY_2_APP_ROLE t on 
    c.id = t.CATEGORY_ID AND
    t.ROLE_ID = (SELECT ID FROM T_ROLE where name = 'SOME_ROLE') AND 
    t.APPLICATION_ID = (SELECT ID FROM T_APPLICATION where name = 'SOME_APPLICATION')
START WITH c.parent_id IS NULL    
CONNECT BY PRIOR c.id = c.parent_id;

Но я не понимаю, как отобразить результат в POJO с сохранением иерархии.

Я знаю, что в JPA нет абстракции для отношения иерархии, но янашел обходной путь для этого (1):

@ManyToOne(fetch = EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private RatingCategory parent;

@OneToMany(mappedBy = "parent")
private Set<RatingCategory> categories;

И это представляет связь с таблицей связей (2):

@ElementCollection
@CollectionTable(
    name="T_RATING_CATEGORY_2_APP_ROLE",
    joinColumns=@JoinColumn(name="CATEGORY_ID")
)
private Set<RatingCategoryToAppRole> apps2roles;

Теперь благодаря (1) я могу получить полную иерархию безограничения для приложения и роли.

public List<RatingCategory> getAll(Application app, Role role) {
    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<RatingCategory> query = cb.createQuery(RatingCategory.class);
    Root<RatingCategory> categories = query.from(RatingCategory.class);
    query.select(categories).where(
            cb.isNull(categories.get(RatingCategory_.parent))
        )
    );
    return Optional.ofNullable(em.createQuery(query).getResultList()).orElse(Collections.emptyList());

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

Как я понимаю, если я добавлю ограничение следующим образом:

public List<RatingCategory> getAll(Application app, Role role) {
    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<RatingCategory> query = cb.createQuery(RatingCategory.class);
    Root<RatingCategory> categories = query.from(RatingCategory.class);
    Join<RatingCategory, RatingCategoryToAppRole> apps2Roles = categories.join(RatingCategory_.apps2roles);
    categories.fetch(RatingCategory_.apps2roles);
    query.select(categories).where(
        cb.and(
            cb.isNull(categories.get(RatingCategory_.parent)),
            cb.equal(apps2Roles.get(RatingCategoryToAppRole_.application), app),
            cb.equal(apps2Roles.get(RatingCategoryToAppRole_.role), role)
        )
    );
    return Optional.ofNullable(em.createQuery(query).getResultList()).orElse(Collections.emptyList());
}

, которое отфильтрует только верхний уровень иерархии и добавит избыточное объединение.

Есть ли решение для JPA (EclipseLink) реализовать такие отношения с сохранением иерархии в POJO?

Или, может быть, может быть другое решение для реализации требований?

...