【发布时间】:2020-01-09 21:22:10
【问题描述】:
我有两个实体:
使用聚合器映射为 OneToOne 的影响详细信息
/**
* @author MalkeithSingh on 27-08-2019
*/
@Entity
@Table(name = "INFLUENCE_DETAILS")
@Builder
@NoArgsConstructor
@AllArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
@JsonNaming(PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy.class)
public class InfluenceDetails {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "influence_summary_id", length = 64)
private Long influenceSummaryId;
@OneToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "aggregator_id", nullable = false, referencedColumnName = "agg_id",
updatable = false, insertable = false)
private QualityAggregator aggregators;
@Column(name = "rule_id", length = 64)
private String ruleId;
@Column(name = "rule_desc", nullable = false)
private String ruleDesc;
@Column(name = "value")
private String value;
实体 2:
和质量聚合器。一个质量聚合器可以有另一个聚合器作为父级,它可以再次有一个父级,依此类推。
因此在同一个表中存在子父关系。
@Entity
@Table(name = "QUALITY_AGGREGATOR")
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@JsonNaming(PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy.class)
@Builder
@JsonInclude(JsonInclude.Include.NON_NULL)
public class QualityAggregator {
@Id
@Column(name = "agg_id")
private String aggId;
@Column(name = "project_id")
private String projectId;
@Column(name = "job_id")
private String jobId;
@Column(name = "job_instance_id")
private String jobInstanceId;
@Column(name = "created_at")
private Date timestamp;
@Column(name = "agg_key")
private String aggKey;
@Column(name = "agg_value")
private String aggValue;
@Column(name = "level")
private Integer level;
@Column(name = "parent_agg_id")
private String parentAggId;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "parent_agg_id", referencedColumnName = "agg_id",insertable = false,updatable = false)
private QualityAggregator parent;
@OneToMany(mappedBy = "parent",fetch = FetchType.EAGER)
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private Set<QualityAggregator> children;
注意:- 聚合器表中的最后一个子级映射到影响详细信息表。
现在我要做的是获取所有 InfluenceDetails,其中映射的子聚合器或任何父聚合器中的 agg_key 和 agg_value 都与提供的值匹配。
这是我目前所拥有的。我不知道聚合器可能有多少级别,所以我采用默认值 5 并使属性可配置。但必须有更好的方法。
public class InfluencerDetailsSpecification {
@Value("{no.of.aggregators}")
private Integer aggregators;
@PostConstruct
public void init(){
setNoOfAggregators(aggregators);
}
private static Integer noOfAggregators = 5;
/**
* Creates a query for searching in aggregators and all its successive parents upto the no of levels specified(Defaults to 5)
* @param request list of AggregatorsRequest to be searched
* @return The prepared specification
*/
public static Specification<InfluenceDetails> findByAggregators(List<AggregatorsRequest> request) {
return (root, query, cb) -> {
Join<InfluenceDetails, QualityAggregator> lastChild = root.join("aggregators");
Function<AggregatorsRequest, Predicate> toPredicate = agg -> {
List<Predicate> listOfOrPredicates = new ArrayList<>();
Join<QualityAggregator, QualityAggregator> parent = lastChild.join("parent",JoinType.LEFT);
Predicate p0 = cb.and(cb.equal(lastChild.get("aggKey"), agg.getAggKey()), cb.equal(lastChild.get("aggValue"), agg.getAggValue()));
listOfOrPredicates.add(p0);
Integer aggsNos = Integer.valueOf(noOfAggregators);
while(aggsNos-- > 2){
listOfOrPredicates.add(cb.and(cb.equal(parent.get("aggKey"), agg.getAggKey()), cb.equal(parent.get("aggValue"), agg.getAggValue())));
parent = parent.join("parent",JoinType.LEFT);
}
return cb.or(listOfOrPredicates.toArray(new Predicate[listOfOrPredicates.size()]));
};
return cb.and(cb.or(request.stream().map(toPredicate).toArray(Predicate[]::new)));
};
}
public static Integer getNoOfAggregators() {
return noOfAggregators;
}
public static void setNoOfAggregators(Integer noOfAggregators) {
if(Objects.isNull(noOfAggregators))
noOfAggregators = 5;
InfluencerDetailsSpecification.noOfAggregators = noOfAggregators;
}
}
【问题讨论】:
-
没有办法使用 Criteria API 有效地做到这一点。您需要改用 RDBMS 提供的分层查询功能(例如
WITH RECURSIVE或CONNECT BY) -
是的,这是选项。我可以使用聚合器表中提供的值递归查询以查找叶节点,然后使用 InfluenceDetails 表中的 aggId 进行搜索。但这将涉及两个不同的步骤。这就是为什么我一直在寻找一种方法来一次性使用标准。如果没有其他选择,那么我会这样做。
标签: java hibernate spring-boot jpa criteria-api