I am trying to implement a generic class (GenericDaoImpl<T,E>
) which contains a method that executes NamedQueries (findByNamedQuery(..)
). This method asks for a named query name and and an object array (parameters). I must iterate them and bind these parameters to the named query.
I can not change the method signature.
public abstract class GenericDaoImpl<T, E> implements GenericDao<T, E> {
@PersistenceContext(unitName="MyUnit")
protected EntityManager em;
private Class<T> persistentClass;
@SuppressWarnings("unchecked")
public GenericDaoImpl() {
this.persistentClass = getPersistentClass();
}
public void setPersistentClass(Class<T> persistentClass) {
this.persistentClass = persistentClass;
}
public Class<T> getPersistentClass() {
Class<?>[] typeArguments = TypeResolver.resolveRawArguments(GenericDao.class, getClass());
this.persistentClass = (Class<T>) typeArguments[0];
return persistentClass;
}
....
@Transactional
public void save(T entity) {
em.persist(entity);
}
@Transactional
public void update(T entity) {
em.merge(entity);
}
@Transactional
public void delete(E id) {
em.remove(em.find(persistentClass,id));
}
...
public List<T> findByNamedQuery(String queryName, Object... params) {
Query q = this.em.createNamedQuery(queryName,persistentClass);
for(int i =1;i<params.length-1;i++){
q.setParameter(i, params[i]);
}
return (List<T>)q.getResultList();
}
}
Now, given this named query:
@NamedQuery(name="Office.findByStreetAndCity",query="SELECT o from Office o JOIN o.address a WHERE a.street=:street AND a.city=:city"),
Let's execute the method:
String[] params= {"Mount Eden Road", "London"};
List<Office> offices= dao.findByNamedQuery("Office.findByStreetAndCity", params);
But it fails:
java.lang.IllegalArgumentException: org.hibernate.QueryException: Not all named parameters have been set: [city, street] [SELECT o from Office o JOIN o.address a WHERE a.street=:street AND a.city =:city]
I am not very familiar with JPA but I think that this is failing because we can not set parameters by index when using named parameters on NamedQuery definition.
Your assumption is correct, and you're mixing between the two.
You can either continue using setParameter("street", "Mount Eden Road")
and change your signature to something like public List<T> findByNamedQuery(String queryName, Map<String, Object> params)
or rewrite your query to use positional parameters (starting from 0):
SELECT o from Office o JOIN o.address a WHERE a.street=? AND a.city=?
for(int i = 0; i < params.length; i++){
q.setParameter(i, params[i]);
}