JPA Criteria api query fails while JPQL goes through - jpa

I'm using JPA to manage my persistency layer.
One of my my Criteria API throws an exception. I re-wrote it in JPQL and it works just fine so I guess I missed something in my criteria api version.
Here it is, my criteria api query:
public FoodItemTagsOverrideRule findByFoodItemIdAndType(long foodItemId, RuleTypes ruleType) {
CriteriaQuery<Rule> c = getCriteriaQuery();
Root<Rule> rule =
c.from(Rule.class);
Predicate foodItemIdCondition =
cb.equal(rule.get(Rule_.foodItemId), foodItemId);
Predicate typeCondition =
cb.equal(rule.get(Rule_.ruleType),
ruleType.toString());
c.where(foodItemIdCondition, typeCondition);
TypedQuery<Rule> q =
entityManager.createQuery(c);
List<Rule> result = q.getResultList();
if (result.isEmpty()) {
return null;
}
return result.get(0);
}
The JPQL version that works just fine:
public Rule findByFoodItemIdAndType(long foodItemId, RuleTypes ruleType) {
TypedQuery<Rule> query = getEntityManager().createQuery(
"SELECT rule " + "FROM " + Rule.class.getSimpleName() + " rule " + "WHERE rule.foodItemId = :foodItemId "
+ "AND rule.ruleType = :ruleType", Rule.class);
query.setParameter("foodItemId", foodItemId);
query.setParameter("ruleType", ruleType.toString());
List<Rule> result = query.getResultList();
if (result.isEmpty()) {
return null;
}
return result.get(0);
}
Can you see a difference there? Did I put something wrong in the criteria api query?
Thakns!

You can try the below code. I tried to fit to your code, you can alter accordingly.
CriteriaBuilder qb = em.getCriteriaBuilder();
CriteriaQuery cq = qb.createQuery();
Root<Rule> rule = cq.from(Rule.class);
List<Predicate> predicates = new ArrayList<Predicate>();
predicates.add(qb.equal(rule.get("foodItemId"), foodItemId));
predicates.add(qb.equal(rule.get("ruleType"), ruleType));
cq.select(rule).where(predicates.toArray(new Predicate[]{}));
em.createQuery(cq).getResultList();
Edit : For type-safe predicate definition
predicates.add(qb.equal(rule.get(Rule_.foodItemId), foodItemId));
predicates.add(qb.equal(rule.get(Rule_.ruleType), ruleType));

Related

Count Criteria joining columns

I'm trying to count the rows of the filter the user uses, when the user wants to list all it works fine, the problem is when they use filters and that makes some joins to work properly.
(This is used for a lazy datatable in jsf)
My criteria code is:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<CarreraHorizontal> cq = cb.createQuery(CarreraHorizontal.class);
Root<CarreraHorizontal> root = cq.from(CarreraHorizontal.class);
CriteriaQuery<Long> cqCount = cb.createQuery(Long.class);
List<Predicate> predicates = new ArrayList<Predicate>();
Join<CarreraHorizontal, Gdp> childJoin = root.join("gdp", JoinType.LEFT);
Join<CarreraHorizontal, Empleado> childJoin2 = root.join("empleado", JoinType.LEFT);
if (!StringUtils.isBlank(grupoTitulacion))
predicates.add(cb.like(root.<String>get("grupoTitulacion"), "%" + grupoTitulacion + "%"));
if(gdp0Bool) {
predicates.add(cb.like(childJoin.<String>get("nombre"), "GDP0"));
if(!StringUtils.isBlank(gdp0Combo))
predicates.add(cb.le(childJoin.<Integer>get("numEscalones"), Integer.parseInt(gdp0Combo)));
}
if(epg6) {
predicates.add(cb.equal(childJoin2.get("progresionEscalon"), epg6));
}
//cq.where(cb.and(predicates.toArray(new Predicate[predicates.size()])));
cqCount.where(cb.and(predicates.toArray(new Predicate[predicates.size()])));
Root<CarreraHorizontal> rootCount = cqCount.from(CarreraHorizontal.class);
CriteriaQuery<Long> a = cqCount.select(cb.count(rootCount));
TypedQuery<Long> res = em.createQuery(a.select(cb.count(rootCount)));
return res.getSingleResult();
My exception is:
Invalid path: 'generatedAlias1.progresionEscalon' [select count(generatedAlias0) from es.valencia.gp.sbch.entity.CarreraHorizontal as generatedAlias0 where generatedAlias1.progresionEscalon=:param0]
I tried everything I found but it's still not working.

