JPQL: Parameter with that position [1] did not exist - jpa

I am working with JPA and JPQL. Using a JPQL I would like to fetch join a collection that is an attribute of a "main" entity rent. Here is my source code:
public Rent getRentWithAllDetails(Rent rent) {
Query queryString = em.createQuery(" select r from Rent r JOIN FETCH r.rentables where r.id = :rid").setParameter(1, rent.getId());
List <Rent> resultList = queryString.getResultList();
return resultList.get(0);
}
and this is the exception I recieve:
Caused by: java.lang.IllegalArgumentException: Parameter with that position [1] did not exist
at org.hibernate.jpa.spi.BaseQueryImpl.findParameterRegistration(BaseQueryImpl.java:518) [hibernate-entitymanager-4.3.7.Final.jar:4.3.7.Final]
at org.hibernate.jpa.spi.BaseQueryImpl.setParameter(BaseQueryImpl.java:674) [hibernate-entitymanager-4.3.7.Final.jar:4.3.7.Final]
at org.hibernate.jpa.spi.AbstractQueryImpl.setParameter(AbstractQueryImpl.java:198) [hibernate-entitymanager-4.3.7.Final.jar:4.3.7.Final]
at org.hibernate.jpa.spi.AbstractQueryImpl.setParameter(AbstractQueryImpl.java:49) [hibernate-entitymanager-4.3.7.Final.jar:4.3.7.Final]
Could someone help me, please?

You are using a named parameter, so you should bind a parameter by that name while creating your query:
String sql = "select r from Rent r JOIN FETCH r.rentables where r.id = :rid";
Query queryString = em.createQuery(sql)
.setParameter("rid", rent.getId());
List<Rent> resultList = queryString.getResultList();

Please change your code to:
public Rent getRentWithAllDetails(Rent rent) {
Query queryString = em.createQuery(" select r from Rent r JOIN FETCH r.rentables where r.id = :rid").setParameter("rid", rent.getId());
List <Rent> resultList = queryString.getResultList();
return resultList.get(0);
}
if rid is repeated multiple times, you can use 0,1..etc accordingly, otherwise use the parameter name itself.

Related

How to avoiding AND condition if parameter is null in Spring Data JPA query

