JPA CriteriaBuilder and Substring - java

I have a CriteriaBuilder where I am trying to get characters starting from 0 to 10. However I am not able to get the desired output.
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Emp> cq = cb.createQuery(Emp.class);
Root<Emp> c = cq.from(Emp.class);
cb.substring(c.<String>get("projDesc"), 0, 10);
cq.orderBy(cb.desc(c.get("salary")));
Query query = em.createQuery(cq);
.....
What could be the reason for this?

From the javadoc
Create an expression for substring extraction. Extracts a substring of
given length starting at the specified position. First position is 1.
Try doing cb.substring(c.<String>get("projDesc"), 1, 10);
I think you're forgetting to select the Expression<E>
Try cq.select(cb.substring(c.<String>get("projDesc"), 1, 10))
It will return List<String> if you need to Return the Emp you can use the
cb.construct(Emp.class, e.get("prop1"), e.get("prop2"), cb.substring(c.<String>get("projDesc"), 1, 10)));

I also, faced same problem in which I have to substring first three char and fetch all accounts starting from 107 which is number. for that I have used CriteriaBuilder and substring method like below.
Predicate accountNumber = criteriaBuilder.equal(criteriaBuilder.substring(from.get("accountNumber").as(String.class), 0, 3), cwipAcc);
but unfortunately it is not working for CriteriaBuilder and substring. so I have used like query to resolve this issue by given code below.
Predicate accountNumber = criteriaBuilder.like(from.get("accountNumber").as(String.class), String.valueOf(cwipAcc) + "%");
here, I have just fetched all the records which is starting from 107 and so on.
Example:
public List<GLCharts> findAccountForCWIP(Long cwipAcc, Long glInfo) {
Map<String, Object> data = new HashMap();
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<GLCharts> criteriaQuery = criteriaBuilder.createQuery(GLCharts.class);
Root<GLCharts> from = criteriaQuery.from(GLCharts.class);
Predicate accountNumber = criteriaBuilder.like(from.get("accountNumber").as(String.class), String.valueOf(cwipAcc) + "%");
Predicate glCompanyInfo = criteriaBuilder.equal(from.join("gLCompanyInfo").get("id"), glInfo);
Predicate finalPredicate = criteriaBuilder.and(accountNumber, glCompanyInfo);
criteriaQuery.select(from).where(finalPredicate).orderBy(Stream.of(criteriaBuilder.asc(from.get("accountNumber"))).collect(Collectors.toList()));
List<GLCharts> glChartsList = entityManager.createQuery(criteriaQuery).getResultList();
return glChartsList;
}

I faced the problem because my requirement was to use a number into substr. Following is the sample code.
#Override
public List<SampleProfile> findNonSampleProfileBySequence(Long SampleNo) {
List<SampleProfile> profiles = new ArrayList<>();
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<SampleProfile> criteriaQuery = criteriaBuilder.createQuery(SampleProfile.class);
Root<SampleProfile> SampleProfileRoot = criteriaQuery.from(SampleProfile.class);
List<Predicate> predicates = new ArrayList<Predicate>();
if (SampleUtil.isValidLong(SampleNo)) {
String SampleStr = Long.toString(SampleNo);
if (StringUtils.isNotBlank(SampleStr) && SampleStr.length() > 5) {
String SampleSequence = SampleStr.substring(5);
predicates.add(criteriaBuilder.equal(criteriaBuilder.substring(SampleProfileRoot.get(SampleProfile_.id).as(String.class), 6), SampleSequence));
predicates.add(criteriaBuilder.equal(SampleProfileRoot.get(SampleProfile_.address).get(Address_.department), SampleStr.substring(0,3)));
}
}
if (!CollectionUtils.isEmpty(predicates)) {
criteriaQuery.where(criteriaBuilder.and(Iterables.toArray(predicates, Predicate.class)));
profiles = entityManager.createQuery(criteriaQuery).setMaxResults(AbstractJpaDAO.MAX_ROW_LIMIT).getResultList();
}
return profiles;
}
Also note that for performance benefits you have to create an index on the same.
The Cast Keyword is important as Hibernate Dialect will create query like this, thus, it has to match with your index.
CREATE INDEX MY_SCHEMA_OWNER.IDX_SUBSTR_SMP_CODE ON MY_SCHEMA_OWNER.SMP_PROFILE (SUBSTR(**CAST**(SMP_CODE AS VARCHAR2(255 CHAR)),6));

Related

How to do a like query with a variable set of strings?