Criteria Builder Convert Lower different variant

Everywhere I look I see people using this
predicate =
criteriaBuilder.like(criteriaBuilder.lower(root.get(Product_.prodName)),
wrapper.getProdName());
especially I mean this Product_.prodName. I have no idea what it is, what it does and why I can't use or have it in my context. Here my code which not works:
Look at the line with !nachname.isEmpty
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Telefonbuch> query = builder.createQuery(Telefonbuch.class);
Root<Telefonbuch> root = query.from(Telefonbuch.class);
EntityType<Telefonbuch> model = em.getMetamodel().entity(Telefonbuch.class);
List<Predicate> predicates = new ArrayList<Predicate>();
if (!vorname.isEmpty()) {
Predicate condition = builder.like(root.<String>get("vorname"),"%"+vorname+"%");
predicates.add(condition);
}
if (!nachname.isEmpty()) {
Predicate condition = builder.like(builder.lower(root.get("nachname"),"%"+nachname+"%"));
predicates.add(condition);
}
query.select(root).where(predicates.toArray(new Predicate[predicates.size()]));
eintraege = em.createQuery(query).getResultList();

JPA Custom Query

I need your help. Basically I want to create a custom query for a view I made that contains most of the data needed by the client. The tricky part here is that the client can specify which columns to include in the search. A sample query would be like:
SELECT distinct s.empno FROM SesdbAllView s
WHERE s.lastname IN :lname AND s.examTaken IN :exam AND
s.training IN :train AND s.trainingFrom BETWEEN :from AND :to AND
s.eligibility IN :elig AND s.profession IN :prof
So I tried translating this to Criteria API but still stuck on how to do it especially in the BETWEEN keywords (where I check a range of a Date and also another for a Integer). When it comes to the IN keywords I'm not sure if I did it correctly as well.
My current code now is:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Tuple> cq = cb.createTupleQuery();
Root<SesdbAllView> r = cq.from(SesdbAllView.class);
Predicate p = cb.conjunction();
for (Map.Entry<String, Object> param : parameters.entrySet()) {
if (param.getValue() instanceof List) {
Expression<String> exp = r.get(param.getKey());
p = cb.and(p, exp.in((List<String>)param.getValue()));
} else if (param.getValue() instanceof DateFromTo) {
DateFromTo fromTo = (DateFromTo) param.getValue();
p = cb.between(r.get(param.getKey()).as(Date.class),fromTo.getFrom(),fromTo.getTo());
} else if (param.getValue() instanceof IntegerFromTo) {
IntegerFromTo fromTo = (IntegerFromTo) param.getValue();
p = cb.between(r.get(param.getKey()).as(Integer.class),fromTo.getFrom(),fromTo.getTo());
} else {
p = cb.and(p, cb.equal(r.get(param.getKey()), param.getValue()));
}
}
cq.distinct(true);
cq.multiselect(r.get("empNo"))
.where(p);
List<Tuple> result = em.createQuery(cq).getResultList();

CollectionAttribute testing in JPA CriteriaBuilder

