Write query in CriteriaBuilder using Optional<T> - java

#Query(value = "select usd from UserDetail usd where usd.phoneNumber=:phoneNumber")
Optional<UserDetail> findByPhoneNumber(#Valid #Param("phoneNumber") String phoneNumber);
This is my JPQL query method and I am here using return type with Optional. I wonder how to write using CriteriaBuilder?
I wrote like this but I am getting an error.
#Override
public Optional<UserDetail> findByCreatedDate(String phoneNumber) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<UserDetail> cq = cb.createQuery(UserDetail.class);
Root<UserDetail> root = cq.from(UserDetail.class);
cq.where(cb.equal(root.get("phoneNumber"), phoneNumber))
.orderBy(cb.desc(root.get("phoneNumber")));
return em.createQuery(cq).getResultList().get(3);
}

If the method return type is Optional that is what exactly you must return.
The line em.createQuery(cq).getResultList().get(3) returns only UserDetail so you need to wrap it to Optional.of:
#Override
public Optional<UserDetail> findByCreatedDate(String phoneNumber) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<UserDetail> cq = cb.createQuery(UserDetail.class);
Root<UserDetail> root = cq.from(UserDetail.class);
cq.where(cb.equal(root.get("phoneNumber"), phoneNumber))
.orderBy(cb.desc(root.get("phoneNumber")));
return Optional.of(em.createQuery(cq).getResultList().get(3));
}
By the way, are you sure the hardcoded .get(3) is what you want? If the list is empty or has less than 4 elements, it would throw a runtime exception.

if a phone number is associated with only one person why are you trying to get a list ?
have a look to this :
#Override
public Optional<UserDetail> findByCreatedDate(String phoneNumber) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<UserDetail> cq = cb.createQuery(UserDetail.class);
Root<UserDetail> root = cq.from(UserDetail.class);
cq.where(cb.equal(root.get("phoneNumber"), phoneNumber)).orderBy(cb.desc(root.get("phoneNumber")));
UserDetail ud = em.createQuery(cq).getSingleResult();
return ud == null ? Optional.empty() : Optional.of(ud)
}
Regards,

Related

Combining JPA And/Or Criteria Predicates

