对子项有限制的JPA层次结构查询

时间:2018-10-08 11:47:57

标签: java jpa one-to-many

我正在尝试为客户设计功能等级。

每个客户都有一个角色。 每个客户可以使用多个应用程序,但一次只能使用一个。

要评级的类别具有层次结构,客户应该对它的每个节点进行评级。

有问题的要求是,在不同的应用程序中,相同的客户应该看到不同的类别等级进行评分。

所以我在数据库中具有以下结构来表示需求:

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中保留层次结构的这种关系?

或者可能存在实现需求的另一种解决方案?

0 个答案:

没有答案