I'm currently working with a JPA project from my university. I'm having problems with a Query, especifically a Method on a #Service class that receives numerous parameters.
First of all, I'm trying just to make the query work with a two parameters.
Here is the method.
#Override
#Transactional ( readOnly = true )
public List<Candidate> obtainCandidatesOps(String name, String firstName,
String secondName, String passport, CandidateType candidateType,
Area area, Country country) {
List<Candidate> resultList = new ArrayList<Candidate>();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Candidate> cq = cb.createQuery(Candidate.class);
Root<Candidate> r = cq.from(Candidate.class);
cq.where(cb.and(cb.equal(r.get("name"), cb.parameter(String.class, "name")),cb.equal(r.get("firstLastName"), cb.parameter(String.class, "firstName"))));
cq.select(r);
resultList = em.createQuery(cq).getResultList();
return resultList;
}
While I try to execute this Query I receive this error:
WARNING: #{searchBean.fillCandidatesOps}: java.lang.IllegalArgumentException: org.hibernate.QueryException: Not all named parameters have been set: [firstName, name] [select generatedAlias0 from Candidate as generatedAlias0 where ( generatedAlias0.name=:name) and ( generatedAlias0.firstLastName=:firstName)]
Where is the mistake/problem?
I've read this topics, and found no solution to my problem.
Caused by: org.hibernate.QueryException: Not all named parameters have been set: [isActive] [from User where isActive = :isActive]
How to solve "org.hibernate.QueryException: Not all named parameters have been set" error?
As the 2 linked threads tell you clear enough (so no idea why you have "looked at them for 5 days" and not noticed this), you need to set the VALUE of the parameters that you have defined in that query.
cq.where(cb.and(cb.equal(r.get("name"), cb.parameter(String.class, "name")),cb.equal(r.get("firstLastName"), cb.parameter(String.class, "firstName"))));
cq.select(r);
Query query = em.createQuery(cq);
query.setParameter("name", name);
query.setParameter("firstName", firstName);
resultList = query.getResultList();
Related
I have two classes, Account and Admin, with many to many mapping.
The Admin class has a collection of Account class and vise versa.
I want to write a query, that given the account id, will return all the account admins.
Here is the relevant fields of the Account class:
#Entity
public class Account {
#Id
public Long id;
#ManyToMany(mappedBy = "account", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
public List<Admin> users = new ArrayList<>();
}
I have tried a regular query for Admin.class with multiselect as each account has a collection of admins, but trying to get a TypedQuery<Admin> out of my CriteriaQuery<Admin> I got an IllegalArgumentException with the message "org.hibernate.hql.internal.ast.QuerySyntaxException: Unable to locate appropriate constructor on class [models.Admin]. Expected arguments are: java.util.Collection [select new models.Admin(generatedAlias0.users) from models.Account as generatedAlias0 where generatedAlias0.id=1L]" (1L here probably since I called this function with 1 as accountId), caused by QuerySyntaxException with the message "Unable to locate appropriate constructor on class [models.Admin]. Expected arguments are: java.util.Collection".
Code:
private static List<Admin> readAccountAdmins(Long accountId) {
CriteriaBuilder cb = JPA.em().getCriteriaBuilder();
CriteriaQuery<Admin> cq = cb.createQuery(Admin.class);
Root<Account> root = cq.from(Account.class);
Predicate idPredicate = cb.equal(root.get(Account_.id), accountId);
cq.multiselect(root.get(Account_.users)).where(idPredicate);
TypedQuery<Admin> typedQuery = JPA.em().createQuery(cq); // exception thrown here
return typedQuery.getResultList();
}
After that I tried running a TypedQuery<List<Admin>>, as I am trying to read a list. This is the first iteration of trying a query of list:
private static List<Admin> readAccountAdmins(Long accountId) {
CriteriaBuilder cb = JPA.em().getCriteriaBuilder();
CriteriaQuery<List<Admin>> cq = cb.createQuery((Class<List<Admin>>)(Class<?>)(Collection.class));
Root<Account> root = cq.from(Account.class);
Predicate idPredicate = cb.equal(root.get(Account_.id), accountId);
cq.select(root.get(Account_.users)).where(idPredicate);
TypedQuery<List<Admin>> typedQuery = JPA.em().createQuery(cq);
return typedQuery.getSingleResult(); // exception thrown here
}
I used getSingleResult as getResultList caused a compilation error, saying the actual return value is List<List<Admin>>> and doesn't match the signature.
This method threw a different exception, a NonUniqueResultException with the message: "result returns more than one elements".
While debugging, I tried to evaluate the expression typedQuery.getResultList() and saw that it actually returns List<Admin> and not List<List<Admin>>, so I got to my final iteration of this function:
private static List<Admin> readAccountAdmins(Long accountId) {
CriteriaBuilder cb = JPA.em().getCriteriaBuilder();
CriteriaQuery<List<Admin>> cq = cb.createQuery((Class<List<Admin>>)(Class<?>)(Collection.class));
Root<Account> root = cq.from(Account.class);
Predicate idPredicate = cb.equal(root.get(Account_.id), accountId);
cq.select(root.get(Account_.users)).where(idPredicate);
TypedQuery<List<Admin>> typedQuery = JPA.em().createQuery(cq);
return (List) typedQuery.getResultList();
}
Now, this function works, but my question is why?
Why did the compiler decide that getResultList returns a different value than the actual return value?
Maybe it makes sense when you take a closer look at your database. A TypeQuery returns entities, so basically rows from tables. List<Admin> is a collection of Entities, so eventhough your Account has a List<Admin> as a field, the Query will still return List<Admin> entities, not List<List<Admin>> as List<Admin> is not an entity.
I hope that makes sense.
I have the following JPA entities
#Entity
#Table(name="application_user")
public class ApplicationUser {
#Id
#Column(name="user_id")
private String userid;
#Column(name="last_write_time")
private Instant lastWrite;
//other fields omitted
}
#Entity
#Table(name="demographic")
public class Demographic {
#Id
#Column(name="user_id")
private String userid;
//primary key is a foreign key link
#OneToOne
#PrimaryKeyJoinColumn(name="user_id", referencedColumnName="user_id")
private ApplicationUser user;
//other fields omitted
}
My goal is to retrieve all of the Demographics that contains users where the last write time is the max value in the column. I pretty much want to write the following SQL using the JPA CriteriaBUilder
select * from demographic where
userid in (
select userid from application_user where
last_write in (
select max(last_write) from application_user
)
)
I tried writing the following CriteriaBuilder Code to accomplish this goal and it compiles successfully. Note I am using the generated Metamodel classes.
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Demographic> c = cb.createQuery(Demographic.class);
Root<Demographic> root = c.from(Demographic.class);
root.fetch(Demographic_.user, JoinType.INNER);
Subquery<Instant> sqLatestUsers = c.subquery(Instant.class);
Root<ApplicationUser> subRootLatestUsers = sqLatestUsers.from(ApplicationUser.class);
sqLatestUsers.select(cb.greatest(subRootLatestUsers.<Instant>get(ApplicationUser_.LAST_WRITE)));
Predicate predicateLatestUsers = subRootLatestUsers.get(ApplicationUser_.LAST_WRITE).in(sqLatestUsers);
Subquery<ApplicationUser> sq = c.subquery(ApplicationUser.class);
Root<Demographic> subRoot = sq.from(Demographic.class);
sq.select(subRoot.<ApplicationUser>get(Demographic_.USER)).where(predicateLatestUsers);
Predicate containsUsers = subRoot.get(Demographic_.USER).in(sq);
c.select(root).where(containsUsers);
The code compiles and successfully deploys in Wildfly 14, but when I execute the code, the get the following error (with white space to improve readability):
Invalid path: 'generatedAlias2.user' : Invalid path: 'generatedAlias2.user'
...
Caused by: org.hibernate.hql.internal.ast.QuerySyntaxException: Invalid path: 'generatedAlias2.user' [
select generatedAlias0 from com.company.model.Demographic as generatedAlias0
inner join fetch generatedAlias0.user as generatedAlias1
where generatedAlias2.user in (
select generatedAlias2.user from com.company.model.Demographic as generatedAlias2 where generatedAlias3.lastWrite in (
select max(generatedAlias3.lastWrite) from com.company.model.StarfishUser as generatedAlias3
)
)
]
Is chaining subqueries (nested subqueries) allowed by the JPA spec? Did I find something that is syntactically correctly but not actually allowed?
I figure out how to get the subquery to work. First is my updated Utility method
public static <R, T> Subquery<T> getLatestSubelement(CriteriaBuilder cb, CriteriaQuery<R> c, Class<T> clazz, SingularAttribute<T, Instant> attribute) {
//Get latest timestamp
Subquery<Instant> sq = c.subquery(Instant.class);
Root<T> subRoot = sq.from(clazz);
sq.select(cb.greatest(subRoot.<Instant>get(attribute)));
//Get object with the latest timestamp
Subquery<T> sq2 = c.subquery(clazz);
Root<T> subRoot2 = sq2.from(clazz);
sq2.where(subRoot2.get(attribute).in(sq));
return sq2;
}
Here is the code that uses the utility method
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Demographic> c = cb.createQuery(Demographic.class);
Root<Demographic> root = c.from(Demographic.class);
joinType = JoinType.INNER;
//use fetch instead of join to prevent duplicates in Lists
root.fetch(Demographic_.user, joinType);
Subquery<ApplicationUser> sq = JpaUtil.getLatestSubelement(cb, c, ApplicationUser.class, ApplicationUser_.lastWrite);
c.where(root.get(Demographic_.user).in(sq));
TypedQuery<Demographic> q = em.createQuery(c);
Stream<Demographic> stream = q.getResultStream();
I have a PagingAndSorting JPA repository declared. I am using the #Query annotation.
I am getting an exception when I call the get() method on an Optional object from the findById(id) method of the repository.
The weird thing is it only happens when I use JPQL.
The code works if my query is native:
#Override
public BatchDto findById(String id) {
Optional<Batch> findResult = this.batchRepository.findById(id);
if (!findResult.isPresent()) return null;
Batch entity = findResult.get(); **<-------- Cast Exception Here**
BatchDto dto = this.mapper.toDto(entity, BatchDto.class);
List<BatchTransaction> transactions = entity.getTransactions();
dto.setTransactionDtos(mapper.toListDto(transactions, TransactionDto.class));
return dto;
}
Inspecting the findResult object with a breakpoint - I can see:
Optional[net.domain.data.batch#4b8bb6f]
when I have nativeQuery = true in the #Query annotation.
#Query(value = Sql.FindBatchById, nativeQuery = true)
Here is the query being used:
SELECT DISTINCT(B.batchNumber), COUNT(B.batchNumber) as TransactionCount FROM BATCH B WHERE B.batchReferenceNumber = :id GROUP BY B.batchNumber
However if I change it to JPQL and remove the nativeQuery=true attribute - the findResult is
Optional[[Ljava.lang.Object;#76e04327].
and I get a ClassCastException:
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to net.domain.data.batch
So bottom line - this works when specify nativeQuery=true and fails when I try to use JPQL.
I would prefer not to specify nativeQuery as we will eventually port this db to Oracle.
First of all the query shown below doesn't return a single Batch instance. Since there are distinct and count aggregate functions, the query will return a List of aggregates.
To be able to read that statistics you can add appropriate method into the batchRepository. Something like this:
#Query("SELECT DISTINCT(B.batchNumber) as dist, COUNT(B.batchNumber) as cnt FROM BATCH B GROUP BY B.batchNumber")
List<Map<Long, Long>> findStatistics();
and then iterate through the list.
UPD
If the id parameter exactly guarantee that will return a single record, you can change a return type to a Map
#Query("SELECT DISTINCT(B.batchNumber) as dist, COUNT(B.batchNumber) as cnt FROM BATCH B WHERE B.batchReferenceNumber = :id GROUP BY B.batchNumber")
Map<Long, Long> findStatisticsById(#Param("id") Long id);
I am trying to return a string from a table based on a conditional ID (subid) from an already populated table. The query should return a list of type ItemDataPoint entity. In a JSF managed bean, the list will the be iterated by a an enhaned for loop. If the word "Include" is found by the loop, the method will create a specific type of chart. In simpler terms, I want to return a string based the ID condition being met. I am getting:
javax.ejb.EJBException
at com.sun.ejb.containers.EJBContainerTransactionManager.processSystemException
(EJBContainerTransactionManager.java:748)
at com.sun.ejb.containers.EJBContainerTransactionManager.
completeNewTx(EJBContainerTransactionManager.java:698)
at com.sun.ejb.containers.EJBContainerTransactionManager.postInvokeTx
(EJBContainerTransactionManager.java:503)
at com.sun.ejb.containers.BaseContainer.postInvokeTx(BaseContainer.java:4475)
at com.sun.ejb.containers.BaseContainer.postInvoke(BaseContainer.java:2009)
at com.sun.ejb.containers.BaseContainer.postInvoke(BaseContainer.java:1979)
Caused by: java.lang.IllegalArgumentException: You have attempted to set
a parameter at position 2 which does not exist in this query string SELECT p FROM
Itemdatapoint p JOIN p.series s WHERE s.master.item.subs.subid = :subid.
at org.eclipse.persistence.internal.jpa.QueryImpl.setParameterInternal(QueryImpl.java:925)
at org.eclipse.persistence.internal.jpa.QueryImpl.setParameterInternal(QueryImpl.java:906)
at org.eclipse.persistence.internal.jpa.EJBQueryImpl.setParameter(EJBQueryImpl.java:469)
at org.eclipse.persistence.internal.jpa.EJBQueryImpl.setParameter(EJBQueryImpl.java:1)
at com.manaar.clientmods.gazprom.design3.data.facade.ItemdatapointFacade.
chartType(ItemdatapointFacade.java:78)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
The subid value of 2 exists if I run a native SQL query on the relevant parent table in the DB. Also the type of the subid is an int in the main entity class, the JPQL Facade class and the managed bean.
The JPQL:
public List<Itemdatapoint> chartType (int subid) {
Query q = em.createQuery("SELECT p FROM Itemdatapoint p JOIN p.series s WHERE s.master.item.subs.subid = :subid");
q.setParameter(subid, "subid");
return q.getResultList();
}
The managed bean:
#Named(value = "reportBean")
#SessionScoped
public class ReportBean implements Serializable {
#Inject
private ItemdatapointFacade facade;
public String typeSwitch1() {
subid = 2;
chartType = facade.chartType(subid);
for(Itemdatapoint e: chartType) {
status = e.getSeries().getMaster().getStatus();
if(status.equals("Include")) {
return "line";
}
}
return null;
}
The xhtml page:
<p:chart type="#{reportBean.typeSwitch1()}" model="#{reportBean.subLineChart1}"/>
I also tried a non Join JPQL just from a single table:
public List<Itemdatapoint> noJoin (int subid) {
Query q = em.createQuery("SELECT p FROM Itemdatapoint p WHERE p.pointid = :subid");
q.setParameter(subid, "subid");
return q.getResultList();
}
Similar problem:
java.lang.IllegalArgumentException: You have attempted to set a
parameter at position 2 which does not exist in this query string
SELECT p FROM Itemdatapoint p WHERE p.pointid = :subid.
I gather that IllegalArgumentException means that the selected entity does not exist or is not the correct type consistent with the query string in the facade class. But in my case the entity exists and the parameter is the correct type.
I would appreciate any help in understanding why i'm getting this error. Thank in advance!
UPDATE
Responding to the answer from lametaweb, I want to better understand the concept of JPA parameters.
According to the JPA documentation, the first argument of the setParameter method is the parameter name or number. The second argument is the object that should be bound to the named parameter. Why does the following work without throwing Illegal ArgumentException?
I tested an xhtml (web page):
<p:dataGrid id="rep1" columns="1" value="#{pageBean.itemPageList1}" var="items1" rows="4">
<p:commandLink value="#{items1.itemname}" action="#{pageBean.showItem1}" ajax="false"/>
</p:dataGrid>
The bean code:
public ListDataModel<Sectionitem> getItemPageList1() {
subid = 1;
reportStatus = "Include";
itemPageList1 = itemFacade.viewItems(subid, reportStatus);
return itemPageList1;
}
The JPA facade:
public ListDataModel<Sectionitem> viewItems(int subid, String stat) {
Query q = em.createQuery("select s from Sectionitem s JOIN s.subs c where c.subid = :subid AND s.status = :stat ORDER BY s.daterec");
q.setParameter("subid", subid);
q.setParameter("stat", stat);
ListDataModel<Sectionitem> res
= new ListDataModel<Sectionitem>(q.getResultList());
return res;
}
Why is it in this case, the object exist but in my original case the subid object does not exist?
You are invoking this method in your code:
setParameter(int position, Object value)
but you have to invoke this one instead:
setParameter(String name, Object value)
So your code should be:
q.setParameter("subid", Integer.valueOf(subid));
But, if you invoke:
q.setParameter(subid, "subid");
here the first parameter represents the position of the argument and the second the value for it. So you are passing a value of "subid" for the parameter in the second (2) position, which doesn't exist, because you only have one parameter in your JPQL query, hence the IllegalArgumentException exception.
Note: Why do you have a primitive type in your entity? Why not an Integer instead an int?
I am using the following JPA query and i am getting the java.lang.IllegalArgumentException: Cannot create TypedQuery for query with more than one return Exception.
TypedQuery<RaBdrRating> uQuery =
(TypedQuery<RaBdrRating>)entityManager.createQuery("
SELECT r.activePackage,SUM(r.duration),SUM(r.charge),COUNT(r)
FROM RaBdrRating r WHERE r.callType = :callType
and r.startDate between :startDate and :endDate
GROUP BY r.activePackage",RaBdrRating.class);
uQuery.setParameter("callType", model.getCallType());
uQuery.setParameter("startDate",startDate);
uQuery.setParameter("endDate",endDate);
List<RaBdrRating> listOfPackages = uQuery.getResultList();
Can any one tell me what is wrong in my query.....I am new to JPA and i am not getting what is the problem and strucked up here.If any one have idea please tell me.
I go the same error:
Cannot create TypedQuery for query with more than one return using requested result type
Solution:
Having a Query like this:
Select e.fieldA, e.fieldB, e.fieldC From Entity e
You have to declare a constructor with the parameters specified on query:
package somepackage;
public class Entity {
...
public class Entity() {}
public class Entity(Type fieldA, Type fieldB, Type fieldC) {
this.fieldA = fieldA;
this.fieldB = fieldB;
this.fieldC = fieldC;
}
....
}
Finally, modify your query
Select NEW somepackage.Entity(e.fieldA, e.fieldB, e.fieldC) From Entity e
You are indicating how the objectes will be created.
This seems to be this bug: https://hibernate.onjira.com/browse/HHH-6304
It is apparently fixed in version 4.1.5.