I want to query like: (sku = 'A' AND uom_code = 'B') OR (sku =
C' AND uom_code = 'D')
But my predicate generate such as (sku = 'A' AND uom_code = 'B' OR sku = 'C' AND uom_code = 'D')
how to fix it. Thank you.
public class VariantSpecification implements Specification<VariantModel> {
private final VariantFilter filter;
public VariantSpecification(VariantFilter filter) {
this.filter = filter;
}
#Override
public Predicate toPredicate(
Root<VariantModel> root,
CriteriaQuery<?> query,
CriteriaBuilder cb
) {
final List<Predicate> predicates = new ArrayList<>();
if (filter.getMerchantId() != null) {
predicates.add(cb.equal(root.get("merchantId"), filter.getMerchantId()));
}
Predicate predicate = cb.and(predicates.toArray(new Predicate[0]));
List<Predicate> predicatesMap = new ArrayList<>();
if (!CollectionUtils.isEmpty(filter.getSkusUoms())) {
filter.getSkusUoms().forEach(pair -> {
String sku = pair.getLeft();
String uom = pair.getRight();
Predicate predicateSku = cb.equal(root.get("sku"), sku);
Predicate predicateUom = cb.equal(root.get("uomCode"), uom);
predicatesMap.add(cb.or(cb.and(predicateSku, predicateUom)));
});
}
if (!predicatesMap.isEmpty()) {
Predicate predicateSector = cb.or(predicatesMap.toArray(new Predicate[0]));
return cb.and(predicate, predicateSector);
}
return predicate;
}
}
Yeah my mistake,
SELECT ... FROM ... WHERE (expression1 AND expression2) OR (expression3 AND expression4)
SELECT ... FROM ... WHERE expression1 AND expression2 OR expression3 AND expression4
According to the SQL specification, both statements mean the same thing. It doesn't matter whether the statement contains the () or not, so Hibernate doesn't use them. The order of precedence is like this, similar to 1+2*3 is the same as 1+(2*3).

CriteriaBuilder search in 2 table get performance issue

I try to search a list keyword on 2 different table by CriteriaBuilder and get performance issue on line 26.
Can someone help me to improve this.
public static Specification<TrainingBatchVolunteerDetails> filterVolunteers(final TrainingVolunteerForm form) {
return new Specification<TrainingBatchVolunteerDetails>() {
#Override
public Predicate toPredicate(Root<TrainingBatchVolunteerDetails> root,
CriteriaQuery<?> query, CriteriaBuilder cb) {
....
List<Predicate> predicates = new ArrayList<Predicate>();
Join<TrainingBatchVolunteerDetails, TrainingBatches> batches = root.join("batches");
Join<TrainingBatches, Training> training = batches.join("training");
training.join("trainingVolunteerDetails").join("eventBaseProfile");
Join<TrainingBatches, EventBaseProfile> eventBaseProfile = root.join("eventBaseProfile");
Join<EventBaseProfile, BaseProfile> baseProfile = eventBaseProfile.join("baseProfile");
Join<CtExtendProfile, BaseProfile> ctExtendProfile = null;
if (StringUtils.isNotEmpty(form.getNricName())) {
...
ctExtendProfile = baseProfile.join("ctExtendProfile", JoinType.LEFT);
List<String> keywords = new ArrayList<String>();
...
predicates.add(cb.or(
baseProfile.<String>get("identifierId").in(keywords),
ctExtendProfile.<String>get("birthCertificate").in(keywords)
));
}
return cb.and(predicates.toArray(new Predicate[predicates.size()]));
}
};
}

Dynamic COUNT in JPA throws NullPointerException

My goal is to make a method with which I can retrieve the total number of rows from any table in my database.
This is what I have done:
ENTITY_MANAGER_FACTORY = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT);
EM = ENTITY_MANAGER_FACTORY.createEntityManager();
public static long getRowCount(Root<?> root) {
CriteriaBuilder cb = EM.getCriteriaBuilder();
CriteriaQuery<Long> query = cb.createQuery(Long.class);
root = query.from(root.getClass());
query.select(cb.count(root));
return EM.createQuery(query).getSingleResult();
}
This is how I call the method:
Root<PlacesEntity> placesRoot = null;
DbTools.getRowCount(placesRoot)
It is not working though, the method throws a NullPointerException at the line root = query.from(root.getClass());.
Try this (warning not tested):
public static long getRowCount(Class<?> rootClass) {
CriteriaBuilder cb = EM.getCriteriaBuilder();
CriteriaQuery<Long> query = cb.createQuery(Long.class);
Expression<?> root = query.from(rootClass);
query.select(cb.count(root));
return EM.createQuery(query).getSingleResult();
}
/*****/
DbTools.getRowCount(PlacesEntity.class);
Good luck!

select all from a JPQL query, with paremeters