I have a CriteriaBuilder and am trying to create a predicate to restrict results in a CollectionAttribute. I would like to say something like get all entities in this collection where the entity's attribute A equals x. For example, I have lots of People entities. They have a collection of job titles (previous and current) and I'd like to know about all people who have had a title of "banker". Here is an example:
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Object> query = builder.createQuery();
Root<MheAreaLocation> root = query.from(MheAreaLocation.class);
Predicate p2 = builder.equal(root.get(Jobs_.jobs).get(Jobs_.titles), "banker");
TypedQuery<Object> q = em.createQuery(query);
List<Object> resultList = q.getResultList();
...
Any help would be great. I'm struggling to find much about CollectionAttribute and using them in predicates...and I keep getting nulls when I use the join below in the predicate :-( Thanks
This is my actual code:
CriteriaBuilder builder = em.getCriteriaBuilder();
// CriteriaQuery<Tuple> query = builder.createQuery();
CriteriaQuery<Object> query = builder.createQuery();
Root<MheAreaLocation> mheAreaLocationRoot = query.from(MheAreaLocation.class);
CollectionJoin<MheLocation, AtlasLocationGroupMap> join = mheAreaLocationRoot.join(MheAreaLocation_.childLocation).join(MheLocation_.atlasLocationGroupMapCollection);
// .join(AtlasLocationGroupMap_.atlasLocationGroup, JoinType.INNER);
Predicate p1 = builder.equal(mheAreaLocationRoot.get(MheAreaLocation_.parentLocation).get(MheLocation_.mheLocId), "AZP1B");
// Predicate p2 = builder.equal(mheAreaLocationRoot.get(MheAreaLocation_.childLocation).get(MheLocation_.atlasLocationGroupMapCollection).);
Predicate p2 = builder.equal(join.get(AtlasLocationGroupMap_.atlasLocationGroup).get(AtlasLocationGroup_.locationGroupType), "NEXT_STATION");
// query.where(builder.and(e1, e2));
// mheAreaLocationRoot.fetch(MheAreaLocation_.childLocation);
// join.fetch(MheLocation_.atlasLocationGroupMapCollection);
// query.multiselect(mheAreaLocationRoot.get(MheAreaLocation_.parentLocation),
// mheAreaLocationRoot.get(MheAreaLocation_.childLocation));
// query.select(builder.tuple(join.get(AtlasLocationGroupMap_.mheLocation)));
TypedQuery<Object> q = em.createQuery(query);
List<Object> resultList = q.getResultList();
...
Other classes (I can add more if needed):
#StaticMetamodel(MheLocation.class)
public class MheLocation_ {
public static volatile CollectionAttribute<MheLocation, AtlasLocationGroupMap> atlasLocationGroupMapCollection;
public static volatile SingularAttribute<MheLocation, String> mheLocId;
}
Take this example which has classes of Farm and Animal (Farm has a collection<Animal>) and we want to impose a criteria on the name of the Animal. Not exactly the same as yours I think, but may point you in the right direction
CriteriaBuilder qb = emf.getCriteriaBuilder();
CriteriaQuery<Farm> crit = qb.createQuery(Farm.class);
Root<Farm> candidate = crit.from(Farm.class);
candidate.alias("f");
crit.select(candidate);
Metamodel model = emf.getMetamodel();
ManagedType farmType = model.managedType(Farm.class);
Attribute animalAttr = farmType.getAttribute("animals");
Join animalJoin = candidate.join((ListAttribute)animalAttr);
animalJoin.alias("a");
Path nameField = animalJoin.get("name");
Predicate nameEquals = qb.equal(nameField, "Woolly Sheep");
crit.where(nameEquals);
Equates to
SELECT f FROM org.jpox.samples.annotations.one_many.bidir.Farm f JOIN f.animals a WHERE (a.name = 'Woolly Sheep')
HTH

using the TSqlParser