I am trying to get the result of one query using Spring Data JPA. Here I am sending some parameter and receiving result according to that.
My repository query is,
#Query("select u.username,p.pname from Users u join u.priviJoin p where u.username = :uname AND p.pname = :pname")
List<Users> findByUsername(#Param("uname") String uname , #Param("pname") String pname );
And calling from controller like the following,
#RequestMapping(value = "/joinResult", method = RequestMethod.GET)
public List<Users> joinResultShow()
{
return (List<Users>) userRepo.findByUsername("test_user","testRole");
}
Here we can see that if I am passing some value then only checking according to that parameter. Here I need to modify my query like if parameter is null, then not need to use AND condition in query.
How can I modify this query for avoiding AND condition if parameter is null? I am new to Spring Data JPA world.
Here are some possible options for you
1. Create multiple methods in your repository like
#Query("select u.username,p.pname from Users u join u.priviJoin p where u.username = :uname AND p.pname = :pname")
List<Users> findByusernamewithRole(#Param("uname") String uname , #Param("pname") String pname );
#Query("select u.username,p.pname from Users u join u.priviJoin p where u.username = :uname")
List<Users> findByUsernameWithoutRole(#Param("uname") String uname);
Write a custom respository and use EntityManager. With this you can create a dynamic queries based on your input using CriteriaBuilder and use this criteria in querying.
Last and the most preferred option in case of dynamic inputs(like you have) is Querydsl.
Some articles about querydsl
http://www.baeldung.com/querydsl-with-jpa-tutorial
http://www.querydsl.com/static/querydsl/latest/reference/html/ch02.html

JPQL order by on map value?

With a collection in a JPA object like:
#ElementCollection
private Map<String, Double> fitValue = new HashMap<String, Double>();
Is there a JPQL syntax that lets me order by the value of a particular Map index?
SELECT o.individual.id FROM IndividualJob AS o JOIN o.fitValue f WHERE o.job.id = :id AND o.job.dtype = :type AND KEY(f) = '/' ORDER BY VALUE(f)
parses fine but throws a NPE trying to fill-in the order by:
Caused by: java.lang.NullPointerException
at org.eclipse.persistence.mappings.ForeignReferenceMapping.getOrderByNormalizedExpressions(ForeignReferenceMapping.java:2505)
at org.eclipse.persistence.internal.expressions.SQLSelectStatement.normalizeOrderBy(SQLSelectStatement.java:1639)
at org.eclipse.persistence.internal.expressions.SQLSelectStatement.normalize(SQLSelectStatement.java:1428)
at org.eclipse.persistence.internal.queries.ExpressionQueryMechanism.buildReportQuerySelectStatement(ExpressionQueryMechanism.java:642)
at org.eclipse.persistence.internal.queries.ExpressionQueryMechanism.buildReportQuerySelectStatement(ExpressionQueryMechanism.java:587)
at org.eclipse.persistence.internal.queries.ExpressionQueryMechanism.prepareReportQuerySelectAllRows(ExpressionQueryMechanism.java:1696)
at org.eclipse.persistence.queries.ReportQuery.prepareSelectAllRows(ReportQuery.java:1207)
at org.eclipse.persistence.queries.ReadAllQuery.prepare(ReadAllQuery.java:798)
at org.eclipse.persistence.queries.ReportQuery.prepare(ReportQuery.java:1075)
at org.eclipse.persistence.queries.DatabaseQuery.checkPrepare(DatabaseQuery.java:666)
Close but not quite.
Thanks.
This is one solution to the problem using a nativeQuery:
"select x.id from (select individual_id id, fitvalue fit from individualjob i join individualjob_fitvalue f on i.id = f.individualjob_id and i.job_id = ? and f.fitvalue_key = '/') as x order by x.fit desc"
Luckily I only needed the id's and not the entire object.

JPA NamedQuery get distinct column

I want to get an list of distinct City's from my User table. I thought the code below would work but gives an error:
User.java
#Entity
#Table(name="user_info")
...
#NamedQuery(name = "User.all.cities", query = "SELECT distinct u.city FROM User u"),
...
#embedded
private City city;
UserBusinessLogic.java:
...
TypedQuery<City> typedQuery = entityManager.createNamedQuery("User.all.cities",User.class);
List<City> names = typedQuery.getResultList();
...
It gives: type mismatch can not convert List to List. I tried two get first user then on getResult a City but same error one line below.
I see some examples but not really tell how to get it with correct code just the SQL syntax.
Thanks for your help
Jess
The first thing which doesn't look good is that you ask for City objects but declare to get User's.
You have:
#NamedQuery(name = "User.all.cities",
query = "SELECT distinct u.city FROM User u"),
TypedQuery<City> typedQuery =
entityManager.createNamedQuery("User.all.cities", User.class);
Where it should be:
TypedQuery<City> typedQuery =
entityManager.createNamedQuery("User.all.cities", City.class);
The code that works is:
results = getJpaTemplate().execute(new JpaCallback<List<City>>() {
#Override
public List<City> doInJpa(EntityManager em) throws PersistenceException {
TypedQuery<City> query = em.createNamedQuery("User.all.cities", City.class);
return query.getResultList() ;
}
}) ;

How do I count the number of rows returned by subquery?

I want to do something like this:
select count(*) from (select ...)
(As it would be in SQL), but in JPA.
Any ideas on how I would do it?
I stumbled upon this issue as well. I would ultimately like to execute the following JPQL:
SELECT COUNT(u)
FROM (
SELECT DISTINCT u
FROM User u
JOIN u.roles r
WHERE r.id IN (1)
)
But this wasn't possible, also not with criteria API. Research taught that this was just a design limitation in JPA. The JPA spec states that subqueries are only supported in WHERE and HAVING clauses (and thus not in the FROM).
Rewriting the query in the following JPQL form:
SELECT COUNT(u)
FROM User u
WHERE u IN (
SELECT DISTINCT u
FROM User u
JOIN u.roles r
WHERE r.id IN (1)
)
using the JPA Criteria API like as follows:
CriteriaQuery<Long> query = cb.createQuery(Long.class);
Root<User> u = query.from(User.class);
Subquery<User> subquery = query.subquery(User.class);
Root<User> u_ = subquery.from(User.class);
subquery.select(u_).distinct(true).where(u_.join("roles").get("id").in(Arrays.asList(1L)));
query.select(cb.count(u)).where(cb.in(u).value(subquery));
Long count = entityManager.createQuery(query).getSingleResult();
// ...
has solved the functional requirement for me. This should also give you sufficient insight into solving your particular functional requirement.
This should do the trick (If you want to use JPA criteria API):
CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
CriteriaQuery<Long> query = cb.createQuery(Long.class);
Root<Entity> root = query.from(Entity.class);
//Selecting the count
query.select(cb.count(root));
//Create your search criteria
Criteria criteria = ...
//Adding search criteria
query.where(criteria);
Long count = getEntityManager().createQuery(query).getSingleResult();
On the other hand, if you want to use JP-QL, the following code should do the trick:
//Add the where condition to the end of the query
Query query = getEntityManager().createQuery("select count(*) from Entity entity where...")
Long count = query.getSingleResult();
Use the following snippet to count rows for a given Criteria Query:
public static Query createNativeCountQuery(EntityManager em, CriteriaQuery<?> criteriaQuery) {
org.hibernate.query.Query<?> hibernateQuery = em.createQuery(criteriaQuery).unwrap(org.hibernate.query.Query.class);
String hqlQuery = hibernateQuery.getQueryString();
QueryTranslatorFactory queryTranslatorFactory = new ASTQueryTranslatorFactory();
QueryTranslator queryTranslator = queryTranslatorFactory.createQueryTranslator(
hqlQuery,
hqlQuery,
Collections.emptyMap(),
em.getEntityManagerFactory().unwrap(SessionFactoryImplementor.class),
null
);
queryTranslator.compile(Collections.emptyMap(), false);
String sqlCountQueryTemplate = "select count(*) from (%s)";
String sqlCountQuery = String.format(sqlCountQueryTemplate, queryTranslator.getSQLString());
Query nativeCountQuery = em.createNativeQuery(sqlCountQuery);
Map<Integer, Object> positionalParamBindings = getPositionalParamBindingsFromNamedParams(hibernateQuery);
positionalParamBindings.forEach(nativeCountQuery::setParameter);
return nativeCountQuery;
}
private static Map<Integer, Object> getPositionalParamBindingsFromNamedParams(org.hibernate.query.Query<?> hibernateQuery) {
Map<Integer, Object> bindings = new HashMap<>();
for (var namedParam : hibernateQuery.getParameterMetadata().getNamedParameters()) {
for (int location : namedParam.getSourceLocations()) {
bindings.put(location + 1, hibernateQuery.getParameterValue(namedParam.getName()));
}
}
return bindings;
}

JPA 2.0, Criteria API, Subqueries, In Expressions

I have tried to write a query statement with a subquery and an IN expression for many times. But I have never succeeded.
I always get the exception, " Syntax error near keyword 'IN' ", the query statement was build like this,
SELECT t0.ID, t0.NAME
FROM EMPLOYEE t0
WHERE IN (SELECT ?
FROM PROJECT t2, EMPLOYEE t1
WHERE ((t2.NAME = ?) AND (t1.ID = t2.project)))
I know the word before 'IN' lose.
Have you ever written such a query? Any suggestion?
Below is the pseudo-code for using sub-query using Criteria API.
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Object> criteriaQuery = criteriaBuilder.createQuery();
Root<EMPLOYEE> from = criteriaQuery.from(EMPLOYEE.class);
Path<Object> path = from.get("compare_field"); // field to map with sub-query
from.fetch("name");
from.fetch("id");
CriteriaQuery<Object> select = criteriaQuery.select(from);
Subquery<PROJECT> subquery = criteriaQuery.subquery(PROJECT.class);
Root fromProject = subquery.from(PROJECT.class);
subquery.select(fromProject.get("requiredColumnName")); // field to map with main-query
subquery.where(criteriaBuilder.and(criteriaBuilder.equal("name",name_value),criteriaBuilder.equal("id",id_value)));
select.where(criteriaBuilder.in(path).value(subquery));
TypedQuery<Object> typedQuery = entityManager.createQuery(select);
List<Object> resultList = typedQuery.getResultList();
Also it definitely needs some modification as I have tried to map it according to your query. Here is a link http://www.ibm.com/developerworks/java/library/j-typesafejpa/ which explains concept nicely.
Late resurrection.
Your query seems very similar to the one at page 259 of the book Pro JPA 2:
Mastering the Java Persistence API, which in JPQL reads:
SELECT e
FROM Employee e
WHERE e IN (SELECT emp
FROM Project p JOIN p.employees emp
WHERE p.name = :project)
Using EclipseLink + H2 database, I couldn't get neither the book's JPQL nor the respective criteria working. For this particular problem I have found that if you reference the id directly instead of letting the persistence provider figure it out everything works as expected:
SELECT e
FROM Employee e
WHERE e.id IN (SELECT emp.id
FROM Project p JOIN p.employees emp
WHERE p.name = :project)
Finally, in order to address your question, here is an equivalent strongly typed criteria query that works:
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Employee> c = cb.createQuery(Employee.class);
Root<Employee> emp = c.from(Employee.class);
Subquery<Integer> sq = c.subquery(Integer.class);
Root<Project> project = sq.from(Project.class);
Join<Project, Employee> sqEmp = project.join(Project_.employees);
sq.select(sqEmp.get(Employee_.id)).where(
cb.equal(project.get(Project_.name),
cb.parameter(String.class, "project")));
c.select(emp).where(
cb.in(emp.get(Employee_.id)).value(sq));
TypedQuery<Employee> q = em.createQuery(c);
q.setParameter("project", projectName); // projectName is a String
List<Employee> employees = q.getResultList();
CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery<Employee> criteriaQuery = criteriaBuilder.createQuery(Employee.class);
Root<Employee> empleoyeeRoot = criteriaQuery.from(Employee.class);
Subquery<Project> projectSubquery = criteriaQuery.subquery(Project.class);
Root<Project> projectRoot = projectSubquery.from(Project.class);
projectSubquery.select(projectRoot);
Expression<String> stringExpression = empleoyeeRoot.get(Employee_.ID);
Predicate predicateIn = stringExpression.in(projectSubquery);
criteriaQuery.select(criteriaBuilder.count(empleoyeeRoot)).where(predicateIn);
You can use double join, if table A B are connected only by table AB.
public static Specification<A> findB(String input) {
return (Specification<A>) (root, cq, cb) -> {
Join<A,AB> AjoinAB = root.joinList(A_.AB_LIST,JoinType.LEFT);
Join<AB,B> ABjoinB = AjoinAB.join(AB_.B,JoinType.LEFT);
return cb.equal(ABjoinB.get(B_.NAME),input);
};
}
That's just an another option
Sorry for that timing but I have came across this question and I also wanted to make SELECT IN but I didn't even thought about double join.
I hope it will help someone.