Spring Predicate JpaSpecificationExecutor IN (Select) Expression - java

I have the following Query which returns me one row:
SELECT * FROM activity_entity
WHERE activity_id IN (SELECT activity_id FROM category_entity WHERE activity_type = 9999)
AND activity_id = 'AA924EDC-7D55-2555-6569-7D54BDF4F71F'
AND category_id = '45CF9A18-1718-482A-A1C6-CA77E23D29B1'
AND internal_module_id = '311';
And the following Spring Predicate:
public List<ActivityEntity> getRepositoryDataPrincipalActivity(AccessRepVisitLogInDto inDto,AccessRepEntity accessRep) {
List<ActivityEntity> activityL = activityRepository.findAll(new Specification<ActivityEntity>() {
#Override
public Predicate toPredicate(Root<ActivityEntity> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
List<Predicate> predicates = new ArrayList<>();
predicates.add(cb.equal(root.get("activityId"), accessRep.getActivityId()));
predicates.add(cb.equal(root.get("categoryId"), inDto.getCategory_id()));
predicates.add(cb.equal(root.get("internalModuleId"), inDto.getInternalModuleId()));
return cb.and(predicates.toArray(new Predicate[0]));
}
});
return activityL;
}
On my predicate I want to include the IN (SELECT) statement:
activity_id IN (SELECT activity_id FROM category_entity WHERE activity_type = 9999)
It is a validation that I need to add to my predicate so that it returns correctly the row that I need.

Subquery<Long> subquery = query.subquery(Long.class);
Root<EntityCategory> category = subquery.from(EntityCategory.class);
Predicate subPredicate = cb.equal(category.get("activityType"), 9999);
subquery.select(category.get("activityId")).where(subPredicate);
predicates.add(root.get("id").in(subquery));

Related

How to handle pagination in JPA (Criteria and Predicates)

I'm fetching the results from DB using criteria and predicates and I got my result list , and I'm trying to apply pagination and sorting but it's not working. Please help me where I'm missing, Here is my code:
private Page<Books> getFiltereBooks(Params params,
PageRequest sortOrder) {
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Books> criteria = builder.createQuery(Books.class);
Root<Books> booksRoot = criteria.from(Books.class);
List<Predicate> predicates = new ArrayList<Predicate>();
predicates.add(builder.equal(booksRoot.get("id"), params.getRequestId()));
predicates.add(builder.like(builder.lower(booksRoot.get("name")),
"%" + params.getName().toLowerCase() + "%"));
criteria.where(builder.and(predicates.toArray( new Predicate[predicates.size()])));
criteria.orderBy(builder.desc(booksRoot.get("id")));
List<Books> result = em.createQuery(criteria).getResultList();
int total = result.size();
Page<Books> result1 = new PageImpl<>(result, sortOrder, total);
return result1;
}
when I use this code :
Page<Books> result1 = new PageImpl<>(result, sortOrder, total);
it's not working, I want to return a Page Object. any help is appreciated.
You can try this
private Page<Books> getFiltereBooks(Params params,
Pageable pageable) {
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Books> criteria = builder.createQuery(Books.class);
Root<Books> booksRoot = criteria.from(Books.class);
List<Predicate> predicates = new ArrayList<Predicate>();
predicates.add(builder.equal(booksRoot.get("id"), params.getRequestId()));
predicates.add(builder.like(builder.lower(booksRoot.get("name")),
"%" + params.getName().toLowerCase() + "%"));
criteria.where(builder.and(predicates.toArray( new Predicate[predicates.size()])));
criteria.orderBy(builder.desc(booksRoot.get("id")));
// This query fetches the Books as per the Page Limit
List<Books> result = em.createQuery(criteria).setFirstResult((int) pageable.getOffset()).setMaxResults(pageable.getPageSize()).getResultList();
// Create Count Query
CriteriaQuery<Long> countQuery = builder.createQuery(Long.class);
Root<Books> booksRootCount = countQuery.from(Books.class);
countQuery.select(builder.count(booksRootCount)).where(builder.and(predicates.toArray(new Predicate[predicates.size()])));
// Fetches the count of all Books as per given criteria
Long count = em.createQuery(countQuery).getSingleResult();
Page<Books> result1 = new PageImpl<>(result, pageable, count);
return result1;
}
Predicate[] getPredicate(Root<Entity> root, CriteriaBuilder criteriaBuilder, filter params) {
Predicate[] predicatesArr = null;
List<Predicate> predicates = new ArrayList<>();
// fill your predicates list here
predicatesArr = predicates.toArray(new Predicate[predicates.size()]);
return predicatesArr;
}
You need to add a method for creating predicates for each query, Note here you are using the same predicates list "which drived from different query" for both queries [pagination and count]
Root<Books> booksRoot = criteria.from(Books.class);
predicates.add(builder.equal(booksRoot.get("id"), params.getRequestId()));

