我正在尝试为客户设计功能等级。
每个客户都有一个角色。 每个客户可以使用多个应用程序,但一次只能使用一个。
要评级的类别具有层次结构,客户应该对它的每个节点进行评级。
有问题的要求是,在不同的应用程序中,相同的客户应该看到不同的类别等级进行评分。
所以我在数据库中具有以下结构来表示需求:
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中保留层次结构的这种关系?
或者可能存在实现需求的另一种解决方案?