I'm attempting to parse SQL using the TSql100Parser provided by microsoft. Right now I'm having a little trouble using it the way it seems to be intended to be used. Also, the lack of documentation doesn't help. (example: http://msdn.microsoft.com/en-us/library/microsoft.data.schema.scriptdom.sql.tsql100parser.aspx )
When I run a simple SELECT statement through the parser it returns a collection of TSqlStatements which contains a SELECT statement.
Trouble is, the TSqlSelect statement doesn't contain attributes such as a WHERE clause, even though the clause is implemented as a class. http://msdn.microsoft.com/en-us/library/microsoft.data.schema.scriptdom.sql.whereclause.aspx
The parser does recognise the WHERE clause as such, looking at the token stream.
So, my question is, am I using the parser correctly? Right now the token stream seems to be the most useful feature of the parser...
My Test project:
public static void Main(string[] args)
{
var parser = new TSql100Parser(false);
IList<ParseError> Errors;
IScriptFragment result = parser.Parse(
new StringReader("Select col from T1 where 1 = 1 group by 1;" +
"select col2 from T2;" +
"select col1 from tbl1 where id in (select id from tbl);"),
out Errors);
var Script = result as TSqlScript;
foreach (var ts in Script.Batches)
{
Console.WriteLine("new batch");
foreach (var st in ts.Statements)
{
IterateStatement(st);
}
}
}
static void IterateStatement(TSqlStatement statement)
{
Console.WriteLine("New Statement");
if (statement is SelectStatement)
{
PrintStatement(sstmnt);
}
}
Yes, you are using the parser correctly.
As Damien_The_Unbeliever points out, within the SelectStatement there is a QueryExpression property which will be a QuerySpecification object for your third select statement (with the WHERE clause).
This represents the 'real' SELECT bit of the query (whereas the outer SelectStatement object you are looking at has just got the 'WITH' clause (for CTEs), 'FOR' clause (for XML), 'ORDER BY' and other bits)
The QuerySpecification object is the object with the FromClauses, WhereClause, GroupByClause etc.
So you can get to your WHERE Clause by using:
((QuerySpecification)((SelectStatement)statement).QueryExpression).WhereClause
which has a SearchCondition property etc. etc.
Quick glance around would indicate that it contains a QueryExpression, which could be a QuerySpecification, which does have the Where clause attached to it.
if someone lands here and wants to know how to get the whole elements of a select statement the following code explain that:
QuerySpecification spec = (QuerySpecification)(((SelectStatement)st).QueryExpression);
StringBuilder sb = new StringBuilder();
sb.AppendLine("Select Elements");
foreach (var elm in spec.SelectElements)
sb.Append(((Identifier)((Column)((SelectColumn)elm).Expression).Identifiers[0]).Value);
sb.AppendLine();
sb.AppendLine("From Elements");
foreach (var elm in spec.FromClauses)
sb.Append(((SchemaObjectTableSource)elm).SchemaObject.BaseIdentifier.Value);
sb.AppendLine();
sb.AppendLine("Where Elements");
BinaryExpression binaryexp = (BinaryExpression)spec.WhereClause.SearchCondition;
sb.Append("operator is " + binaryexp.BinaryExpressionType);
if (binaryexp.FirstExpression is Column)
sb.Append(" First exp is " + ((Identifier)((Column)binaryexp.FirstExpression).Identifiers[0]).Value);
if (binaryexp.SecondExpression is Literal)
sb.Append(" Second exp is " + ((Literal)binaryexp.SecondExpression).Value);
I had to split a SELECT statement into pieces. My goal was to COUNT how many record a query will return. My first solution was to build a sub query such as
SELECT COUNT(*) FROM (select id, name from T where cat='A' order by id) as QUERY
The problem was that in this case the order clause raises the error "The ORDER BY clause is not valid in views, inline functions, derived tables, sub-queries, and common table expressions, unless TOP or FOR XML is also specified"
So I built a parser that split a SELECT statment into fragments using the TSql100Parser class.
using Microsoft.Data.Schema.ScriptDom.Sql;
using Microsoft.Data.Schema.ScriptDom;
using System.IO;
...
public class SelectParser
{
public string Parse(string sqlSelect, out string fields, out string from, out string groupby, out string where, out string having, out string orderby)
{
TSql100Parser parser = new TSql100Parser(false);
TextReader rd = new StringReader(sqlSelect);
IList<ParseError> errors;
var fragments = parser.Parse(rd, out errors);
fields = string.Empty;
from = string.Empty;
groupby = string.Empty;
where = string.Empty;
orderby = string.Empty;
having = string.Empty;
if (errors.Count > 0)
{
var retMessage = string.Empty;
foreach (var error in errors)
{
retMessage += error.Identifier + " - " + error.Message + " - position: " + error.Offset + "; ";
}
return retMessage;
}
try
{
// Extract the query assuming it is a SelectStatement
var query = ((fragments as TSqlScript).Batches[0].Statements[0] as SelectStatement).QueryExpression;
// Constructs the From clause with the optional joins
from = (query as QuerySpecification).FromClauses[0].GetString();
// Extract the where clause
where = (query as QuerySpecification).WhereClause.GetString();
// Get the field list
var fieldList = new List<string>();
foreach (var f in (query as QuerySpecification).SelectElements)
fieldList.Add((f as SelectColumn).GetString());
fields = string.Join(", ", fieldList.ToArray());
// Get The group by clause
groupby = (query as QuerySpecification).GroupByClause.GetString();
// Get the having clause of the query
having = (query as QuerySpecification).HavingClause.GetString();
// Get the order by clause
orderby = ((fragments as TSqlScript).Batches[0].Statements[0] as SelectStatement).OrderByClause.GetString();
}
catch (Exception ex)
{
return ex.ToString();
}
return string.Empty;
}
}
public static class Extension
{
/// <summary>
/// Get a string representing the SQL source fragment
/// </summary>
/// <param name="statement">The SQL Statement to get the string from, can be any derived class</param>
/// <returns>The SQL that represents the object</returns>
public static string GetString(this TSqlFragment statement)
{
string s = string.Empty;
if (statement == null) return string.Empty;
for (int i = statement.FirstTokenIndex; i <= statement.LastTokenIndex; i++)
{
s += statement.ScriptTokenStream[i].Text;
}
return s;
}
}
And to use this class simply:
string fields, from, groupby, where, having, orderby;
SelectParser selectParser = new SelectParser();
var retMessage = selectParser.Parse("SELECT * FROM T where cat='A' Order by Id desc",
out fields, out from, out groupby, out where, out having, out orderby);