Tenho o seguinte método da API de critérios JPA:
public List<AggregationTask> findNonExpiredTasksBy(String entityName, Map<String, String> columnValues) {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<AggregationTask> query = cb.createQuery(AggregationTask.class);
Root<AggregationTask> root = query.from(AggregationTask.class);
Predicate entityNamePredicate = cb.equal(root.get("edaEntityName"), entityName);
Predicate columnPredicate = cb.conjunction();
if (columnValues != null && !columnValues.isEmpty()) {
List<Predicate> columnConditions = new ArrayList<>();
for (Map.Entry<String, String> entry : columnValues.entrySet()) {
Predicate condition = cb.and(
cb.equal(root.get("edaEntityColumnName"), entry.getKey()),
cb.equal(root.get("edaEntityColumnValue"), entry.getValue())
);
columnConditions.add(condition);
}
columnPredicate = cb.or(columnConditions.toArray(new Predicate[0]));
}
Predicate expiresAtPredicate = cb.greaterThan(root.get("expiresAt"), cb.currentTimestamp());
query.where(cb.and(entityNamePredicate, columnPredicate, expiresAtPredicate));
return entityManager.createQuery(query).getResultList();
}
O que produz a seguinte consulta:
select
*
from
aggregation_tasks at1_0
where
at1_0.eda_entity_name =?
and
(
at1_0.eda_entity_column_name =?
and at1_0.eda_entity_column_value =?
or at1_0.eda_entity_column_name =?
and at1_0.eda_entity_column_value =?
or at1_0.eda_entity_column_name =?
and at1_0.eda_entity_column_value =?
or at1_0.eda_entity_column_name =?
and at1_0.eda_entity_column_value =?
or at1_0.eda_entity_column_name =?
and at1_0.eda_entity_column_value =?
or at1_0.eda_entity_column_name =?
and at1_0.eda_entity_column_value =?
)
and at1_0.expires_at > localtimestamp
O problema é que preciso agrupar os seguintes pares de condições entre parênteses usando OR
:
and
(
(at1_0.eda_entity_column_name =? and at1_0.eda_entity_column_value =?)
or
(at1_0.eda_entity_column_name =? and at1_0.eda_entity_column_value =?)
or
(at1_0.eda_entity_column_name =? and at1_0.eda_entity_column_value =?)
or
(at1_0.eda_entity_column_name =? and at1_0.eda_entity_column_value =?)
or
(at1_0.eda_entity_column_name =? and at1_0.eda_entity_column_value =?)
or
(at1_0.eda_entity_column_name =? and at1_0.eda_entity_column_value =?)
)
O que estou fazendo errado no meu código e como posso fazer isso?
ATUALIZADO
Reimplementei o método com JPQA Query baseado em concat de strings:
public List<AggregationTask> findNonExpiredTasksBy(String entityName, Map<String, String> columnValues) {
StringBuilder sql = new StringBuilder("SELECT * FROM aggregation_tasks WHERE eda_entity_name = :entityName ");
sql.append("AND expires_at > CURRENT_TIMESTAMP ");
if (MapUtils.isNotEmpty(columnValues)) {
sql.append("AND (");
int count = 0;
for (Map.Entry<String, String> entry : columnValues.entrySet()) {
if (count > 0) {
sql.append(" OR ");
}
sql.append("(")
.append("eda_entity_column_name = :columnName" + count)
.append(" AND eda_entity_column_value = :columnValue" + count)
.append(")");
count++;
}
sql.append(") ");
}
Query query = entityManager.createNativeQuery(sql.toString(), AggregationTask.class);
query.setParameter("entityName", entityName);
if (columnValues != null && !columnValues.isEmpty()) {
int count = 0;
for (Map.Entry<String, String> entry : columnValues.entrySet()) {
query.setParameter("columnName" + count, entry.getKey());
query.setParameter("columnValue" + count, entry.getValue());
count++;
}
}
return query.getResultList();
}
e a consulta funciona bem. Então ainda não entendi o que estou fazendo de errado com a versão dessa lógica na API de Critérios JPA...