I have two tables that looks like:
Table_0
ID Description
5 description5
6 description6
7 description7
8 description8
Table_1
ID Table_0_ID Status
1 5 LOADED
2 6 LOADED
3 7 LOADED
4 7 LOADED
Table_2
ID Table_1_ID
1 1
2 2
3 3
4 4
my expected result when I invoke findAll query is that it eliminates duplicates so result will return (for both tables) three rows (table_1 id from 1 to 3).
I wrote both named query and criteria builder query but the last one seems to be by 4 times faster. I wonder why. Am I making a mistake?
Here is the code:
query = "SELECT OBJECT(sb) FROM Table_2 sb WHERE sb.Table_1.id IN (SELECT MAX(maxsr.id) FROM Table_1 maxsr WHERE maxsr.status = LOADED GROUP BY maxsr.Table_0.id)")
criteria builder:
final EntityManager em = getEntityManager();
final CriteriaBuilder cb = em.getCriteriaBuilder();
final CriteriaQuery<Table_2> criteriaQuery = cb.createQuery(Table_2.class);
final Root<Table_2> root = criteriaQuery.from(Table_2.class);
Predicate p = getPredicateOnList(data, cb, root, gateSession);
if (p != null) {
criteriaQuery.where(p);
}
final Query q = getEntityManager().createQuery(criteriaQuery);
return q.getResultList();
}
method getPredicateOnList
private Predicate getPredicateOnList(final PaginationData data, final CriteriaBuilder cb, final Root<Table_2> root) {
final Join<Table_2, Table_1> readingJoin = root.join("Table_1");
boolean filterUnloaded = false;
boolean selectMax = true;
for (KeyValuePair pair : data.getRequestParams()) {
if (pair.getKey().equalsIgnoreCase("filterUnloaded")) {
filterUnloaded = ParsingUtils.parseBoolean(pair.getValue(), false);
}
if (pair.getKey().equalsIgnoreCase("selectMax")) {
selectMax = ParsingUtils.parseBoolean(pair.getValue(), true);
}
}
Predicate predicate = null;
if (selectMax) {
List<Long> maxReadingIds = getMaxReadingIds(gateSession.getId(), filterUnloaded);
if (maxReadingIds == null || maxReadingIds.isEmpty()) {
//do nothing
} else {
predicate = readingJoin.get("id").in(maxReadingIds);
}
}
return predicate;
}
method getMaxReadingIds
private List<Long> getMaxReadingIds(Long sessionId, boolean filterUnloaded) {
final CriteriaBuilder cb = em.getCriteriaBuilder();
final CriteriaQuery<Long> maxReadingIdQuery = cb.createQuery(Long.class);
final Root<Table_1> Table_1Root = maxReadingIdQuery.from(Table_1.class);
final Path<Long> idGet = Table_1Root.get("id");
maxReadingIdQuery.select(cb.greatest(idGet));
final Join<Table_1, Table_0> join = Table_1Root.join("Table_0");
maxReadingIdQuery.groupBy(join.get("id"));
Predicate predicate = null;
if (filterUnloaded) {
predicate = cb.equal(Table_1Root.get("status"), LOADED);
}
//omiss sessionId parameter
if (predicate != null) {
maxReadingIdQuery.where(predicate);
}
final Query q = getEntityManager().createQuery(maxReadingIdQuery);
return q.getResultList();
}
(I made some semplification, if code is not well defined please tell me and I supply more information)
Both results are correct but criteria builder is faster.
Related
I have a parent Entity called CustomerDetailsEntity. It has following one to many mapping to another Entity IBPSDiscrepancyFieldsEntity.
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "Customer_discrepancy_fk", referencedColumnName = "customer_detail_id")
private Set<IBPSDiscrepancyFieldsEntity> ibpsDiscrepancyFieldsEntity;
Now, say for id=123, in IBPSDiscrepancyFieldsEntity Table, I have 4 rows of data.
Now when I hit criteria search on CustomerDetailsEntity I am getting 4 results with same data instead of one. This is how my search criteria is written:
public Map<String, Object> search(Long slrId, String param, String ibpsStatus, Pageable pageable) {
Criteria criteria = getSession().createCriteria(CustomerDetailsEntity.class);
criteria.add(Restrictions.eq("slrDtlId.sellerDtlId", slrId));
criteria.addOrder(Order.desc("modifiedOn"));
if (param != null && !param.isEmpty()) {
Criterion fName = Restrictions.like("customerFirstName", param+"%");
Criterion lName = Restrictions.like("customerLastName", param+"%");
Criterion appNo = Restrictions.like("loanAppNumber", "%"+param+"%");
Disjunction orExp = Restrictions.or(fName, lName, appNo);
criteria.add(orExp);
}
if (ibpsStatus != null && !ibpsStatus.isEmpty()) {
if (ibpsStatus.equalsIgnoreCase(Constant.IBPS_DISCREPANT)) {
criteria.add(Restrictions.eq("ibpsStatus", IBPSStatus.IBPS_DISCREPANT));
} else if (ibpsStatus.equalsIgnoreCase(Constant.IBPS_RESOLVED)) {
criteria.add(Restrictions.eq("ibpsStatus", IBPSStatus.IBPS_RESOLVED));
}
} else {
Criterion discrepant = Restrictions.eq("ibpsStatus", IBPSStatus.IBPS_DISCREPANT);
Criterion resolved = Restrictions.eq("ibpsStatus", IBPSStatus.IBPS_RESOLVED);
LogicalExpression orExp = Restrictions.or(discrepant, resolved);
criteria.add(orExp);
}
criteria.setFirstResult(pageable.getPageSize() * pageable.getPageNumber());
criteria.setMaxResults(pageable.getPageSize());
List<CustomerDetailsEntity> result = (List<CustomerDetailsEntity>) criteria.list();
Map<String,Object> countResultMap = new HashMap<>(2);
countResultMap.put(Constant.QUERY_RESULT, result);
logger.info("##### checking here");
logger.info("--->"+result.size());
criteria.setFirstResult(0);
Long count = (Long) criteria.setProjection(Projections.rowCount()).uniqueResult();
logger.info("Total Count : "+count);
countResultMap.put(Constant.TOTAL_COUNT, count);
return countResultMap;
}
How do I get only 1 result of CustomerDetailsEntity id=123 when I search, instead of 4 duplicate values. In the logs, result.size() shows as 4. Is the Issue with my mapping or criteria method? Please help.
I have a function that returns a list of templates(unsorted); can someone tell me how I can get the list in a sorted format?
public List<FormTemplate> listDomainTemplates(Integer id) {
Domain domain = domainService.getDomain(id);
if (domain == null) {
return new ArrayList<>();
}
CriteriaBuilder cb = sessionFactory.getCurrentSession().getCriteriaBuilder();
CriteriaQuery<FormTemplate> query = cb.createQuery(FormTemplate.class);
Root<FormTemplate> application = query.from(FormTemplate.class);
query.select(application);
Predicate predicate = cb.equal(application.get("domain"), domain);
query.where(predicate);
Query<FormTemplate> q = sessionFactory.getCurrentSession().createQuery(query);
return q.getResultList();
}
Please experiment with following:
query.orderBy(cb.asc(application.get(...));
... - should point to FormTemplate field to sort, I suppose.
I have to make a query in criteria api of two tables that is to say I want to select in the query all the fields of the two tables.
Here I leave the models that I want to select (I have the metamodels of each of them) and I give you an example of how to select the data
of each of them in different consultations
public List<Empleado> empleadoB() {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Empleado> criteria = cb.createQuery(Empleado.class);
Root<Empleado> member = criteria.from(Empleado.class);
criteria.select(member).orderBy(cb.asc(member.get(Empleado_.p_ap)));
return em.createQuery(criteria).getResultList();
}
public List<Empleado2> empleadosNom() {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Empleado2> criteria = cb.createQuery(Empleado2.class);
Root<Empleado> member = criteria.from(Empleado.class);
Root<Tipo_docs> tDoc = criteria.from(Tipo_docs.class);
Root<Estad_civil> eCivil = criteria.from(Estad_civil.class);
Root<Profesiones> prof = criteria.from(Profesiones.class);
Root<Eps> eps = criteria.from(Eps.class);
Root<Pensiones> pens = criteria.from(Pensiones.class);
Root<Usuario> useri = criteria.from(Usuario.class);
Root<Cargos> cargo = criteria.from(Cargos.class);
criteria.select(cb.construct(Empleado2.class, member.get(Empleado_.id_emp),
member.get(Empleado_.p_ap), member.get(Empleado_.s_ap),
member.get(Empleado_.nombre), member.get(Empleado_.tipo_doc),
member.get(Empleado_.numero_doc), member.get(Empleado_.lugar_exp),
member.get(Empleado_.fecha_nac), member.get(Empleado_.lugar_nac),
member.get(Empleado_.estadocivil), member.get(Empleado_.direc),
member.get(Empleado_.tel), member.get(Empleado_.profesion),
cb.selectCase() .when(cb.isNull(member.get(Empleado_.matricula_prof)), "No Registrado") .otherwise(member.get(Empleado_.matricula_prof)),
member.get(Empleado_.mail),member.get(Empleado_.eps),
cb.selectCase() .when(cb.isNull(member.get(Empleado_.pensiones)), (long)0) .otherwise(member.get(Empleado_.pensiones)),
cb.selectCase() .when(cb.isNull(member.get(Empleado_.usu)), "No Existe") .otherwise(member.get(Empleado_.usu)),
tDoc.get(Tipo_docs_.desc_tdoc),
eCivil.get(Estad_civil_.desc_ecivil), prof.get(Profesiones_.nombre_prof),
eps.get(Eps_.nom_eps),
cb.selectCase() .when(cb.isNull(member.get(Empleado_.pensiones)), "No Aplica") .otherwise(pens.get(Pensiones_.nom_pension)),
cargo.get(Cargos_.id_cargo), cargo.get(Cargos_.desc_cargo) ));
criteria.where(cb.and(cb.equal(member.get(Empleado_.tipo_doc), tDoc.get(Tipo_docs_.id_entidad)),
cb.equal(member.get(Empleado_.estadocivil), eCivil.get(Estad_civil_.id_ecivil)),
cb.equal(member.get(Empleado_.profesion), prof.get(Profesiones_.id_prof)),
cb.equal(member.get(Empleado_.eps), eps.get(Eps_.id_eps)),
cb.or(cb.equal(member.get(Empleado_.usu), useri.get(Usuario_.usuario)), cb.isNull(member.get(Empleado_.usu))),
cb.or(cb.equal(member.get(Empleado_.pensiones), pens.get(Pensiones_.id_pension)), cb.isNull(member.get(Empleado_.pensiones))) ));
criteria.distinct(true);
return em.createQuery(criteria).getResultList();
}
I have a java class with a enum field,
org.example.Importacion {
...
#Enumerated(EnumType.STRING)
private EstadoImportacion estadoImportacion;
public static enum EstadoImportacion {
NOT_VALID, IMPORTED, ERROR, VALID
}
}
When I create a Query with CriteriaBuilder and I try to compare the enum values, one from a filter to the criteriabuilder using literals, the final result of the query does not filter the enum values, so if I send org.example.Importacion.EstadoImportacion.ERROR to the iterator method, the rersult will not filter ERROR on the filnal result list.
The companyCod filters ok, so If I send "COMPANY001" as a companyCode, the querybuilder filters the final result.
I would like to know how to compare enums in the query:
public Iterator<Importacion> iterator (
long first,
long count,
String companyCod,
org.example.Importacion.EstadoImportacion estado) {
CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
CriteriaQuery<Importacion> criteria = cb.createQuery(Importacion.class);
Root<Importacion> desembolso = criteria.from(Importacion.class);
criteria.select(desembolso);
Predicate p = cb.conjunction();
if(companyCod != null) {
p = cb.and(p, cb.equal(desembolso.get("codigo"), companyCod));
//This part works fine!
}
if (estado != null) {
Expression<org.example.Importacion.EstadoImportacion> estadoImportacion = null;
if (estado.equals(org.example.Importacion.EstadoImportacion.ERROR)) {
estadoImportacion = cb.literal(org.example.Importacion.EstadoImportacion.ERROR);
}
if (estado.equals(org.example.Importacion.EstadoImportacion.IMPORTED)) {
estadoImportacion = cb.literal(org.example.Importacion.EstadoImportacion.IMPORTED);
}
if (estado.equals(org.example.Importacion.EstadoImportacion.NOT_VALID)) {
estadoImportacion = cb.literal(org.example.Importacion.EstadoImportacion.NOT_VALID);
}
if (estado.equals(org.example.Importacion.EstadoImportacion.VALID)) {
estadoImportacion = cb.literal(org.example.Importacion.EstadoImportacion.VALID);
}
p = cb.and(p, cb.equal(estadoImportacion, cb.literal(estado)));
//Doesn't seems to compare enum values
}
criteria.where(p);
javax.persistence.Query query = em.createQuery(criteria);
query.setMaxResults((int)count + (int)first + 1);
query.setFirstResult((int)first);
List resultList = query.getResultList();
Iterator iterator = (Iterator) resultList.iterator();
LOGGER.info("desembolso size: {}", resultList.size());
return iterator;
}
Your criteria compares a literal with the enum. That's not what you want. You want to compare the Importacion's estadoImportacion with the given estado:
Predicate p = cb.conjunction();
if(companyCod != null) {
p = cb.and(p, cb.equal(desembolso.get("codigo"), companyCod));
}
if (estado != null) {
p = cb.and(p, cb.equal(desembolso.get("estadoImportacion"), estado));
}
i want add some more complicated selections as below on date coloumn
select TO_DATE ('04-JAN-2015','DD-MM-YYYY'), to_char(TO_DATE ('04-JAN-2015','DD-MM-YYYY'), 'FMDAY') day,
(case when to_char(trunc(TO_DATE ('04-JAN-2015','DD-MM-YYYY'), 'mm'), 'FMDAY') = 'SUNDAY' then to_number(to_char(TO_DATE ('04-JAN-2015','DD-MM-YYYY'), 'W'))
else ceil((to_char(TO_DATE ('04-JAN-2015','DD-MM-YYYY'), 'dd') + 1 - to_char(next_day(trunc(TO_DATE ('04-JAN-2015','DD-MM-YYYY'), 'mm'), 'SUNDAY'), 'dd'))/7)
end)+1 week_no
from dual
here breakupFields is list of string which leads to date column
final QueryBuilder distinctDateTimeFilter = new QueryBuilder() {
final List<String> breakupFields = new ArrayList<String>(fields.length);
for (final String f : fields)
if (!Strings.isEmpty(f)) {
breakupFields.add(f);
}
}
final QueryBuilder distinctDateTimeFilter = new QueryBuilder() {
#Override
public CriteriaQuery buildQuery(CriteriaBuilder cb, CriteriaQuery query, Predicate p, List orders, Root rt) {
Expression selection = null;
selection = cb.function("TO_CHAR", String.class, cb.sum(CriteriaQueryUtils.getPath(rt, breakupFields), cb.literal(miliSecToAdd)),
cb.literal("W-MM-YYYY"));
return query.select(selection).distinct(true).where(p).orderBy(orders);
}
}
final List<Object> objs = new ArrayList<Object>(ds.executeQuery(classObject, Object.class, distinctDateTimeFilter, ef, session,
SuperUserSecurityContext.class));
Can any one suggest how to add
(case when to_char(trunc(TO_DATE ('04-JAN-2015','DD-MM-YYYY'), 'mm'), 'FMDAY') = 'SUNDAY' then to_number(to_char(TO_DATE ('04-JAN-2015','DD-MM-YYYY'), 'W'))
else ceil((to_char(TO_DATE ('04-JAN-2015','DD-MM-YYYY'), 'dd') + 1 - to_char(next_day(trunc(TO_DATE ('04-JAN-2015','DD-MM-YYYY'), 'mm'), 'SUNDAY'), 'dd'))/7)
end)+1 to criteria builder
You can introduce a column in your Entity marked as
#Formula(value="the expression")
See more here and use the column in the queries.
Or you can define native SQL query with the desired entity class mapped and use fuly native SQL.