How to get selected fields from table using hibernate (I am not using Criteria object but CriteriaQuery object so setProjection solution won't work)

I want to get selected fields from table with filters on the top of it.
For example :
If I want to filter my results on the basis of an attribute in the table named "created_on", I would do something like http://example.com/users/?created_on = 01-12-2016
I am achieving above task by doing this :
public List<User> searchUser(List<SearchCriteria> params
, int offset, int limit) {
Session session = mysqlConnectionFactory.getSession();
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery<User> query = builder.createQuery(User.class);
Root r = query.from(User.class);
Predicate predicate = builder.conjunction();
if (params != null) {
for (SearchCriteria param : params) {
if (param.getOperation().equalsIgnoreCase(">")) {
predicate = builder.and(predicate,
builder.greaterThanOrEqualTo(r.get(param.getKey()),
param.getValue().toString()));
} else if (param.getOperation().equalsIgnoreCase("<")) {
predicate = builder.and(predicate,
builder.lessThanOrEqualTo(r.get(param.getKey()),
param.getValue().toString()));
} else if (param.getOperation().equalsIgnoreCase(":")) {
if (r.get(param.getKey()).getJavaType() == String.class) {
predicate = builder.and(predicate,
builder.like(r.get(param.getKey()),
"%" + param.getValue() + "%"));
} else {
predicate = builder.and(predicate,
builder.equal(r.get(param.getKey()), param.getValue()));
}
}
}
query.where(predicate);
}
List<User> Users = session.createQuery(query)
.setFirstResult(offset).setMaxResults(limit).getResultList();
return Users;
}
Now I also wish to incorporate the functionality which will return selected fields for me, how can I do that?

Error in getting list from SQL view through JPA Creteria Query

I have created SQL view in Mysql database. I am writing web service to show list using JPA based on user search entry. Hibernate query is executing correctly in console, but list is not showing when checked in advaced rest client. Please check below for required code:
Entity Class:
#Entity
#Table(name = "tbi_datadiscovery_detail_view"/*, catalog = "timebound"*/)
public class TbiDDDetailView implements java.io.Serializable {
// Fields
#Id
#Column(name = "dd_metric_id")
#GeneratedValue(generator = "gen")
#GenericGenerator(name = "gen", strategy = "foreign", parameters = #Parameter(name = "property", value = "ddConfigId"))
private Integer ddConfigId;
#Column(name="dd_metric_name")
private String metricName;
#Column(name="dd_type")
private String ddType;
#Column(name="dd_name")
private String ddName;
#Column(name="discovery_id")
private Integer discoveryId;
#Column(name="key_words")
private String keywords;
#OneToOne
#PrimaryKeyJoinColumn
private TbiDDConfigMaster tbiDDConfigMaster;
//setters & getters
}
JPA Repository:
public interface TbiDDDetailViewDao extends CrudRepository<TbiDDDetailView, Integer>, JpaRepository<TbiDDDetailView, Integer> {
#Query("select v from TbiDDDetailView v,TbiDDConfigMaster t where v.ddConfigId = t.ddConfigId and t.domainId=?1 and v.metricName LIKE %?2% or t.keywords LIKE %?2%")
List<TbiDDDetailView> findByDomainIdAndMetricNameLike(Integer domainId,String metricName);
}
Service Implementation:
public TbiDDDetailViewListResponse getViewMatrics(List sortProperties, List sortTypes,
List operator, List value, List property, int page, int limit,
Integer domainId, String searchString) {
TbiDDDetailViewListResponse res = new TbiDDDetailViewListResponse();
List<TbiDDDetailView> tbiDDDetailViews = tbiDDDetailViewDao.findByDomainIdAndMetricNameLike(domainId, searchString);
CriteriaBuilder cb=em.getCriteriaBuilder();
CriteriaQuery<TbiDDDetailView> qry =cb.createQuery(TbiDDDetailView.class);
Root root = qry.from(TbiDDDetailView.class);
Root config = qry.from(TbiDDConfigMaster.class);
List creteriaList = new ArrayList<>();
Predicate predicate1 = cb.equal(root.get("tbiDDConfigMaster").get("ddConfigId"), config.get("ddConfigId"));
creteriaList.add(predicate1);
Predicate predicate2 = cb.equal(root.get("tbiDDConfigMaster").get("ddConfigId"), domainId);
creteriaList.add(predicate2);
Predicate predicate3 = cb.like(cb.upper(root.get("metricName")),searchString);
creteriaList.add(predicate3);
CriteriaQuery<TbiDDDetailView> criteriaQuery = qry.select(cb.construct(TbiDDDetailView.class, root.get("ddConfigId"),root.get("metricName"),root.get("ddType"),
root.get("ddName"),root.get("discoveryId")));
List<Order> orderList = new ArrayList<>();
orderList = getSort(cb,root,sortProperties, sortTypes, null);
qry.where(cb.and((Predicate[]) creteriaList.toArray(new Predicate[0])));
int start=0;
if(limit != 0)
start=(page-1)*limit;
TypedQuery<TbiDDDetailView> tq = em.createQuery(qry);
tbiDDDetailViews = tq.setFirstResult(start).setMaxResults(limit).getResultList();
TypedQuery<TbiDDDetailView> queryTotal = em.createQuery(criteriaQuery);
long totalRecords = (long) queryTotal.getResultList().size();
List<DDDetailViewResponse> details = new ArrayList<>();
if(tbiDDDetailViews!=null)
{
for(TbiDDDetailView t : tbiDDDetailViews){
DDDetailViewResponse detailView = new DDDetailViewResponse();
detailView.setDdMetricId(t.getDdConfigId());
detailView.setDdMetricName(t.getMetricName());
detailView.setDdType(t.getDdType());
detailView.setDdName(t.getDdName());
detailView.setDiscoveryId(t.getDiscoveryId());
details.add(detailView);
System.out.println("name-->"+t.getDdName()+"------type-------"+t.getDdType()+"-------------id--------------"+t.getDdConfigId()+"---------Metricname-----------"+t.getMetricName());
}
}
res.setRecords(details);
res.setPageNumber(page);
if(limit != 0) {
int Rem = (totalRecords%limit)>0?new Integer(1):0;
int total = (int) (totalRecords/limit + Rem);
res.setTotalPages(total);
log.info("TotalRecords :"+totalRecords + "Total Pages:" +total);
}
return res;
}
When I checked with debugging mode, execution is not going inside for loop only, its directly coming out of loop.
Hibernate query execution in console:
Hibernate: select tbidddetai0_.dd_metric_id as dd_metri1_34_, tbidddetai0_.dd_name as dd_name2_34_, tbidddetai0_.dd_type as dd_type3_34_, tbidddetai0_.discovery_id as discover4_34_, tbidddetai0_.key_words as key_word5_34_, tbidddetai0_.dd_metric_name as dd_metri6_34_ from tbi_datadiscovery_detail_view tbidddetai0_ cross join tbi_dd_config_master tbiddconfi1_ where tbidddetai0_.dd_metric_id=tbiddconfi1_.dd_metric_config_id and tbiddconfi1_.domain_id=? and (tbidddetai0_.dd_metric_name like ?) or tbiddconfi1_.keywords like ?
Hibernate: select tbiddconfi0_.dd_metric_config_id as dd_metri1_35_0_, tbiddconfi0_.created_by as created_2_35_0_, tbiddconfi0_.created_date as created_3_35_0_, tbiddconfi0_.domain_id as domain_i4_35_0_, tbiddconfi0_.is_active as is_activ5_35_0_, tbiddconfi0_.keywords as keywords6_35_0_, tbiddconfi0_.metric_name as metric_n7_35_0_, tbiddconfi0_.modified_by as modified8_35_0_, tbiddconfi0_.modified_date as modified9_35_0_ from tbi_dd_config_master tbiddconfi0_ where tbiddconfi0_.dd_metric_config_id=?
Hibernate: select tbidddetai0_.dd_metric_id as col_0_0_, tbidddetai0_.dd_metric_name as col_1_0_, tbidddetai0_.dd_type as col_2_0_, tbidddetai0_.dd_name as col_3_0_, tbidddetai0_.discovery_id as col_4_0_ from tbi_datadiscovery_detail_view tbidddetai0_ cross join tbi_dd_config_master tbiddconfi1_ where tbidddetai0_.dd_metric_id=tbiddconfi1_.dd_metric_config_id and tbidddetai0_.dd_metric_id=4926 and (upper(tbidddetai0_.dd_metric_name) like ?) limit ?
Hibernate: select tbidddetai0_.dd_metric_id as col_0_0_, tbidddetai0_.dd_metric_name as col_1_0_, tbidddetai0_.dd_type as col_2_0_, tbidddetai0_.dd_name as col_3_0_, tbidddetai0_.discovery_id as col_4_0_ from tbi_datadiscovery_detail_view tbidddetai0_ cross join tbi_dd_config_master tbiddconfi1_ where tbidddetai0_.dd_metric_id=tbiddconfi1_.dd_metric_config_id and tbidddetai0_.dd_metric_id=4926 and (upper(tbidddetai0_.dd_metric_name) like ?)
95953 [http-bio-9090-exec-3] INFO com.acinfotech.timebound.jpa.service.ReportJobsPersistenceServiceImpl - TotalRecords :0Total Pages:0
I have got it working by changing Creteria query with Predicate
Check below code that has worked for me:
CriteriaBuilder cb=em.getCriteriaBuilder();
CriteriaQuery<TbiDDDetailView> qry =cb.createQuery(TbiDDDetailView.class);
Root root = qry.from(TbiDDDetailView.class);
Root config = qry.from(TbiDDConfigMaster.class);
List creteriaList = new ArrayList<>();
Predicate predicate1 = cb.equal(root.get("tbiDDConfigMaster").get("ddConfigId"), config.get("ddConfigId"));
creteriaList.add(predicate1);
Predicate predicate2 = cb.equal(root.get("tbiDDConfigMaster").get("domainId"), domainId);
creteriaList.add(predicate2);
Predicate predicate3 = cb.or(cb.like(cb.upper(root.get("tbiDDConfigMaster").get("keywords")), "%"+searchString+"%"), cb.like(cb.upper(root.get("metricName")),"%"+searchString+"%"));
CriteriaQuery<TbiDDDetailView> criteriaQuery = qry.select(cb.construct(TbiDDDetailView.class, root.get("ddConfigId"),root.get("metricName"),root.get("ddType"),
root.get("ddName"),root.get("discoveryId")));
qry.where(cb.and((Predicate[]) creteriaList.toArray(new Predicate[0])), predicate3);
TypedQuery<TbiDDDetailView> tq = em.createQuery(qry);
return tq.getResultList();

Why does JPA Criteria Query not allow to correlate a subquery?

I need to match the main query with a subquery with the ID of category. JPA Criteria Query does not allow me to set the Predicate to the Category because the query is returning types PubThread.
Predicate correlatePredicate = criteriaBuilder.equal(rootPubThreadSub.get(PubThread_.id), rootPubThread);
Below the entire criteria query.
CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery<PubThread> cq = criteriaBuilder.createQuery(PubThread.class);
// 1) MainQuery
// Create the FROM
Root<PubThread> rootPubThread = cq.from(PubThread.class);
// Create the JOIN from the first select: join-chaining. You only need the return for ordering. e.g. cq.orderBy(cb.asc(categoryJoin.get(Pub_.title)));
Join<Pub, PubCategory> categoryJoin = rootPubThread.join(PubThread_.pups).join(Pub_.pubCategory);
// Create the WHERE
cq.where(criteriaBuilder.not(criteriaBuilder.equal(rootPubThread.get(PubThread_.id), threadId)));
// Create the SELECT, at last
cq.select(rootPubThread).distinct(true);
// 2) Subquery
Subquery<PubThread> subquery = cq.subquery(PubThread.class);
Root<PubThread> rootPubThreadSub = subquery.from(PubThread.class);
subquery.where(criteriaBuilder.equal(rootPubThread.get(PubThread_.id), threadId));
Join<Pub, PubCategory> categoryJoinSub = rootPubThreadSub.join(PubThread_.pups).join(Pub_.pubCategory);
subquery.select(rootPubThreadSub);
Predicate correlatePredicate = criteriaBuilder.equal(rootPubThreadSub.get(PubThread_.id), rootPubThread);
subquery.where(correlatePredicate);
cq.where(criteriaBuilder.exists(subquery));
How do you formulate this Criteria Query to match this SQL code? I think I'm pretty close.
SELECT Distinct(pt2.id), pt2.name
FROM pubthread pt2
JOIN pub_pubthread ppt2 ON pt2.id = ppt2.pubThreads_id
JOIN pub p2 ON ppt2.pups_id = p2.id
JOIN pubcategory pc2 ON p2.pubCategoryId = pc2.id
WHERE pt2.id != 1 and EXISTS (
SELECT DISTINCT(pt.id) FROM pubthread pt
JOIN pub_pubthread ppt ON pt.id = ppt.pubThreads_id
JOIN pub p ON ppt.pups_id = p.id
JOIN pubcategory pc ON p.pubCategoryId = pc.id
where pc2.id = pc.id and pt.id = 1
)
I assigned the wrong object to the predicate. If you want to match a subquery's id to the parent id, you must assign the Join, not the Root, if predicates do not belong to the root. Well, how obvious is that. :) However, glad to share. Below is the new query. There is an additional Predicate at the very bottom, which solves the issue. Also in conjunction with another boolean statement.
CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery mainQuery = criteriaBuilder
.createQuery(PubThread.class);
// 1) MainQuery
// Create the FROM
Root<PubThread> rootPubThread = mainQuery.from(PubThread.class);
// Create the JOIN from the first select: join-chaining. You only need the return for ordering. e.g. cq.orderBy(cb.asc(categoryJoin.get(Pub_.title)));
Join<Pub, PubCategory> categoryJoin = rootPubThread.join(PubThread_.pups).join(Pub_.pubCategory);
// Create the WHERE
mainQuery.where(criteriaBuilder.not(criteriaBuilder.equal(rootPubThread.get(PubThread_.id), threadId)));
// Create the SELECT, at last
mainQuery.select(rootPubThread).distinct(true);
// 2) Subquery
Subquery<PubThread> subquery = mainQuery.subquery(PubThread.class);
Root<PubThread> rootPubThreadSub = subquery.from(PubThread.class);
//subquery.where(criteriaBuilder.equal(rootPubThread.get(PubThread_.id), threadId));
Join<Pub, PubCategory> categoryJoinSub = rootPubThreadSub.join(PubThread_.pups).join(Pub_.pubCategory);
subquery.select(rootPubThreadSub);
//Predicate correlatePredicate = criteriaBuilder.equal(rootPubThreadSub.get(PubThread_.id), rootPubThread);
Predicate correlatePredicate = criteriaBuilder.and(
//criteriaBuilder.equal(rootPubThreadSub.get(PubThread_.id), rootPubThread),
criteriaBuilder.equal(categoryJoinSub.get(PubCategory_.id), categoryJoin.get(PubCategory_.id)),
criteriaBuilder.equal(rootPubThreadSub.get(PubThread_.id), threadId)
);
subquery.where(correlatePredicate);
//Predicate correlatePredicate = criteriaBuilder.equal(rootPubThreadSub.get(PubThread_.id), rootPubThread);
Predicate mainPredicate = criteriaBuilder.and(
criteriaBuilder.not(criteriaBuilder.equal(rootPubThread.get(PubThread_.id), threadId)),
criteriaBuilder.exists(subquery)
);
//cq.where(criteriaBuilder.exists(subquery));
mainQuery.where(mainPredicate);
TypedQuery<PubThread> typedQuery = em.createQuery(mainQuery);
List<PubThread> otherPubThreads = typedQuery.getResultList();

JPA 2 Criteria API using metamodel case sensitive condition

I have the following line of code to get the results based on like statement using Hibernate 4 API
Predicate predicate = cb.like(emp.get(EmployeeDetail_.empName),
empName+"%");
The generated sql statement is
select employeede0_.EMPLOYEE_NAME as EMPLOYEE1_0_ from EMPLOYEES employeede0_
where employeede0_.EMPLOYEE_NAME like 'smith%'
How can I modify my java code to have EMPLOYEE_NAME in lower case? The generated sql output should be like the following
select employeede0_.EMPLOYEE_NAME as EMPLOYEE1_0_ from EMPLOYEES employeede0_
where lower(employeede0_.EMPLOYEE_NAME) like lower('smith%')
Complete code for getting results
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Employee> c = cb.createQuery(Employee.class);
Root<Employee> emp = c.from(Employee.class);
c.select(emp);
List<Predicate> criteria = new ArrayList<Predicate>();
ParameterExpression<String> pexp = cb.parameter(String.class,
"empName");
Predicate predicate = cb.like(emp.get(Employee_.empName),
empName+"%");
criteria.add(predicate);
if (criteria.size() == 1) {
c.where(criteria.get(0));
} else if (criteria.size() > 1) {
c.where(cb.and(criteria.toArray(new Predicate[0])));
}
TypedQuery<EmployeeDetail> q = entityManager.createQuery(c);
data.setResult(q.getResultList());
Use CriteriaBuilder#lower():
Predicate predicate = cb.like(cb.lower(emp.get(EmployeeDetail_.empName)),
empName.toLowerCase() + "%");

Categories