I am triying to do a "like query" with a variable set of strings, in order to retrieve in a single query all texts that contains a set of words, that is:
public long countByTextLike(Set<String> strings) {
CriteriaBuilder builder = manager.getCriteriaBuilder();
CriteriaQuery<Long> query = builder.createQuery(Long.class);
Root<Example> root = query.from(Example.class);
query.select(builder.count(root.get("id"))).where(
builder.and(
builder.equal(root.get("lang"), "EN")
)
);
//this does not work
for (String word : strings) {
query.where(builder.or(builder.like(root.get("text"), word)));
}
return manager.createQuery(query).getSingleResult();
}
unfortunately this does not work because the where is overwritten in each loop. Only the last word of loop is used and "AND" restictions are being overwriten.
How is possible to do a "like query" with a variable number of strings? It is not posible?
I am using the spring framework but i think that the question could be extendable to hibernate
You can use predicates, and then add them all with only one where clause
public long countByTextLike(Set<String> strings) {
CriteriaBuilder builder = currentSession().getCriteriaBuilder();
CriteriaQuery<Long> query = builder.createQuery(Long.class);
Root<Example> root = query.from(Example.class);
Predicate[] predicates = new Predicate[strings.size()];
query.select(builder.count(root.get("id")));
Predicate langPredicate = builder.equal(root.get("lang"), "EN");
int cont = 0;
for (String word : strings) {
Predicate pred = builder.like(root.get("text"), "%" + word + "%");
predicates[cont++] = pred;
}
Predicate orPredicate = builder.or(predicates);
Predicate finalPredicate = builder.and(orPredicate, langPredicate);
return manager.createQuery(query).where(finalPredicate).getSingleResult();
}

How parse a type String to Long in Criteria with JPA using expression LIKE?

I'm new here, I have a problem writting a method where I tried to call a expression with criteria using JPA and Spring, I have the next code:
#Override
public List<ContractOrder> getOrdersByIn(List<String> paramsIn ) {
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<ContractOrder> query = builder.createQuery(ContractOrder.class);
Root<ContractOrder> root = query.from(ContractOrder.class);
Expression<String> exp = root.get("**order_id**");
Predicate predicateIn = exp.**in**(paramsIn);
ParameterExpression<Long> pexp = builder.parameter(Long.class,"order_id");
Predicate predicateLike = builder.like(exp, pexp);
query.where(builder.or(predicateIn,predicateLike));
TypedQuery<ContractOrder> queryT = entityManager.createQuery(query.select(root));
queryT.setParameter(0, Long.valueOf("%5"));
List<ContractOrder> lista = queryT.getResultList();
return lista;
}
Where "order_id" is mapping as type long and I want to pass a parameter like this "%5".
Can you help me with it?
Regards!!
Why this %5 ???
It's a variable ???????
queryT.setParameter(0, Long.valueOf(5));
or
queryT.setParameter(0, Long.valueOf("5"));
should be OK.
Why ** and not just * ?I don't understand...
Predicate predicateIn = exp.**in**(paramsIn);
Is it compile?????

CriteriaBuilder in JPA with the method between

I'm trying to use the CriteriaBuilder.between but I have an error, the code is this:
public List<Oferta> findByFechaFinOferta(Date fechaInicio,Date fechaFin){
CriteriaBuilder cBuilder = em.getCriteriaBuilder();
CriteriaQuery<Oferta> cQuery =cBuilder.createQuery(Oferta.class);
Root<Oferta> a = cQuery.from(Oferta.class);
ParameterExpression <String> param = cBuilder.parameter(String.class);
cQuery.select(a).where(cBuilder.between(a.get("fechaFin"),fechaInicio,fechaFin));
TypedQuery<Oferta> tQuery = em.createQuery(cQuery);
List<Oferta> oferta = tQuery.getResultList();
return oferta;
}
The cQuery.select(a).where(cBuilder.between(a.get("fechaFin"),fechaInicio,fechaFin)); is wrong, but how I can do to make it work out?.
Thanks so much
In this way I can answer myself:
Predicate predicate = cBuilder.between(a.get(Oferta_.fechaFin),fechaInicio,fechaFin);
cQuery.select(a).where(predicate);

building a criteria query with jpa 2.0 by using a dynamic list