I have selection parameters on flat data, only don't know how to either omit a parameter entirely, or make it a complete wildcard. The search might use one or all parameters. How is this done? With ANY or ALL? Or, is there another way?
I would like to use one general query with all the paremeters, and pass in "all" or "any", something along those lines, for some of those parameters.
existing code:
package legacy.database;
import java.sql.Timestamp;
import java.util.List;
import java.util.logging.Logger;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.Query;
public class MyQueries {
private static final Logger log = Logger.getLogger(MyQueries.class.getName());
private final EntityManagerFactory emf = Persistence.createEntityManagerFactory("LegacyDatabasePU");
private final EntityManager em = emf.createEntityManager();
public MyQueries() {
}
public List<Clients> findAll() {
Query q = em.createQuery("select c from Clients c");
List<Clients> clients = q.getResultList();
return clients;
}
public List<Clients> selectWithParameters(Criteria c) {
log.info(c.toString());
String opener = c.getOpener();
String closer1 = c.getCloser1();
String status = c.getStatus();
Query q = em.createQuery(
"SELECT c FROM Clients c "
+ "WHERE c.status like :status "
+ "and c.closer1 like :closer1 "
+ "and c.opener like :opener");
q.setParameter("opener", opener);
q.setParameter("closer1", closer1);
q.setParameter("status", status);
log.info(q.toString());
List<Clients> clients = q.getResultList();
log.fine(clients.toString());
return clients;
}
public Clients findById(int id) {
Clients client = em.find(Clients.class, id);
return client;
}
public void send(int id) {
Clients c = em.find(Clients.class, id);
java.util.Date date = new java.util.Date();
Timestamp t = new Timestamp(date.getTime());
em.getTransaction().begin();
c.setDateUpdated(t.toString());
em.getTransaction().commit();
}
}
In case the parameters are optional, the criteria API provides some more flexibility.
If the selectWithParameters is called often, consider using parameters, since the DB can cache the parametrized query then.
selectWithParameters with optional parameters reads like this:
public List<Clients> selectWithParameters(Criteria criteria) {
log.info(criteria.toString());
String opener = criteria.getOpener();
String closer1 = criteria.getCloser1();
String status = criteria.getStatus();
CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery<Clients> query = criteriaBuilder.createQuery(Clients.class);
Root<Clients> c = query.from(Clients.class);
List<Predicate> wherePredicates = new LinkedList<Predicate>();
if (null != status) {
wherePredicates.add(criteriaBuilder.like(c.get("status"), status));
}
if (null != closer1) {
wherePredicates.add(criteriaBuilder.like(c.get("closer1"), closer1));
}
if (null != opener) {
wherePredicates.add(criteriaBuilder.like(c.get("opener"), opener));
}
query.where(wherePredicates.toArray(new Predicate[0]));
List<Clients> clients = em.createQuery(query).getResultList();
log.fine(clients.toString());
return clients;
}
Thank you, Heiner. This worked, not sure why I had trouble with Heiner's code, but his sample put me in the right direction:
public List<Clients> selectByCriteria(Criteria criteria) {
CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery<Clients> clientCriteriaQuery = criteriaBuilder.createQuery(Clients.class);
Root<Clients> clientRoot = clientCriteriaQuery.from(Clients.class);
clientCriteriaQuery.select(clientRoot);
List<Predicate> predicates = new ArrayList<>();
predicates.add(criteriaBuilder.like(clientRoot.get(Clients_.phone1), "%" + criteria.getPhone1() + "%"));
if (!criteria.getOpener().equalsIgnoreCase("all")) {
predicates.add(criteriaBuilder.like(clientRoot.get(Clients_.opener), "%" + criteria.getOpener() + "%"));
}
if (!criteria.getCloser1().equalsIgnoreCase("all")) {
predicates.add(criteriaBuilder.like(clientRoot.get(Clients_.closer1), "%" + criteria.getCloser1() + "%"));
}
if (!criteria.getStatus().equalsIgnoreCase("all")) {
predicates.add(criteriaBuilder.like(clientRoot.get(Clients_.status), "%" + criteria.getStatus() + "%"));
}
clientCriteriaQuery.where(predicates.toArray(new Predicate[0]));
List<Clients> clients = em.createQuery(clientCriteriaQuery).getResultList();
return clients;
}
There's probably no substantive difference (?) from what Heiner answered. JPA and JPQL are a bit murky. I can't believe it, but I almost prefer SQL! I'll have to adjust.

How to do a distinct count in JPA critera API?

I would like to do this but with the criteria API instead:
select count(distinct e) from Event e, IN(e.userAccessPermissions) p where p.principal = :principal and p.permission in (:permissions)
Any ideas?
You can use countDistinct on CriteriaBuilder
criteriaQuery.select(criteriaBuilder.countDistinct(entityRoot))
Use c.distinct(true) on your Query.
See http://relation.to/Bloggers/ATypesafeCriteriaQueryAPIForJPA for more samples.
Like this?
Criteria crit = session.createCriteria(Event.class):
crit.createAlias("userAccessPermissions", "p");
crit.add(Restrictions.eq("p.principal", principal);
crit.add(Restrictions.in("p.permission", permissions);
crit.setProjection(Projections.countDistinct("id"));
public long getCount(String xValue){
EntityManager entityManager = this.getEntityManager();
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Long> criteriaQuery = cb.createQuery(Long.class);
Root<MyEntity> root = criteriaQuery.from(MyEntity.class);
criteriaQuery.select(cb.count(criteriaQuery.from(MyEntity.class)));
List<Predicate> predicates = new ArrayList<>();
Predicate xEquals = cb.equal(root.get("x"), xValue);
predicates.add(xEquals);
criteriaQuery.select(cb.countDistinct(root));
criteriaQuery.where(predicates.toArray(new Predicate[0]));
return entityManager.createQuery(criteriaQuery).getSingleResult();
}
With Spring Data Jpa, we can use this method:
/*
* (non-Javadoc)
* #see org.springframework.data.jpa.repository.JpaSpecificationExecutor#count(org.springframework.data.jpa.domain.Specification)
*/
#Override
public long count(#Nullable Specification<T> spec) {
return executeCountQuery(getCountQuery(spec, getDomainClass()));
}

Categories