I'm a bit confused while creating a criteriaQuery with JPA 2.0.
Prerequisites:
I have a Gui, where the user can mark some checkboxes of (let us say) wheatherstations with some options like temperature/wind/timeperiod/etc...
Now I want to set up a criteriaQuery to pick just the selected items from a sql database and return it as an object/Map/List for building some DataModels (this will be used for generating a few primefaces charts).
What i have so far:
// for presentation purposes just this mockup-data
Calendar start = new GregorianCalendar(2011, Calendar.APRIL, 1);
Calendar end = new GregorianCalendar(2011, Calendar.MAY, 1);
List<String> selectedStations = new LinkedList<String>() {{
add("PS1");
add("PS2");
add("PS3");
}};
Map<String, Object selectedOptions = new LinkedHashMap<String, Object>() {{
put("opt1","val1");
put("opt2","val2");
put("opt3","val3");
}};
List<String> sel = new LinkedList<String>() {{
add("selOpt1");
add("selOpt2");
add("selOpt3");
}};
criteriaBuilder, criteriaQuery and the mapping class:
// go for the criteriaBuilder
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Tuple> cq = cb.createTupleQuery();
Root<StationItem> r = cq.from(StationItem.class);
Setting up the predicates:
// ... where (name="PS1" or name="PS2" or name="PS3") ...
Predicate p1 = cb.disjunction();
for (String s : selectedStations) {
p1 = cb.or(p1, cb.equal(r.get("name").as(String.class), s));
}
Predicate p2 = cb.between(r.get("fetchDate").as(Date.class),
start.getTime(), end.getTime());
Predicate p3 = cb.conjunction();
for (Map.Entry<String, Object> param : selectedOptions.entrySet())
p3 = cb.and(p3, cb.equal(r.get(param.getKey()), param.getValue()));
And the final step to run the query and fetching the results:
At this point I do not know what is the best approach to fill the multiselect criteria with my selections. I would like to insert all items/selections from the List sel to cq.multiselect() with some kind of a loop in a dynamic way...Any idea is welcome!
// This is working but static :(
cq.multiselect(r.get(sel.get(0)), r.get(sel.get(1)), r.get(sel.get(2)));
// i would prefer to have something like
for (int i=0;i<sel.size();i++) {
cq.multiselect().add(r.get(sel.get(i)));
}
Concatenating my WHERE-clause and executing the query:
cq.where(cb.and(p1,p2,p3));
List<Tuple> res = em.createQuery(cq).getResultList();
for (Tuple t : res) {
// do something ...
};
return <something useful>
Following a pseudo SQL query to sum up what I want to achieve:
SELECT {items from List<String> sel}
FROM MyStationDatabase
WHERE (name = selectedStation.get(0) OR ... OR name = selectedStation.get(last))
AND {items from Map<String,Object> selectedOptions}
Well, sometimes it's too trivial to be true -.-
One way to fill the cq.multiselect() with my dynamic list is to just create a list of selections and pass this over to my multiselect-query.
List<Selection<?>> s = new LinkedList<Selection<?>>();
for (String item : sel) {
s.add(r.get(item));
}
cq.multiselect(s);
easy, but maybe someone has the same struggles with this :)
and even if not, see it as an example for a criteriaQuery ;)

How can I retrieve multiple objects with JPA?

I'm using JPA2/hibernate with this data model:
class Stock {
#ManyToOne
private StockGroup stockGroup;
private boolean visible;
}
class StockGroup {
#OneToMany(mappedBy = "stockGroup")
private List<Stock> stocks;
}
I would like to retrieve StockGroup's containing Stock's where visible==true.
I've come up with this faulty code:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<StockGroup> q = cb.createQuery(StockGroup.class);
Root<StockGroup> r = q.from(StockGroup.class);
Join<StockGroup, Stock> j = r.join(StockGroup_.stocks, JoinType.INNER);
Predicate p = cb.equal(j.get(Stock_.visible), true);
// This becomes a cartesian product :(
List<StockGroup> l = em.createQuery(q.where(p)).getResultList();
// Stocks are not filtered on visible :(
l.get(0).getStocks();
Is it possible to retrieve the StockGroup and Stock Objects with one CriteriaQuery or can JPA only fill one type at once? Or can i add some Criteria when .getStocks() is filled lazily?
The trick to doing this is returning a tuple containing an old-fashioned join between the Stock and the StockGroup, like this:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Tuple> q = cb.createQuery(Tuple.class);
Root<Stock> sRoot = q.from(Stock.class);
Root<StockGroup> sgRoot = q.from(StockGroup.class);
q.select(cb.tuple(sRoot, sgRoot)).where(
cb.and(cb.equal(sRoot.get(Stock_.stockGroup), sgRoot),
cb.isTrue(sRoot.get(Stock_.visible))));
List<Tuple> l = em.createQuery(q).getResultList();
The tuple is then not fully type safe, but you can reach it by position (or by alias, if you've given your select expressions or roots an alias):
for (Tuple t : l) {
Stock s = (Stock) t.get(0);
StockGroup sg = (StockGroup) t.get(1);
System.out.println("Stock is : " + s + " .... StockGroup: " + sg);
}
There's a good article on IBM DeveloperWorks on JPA2 Typesafe Queries.
Good luck in your JPA2 endeavors!

Categories