Getting column names from a JPA Native Query - java

I have an administrative console in my web application that allows an admin to perform a custom SQL SELECT query on our database.
Underneath, the application is using Hibernate, but these queries are not HQL, they're pure SQL, so I'm using a Native Query like this:
protected EntityManager em;
public List<Object[]> execute(String query) {
Query q = em.createNativeQuery(query);
List<Object[]> result = q.getResultList();
return result;
}
This works correctly, but it only returns the rows of data, with no extra information. What I would like is to also get the column names, so when I print the results back to the user I can also print a header to show what the various columns are.
Is there any way to do this?

Query query = entityManager.createNamedQuery(namedQuery);
NativeQueryImpl nativeQuery = (NativeQueryImpl) query;
nativeQuery.setResultTransformer(AliasToEntityMapResultTransformer.INSTANCE);
List<Map<String,Object>> result = nativeQuery.getResultList();
And now you have Map<String,Object> . You can see your Column Names

2020
With hibernate 5.2.11.Final is actually pretty easy.
In my example you can see how I get the column names for every row. And how I get values by column name.
Query q = em.createNativeQuery("SELECT columnA, columnB FROM table");
List<Tuple> result = q.getResultList();
for (Tuple row: result){
// Get Column Names
List<TupleElement<Object>> elements = row.getElements();
for (TupleElement<Object> element : elements ) {
System.out.println(element.getAlias());
}
// Get Objects by Column Name
Object columnA;
Object columnB;
try {
columnA = row.get("columnA");
columnB= row.get("columnB");
} catch (IllegalArgumentException e) {
System.out.println("A column was not found");
}
}

This code worked for me
DTO Class :
public class ItemResponse<T> {
private T item;
public ItemResponse() {
}
public ItemResponse(T item) {
super();
this.item = item;
}
public T getItem() {
return item;
}
public void setItem(T item) {
this.item = item;
}
}
Service Class is in the below
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import org.springframework.stereotype.Service;
import org.hibernate.transform.AliasToEntityMapResultTransformer;
#Service
public class ServiceClass{
#PersistenceContext
public EntityManager entityManager;
public ItemResponse exceuteQueryResponse(String queryString) {
ItemResponse itemResponse=new ItemResponse();
Query jpaQuery = entityManager.createNativeQuery(queryString);
org.hibernate.Query hibernateQuery =((org.hibernate.jpa.HibernateQuery)jpaQuery).getHibernateQuery();
hibernateQuery.setResultTransformer(AliasToEntityMapResultTransformer.INSTANCE);
List<Map<String,Object>> res = hibernateQuery.list();
itemResponse.setItem(res);
return itemResponse;
}
}

Ryiad's answer DTO adds some confusion, you should have kept it away.
You should have explained that it works only with hibernate.
If like me you needs to keep the order of columns, you can specify your own transformer. i copied the code from hibernate and changed the HashMap to LinkedHashMap:
import java.util.LinkedHashMap;
import java.util.Map;
import org.hibernate.transform.AliasedTupleSubsetResultTransformer;
import org.hibernate.transform.ResultTransformer;
/**
* {#link ResultTransformer} implementation which builds a map for each "row", made up of each aliased value where the
* alias is the map key. Inspired by {#link org.hibernate.transform.AliasToEntityMapResultTransformer}, but kepping the
* ordering of elements.
* <p/>
* Since this transformer is stateless, all instances would be considered equal. So for optimization purposes we limit
* it to a single, singleton {#link #INSTANCE instance}.
*/
public class AliasToEntityMapResultTransformer extends AliasedTupleSubsetResultTransformer {
public static final AliasToEntityMapResultTransformer INSTANCE = new AliasToEntityMapResultTransformer();
/**
* Disallow instantiation of AliasToEntityMapResultTransformer.
*/
private AliasToEntityMapResultTransformer() {
}
#Override
public Object transformTuple(Object[] tuple, String[] aliases) {
Map result = new LinkedHashMap<>(tuple.length);
for (int i = 0; i < tuple.length; i++) {
String alias = aliases[i];
if (alias != null) {
result.put(alias, tuple[i]);
}
}
return result;
}
#Override
public boolean isTransformedValueATupleElement(String[] aliases, int tupleLength) {
return false;
}
/**
* Serialization hook for ensuring singleton uniqueing.
*
* #return The singleton instance : {#link #INSTANCE}
*/
private Object readResolve() {
return INSTANCE;
}
}
With this transformer you can used Ryiad's solution with Hibernate:
Query jpaQuery = entityManager.createNativeQuery(queryString);
org.hibernate.Query hibernateQuery =((org.hibernate.jpa.HibernateQuery)jpaQuery).getHibernateQuery();
hibernateQuery.setResultTransformer(AliasToEntityMapResultTransformer.INSTANCE);
List<Map<String,Object>> res = hibernateQuery.list();

After a long time without any answer, And based on my own further research, It seems that it can not be possible, Unfortunately.

If the JPA provider does not support the retrieval of query metadata, another solution could be the use of a SQL parser like JSQLParser, ZQL or General SQL Parser (comercial), which extracts the fields from the SELECT statement.

cast query to hibernate query, then use hibernate method
//normal use, javax.persistence.Query interface
Query dbQuery = entityManager.createNativeQuery(sql);
//cast to hibernate query
org.hibernate.Query hibernateQuery =((org.hibernate.jpa.HibernateQuery)dbQuery)
.getHibernateQuery();
hibernateQuery.setResultTransformer(AliasToEntityMapResultTransformer.INSTANCE);
List<Map<String,Object>> res = hibernateQuery.list();
List<TxTestModel> txTestModels = new ArrayList<>();
res.forEach(e->{
TxTestModel txTestModel = new ObjectMapper().convertValue(e, TxTestModel.class);
// txTestModels.add(new TxTestModel().setIdd((Integer) e.get("idd")).setMmm((String) e.get("mmm")).setDdd((Date) e.get("ddd")));
txTestModels.add(txTestModel);
});
System.out.println(txTestModels.size());

To enforce em.createNativeQuery(..).getResultList() to return List<Tuple> specify it with Tuple.class when creating native queries :
Query q = em.createNativeQuery("SELECT columnA, columnB FROM table", Tuple.class );
List<Tuple> result = q.getResultList();
for (Tuple row: result){
// Get Column Names
List<TupleElement<Object>> elements = row.getElements();
for (TupleElement<Object> element : elements ) {
System.out.println(element.getAlias());
}
// Get Objects by Column Name
Object columnA;
Object columnB;
try {
columnA = row.get("columnA");
columnB= row.get("columnB");
} catch (IllegalArgumentException e) {
System.out.println("A column was not found");
}
}

This worked for me:
final Query emQuery = em.createNativeQuery(query, Tuple.class);
final List<Tuple> queryRows = emQuery.getResultList();
final List<Map<String, Object>> formattedRows = new ArrayList<>();
queryRows.forEach(row -> {
final Map<String, Object> formattedRow = new HashMap<>();
row.getElements().forEach(column -> {
final String columnName = column.getAlias();
final Object columnValue = row.get(column);
formattedRow.put(columnName, columnValue);
});
formattedRows.add(formattedRow);
});
return formattedRows;

This is the working solution
Below example return the objectlist from the query.
Looping the same and from the first object cast it to hasmap, and hashmap.keyset will give you all the coulmn names in a set.
List dataList = session.createSQLQuery("SLECT * FROM EMPLOYEETABLE").setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).list();
for (Object obj : dataList) {
HashMap<String, Object> hashMap = (HashMap<String, Object>) obj;
Set<String> keySet = hashMap.keySet();
break;
}

I also faced a similar problem working with JPA. There is no direct way in JPA to access the resultset metadata. The solution can be extracting column names from the query itself or use JDBC to get the metadata.

Related

How to extract a hashmap object from jdbc template query

I'm trying to extract 2 lists/arrays of Integers from a JDBCTemplate query.
I assume retrieving a Map would be the most practical.
The query is
Map<Integer, Integer> availabletime = jdbctemp.query("
Select a.hour,
s.duration from appointment as a inner join services as s on a.service_fid=s.id
where date=? and guru_fid=?
",date,guru_fid,//mapperlogic.class);
I need a.hour and s.duration as key value pairs of a hashmap. I'm a bit confused regarding the row mapper logic here. Ive mapped only to objects as of now like
public class RoleRowMapper implements RowMapper<Role> {
#Override
public Role mapRow(ResultSet row, int rowNum) throws SQLException {
Role role=new Role();
role.setId(row.getLong("id"));
role.setName(row.getString("name"));
return role;
}
}
` Can someone help me with extracting query results to Maps or multiple lists??
.query() will always return list. Hence, added .get(0)
public Map<Integer,Integer> getAvailableTime(Date date, Integer guru_fid) {
return jdbctemp.query("Select a.hour, s.duration from appointment as a inner join services as s on a.service_fid=s.id where date=? and guru_fid=? ",new Object[] { date, guru_fid }, (ResultSet rs) -> {
HashMap<Integer,Integer> results = new HashMap<>();
while (rs.next()) {
results.put(rs.getInt("a.hour"), rs.getInt("s.duration"));
}
return results;
}).get(0);
}

Is possibility using Spring Data MongoDb to define custom Criteria?

I have simple interface with one method:
Criteria toCriteria(String key, String value)
And next I'd like to have next implementation
public class EqExpression implements Expression
{
#Override
public Criteria toCriteria(String key, String value)
{
return Criteria.where(key).eq(Pattern.compile(value));
}
}
}
but there isn't $eq operator. So my questions:
Why org.springframework.data.mongodb.core.query.Criteria doesn't have such operator?
Is there a way to implement custom Criteria implementation or is there any workaround?
For me it would be good to have code like
#Override
public Criteria toCriteria(String key, String value)
{
//return new BasicDBObject(key, new BasicDBObject("$eq", value)) converted to Criteria
}
In general, my purpose is to implement rest query language and for each operation like gt, lt I have specific implementation of Expression interface.
Request may looks like name=John&age>20
I am building whole query using next code:
List<Criteria> criterias = new ArrayList<Criteria>();
...
while (matcher.find())
{
String key = matcher.group(1);
String operator = matcher.group(2);
String value = matcher.group(3);
// get from map appropriate implementation
criterias.add(expressions.get(operator).toCriteria(key, value));
}
May be you have any suggestions how to implement it more elegant
using this answer, I was able to create simple $eq Criteria
private Criteria getEqCriteria(String value)
{
// hack Criteria, because new Criteria().is(value) not working!
Criteria c = new Criteria();
try
{
Field _criteria = c.getClass().getDeclaredField("criteria");
_criteria.setAccessible(true);
#SuppressWarnings("unchecked")
LinkedHashMap<String, Object> criteria = (LinkedHashMap<String, Object>) _criteria.get(c);
criteria.put("$eq", value);
Field _criteriaChain = c.getClass().getDeclaredField("criteriaChain");
_criteriaChain.setAccessible(true);
#SuppressWarnings("unchecked")
List<Criteria> criteriaChain = (List<Criteria>) _criteriaChain.get(c);
criteriaChain.add(c);
} catch (Exception e)
{
// Ignore
}
return c;
}

Get parameter name of class to pass it to other method in java

I would like to pass parameter name as a parameter to other method, f.e:
I have class:
public class Foo() {
public Bar bar;
public Bar anotherBar;
public Bar yetAnotherBar;
public void doSomethingWithBar() {
common.doingSomething(
getMostImportantBarParameterName()
);
}
}
And in this class I would to have method:
public String getMostImportantBarParameterName() {
return Foo.bar;
}
but instead of returning value of bar, I would like to get a name of parameter bar, so it should just return "bar".
For now I have to do this that way:
public String getMostImportantBarParameterName() {
return "bar";
}
Why I wanna achieve something like that?
I am trying as much I can to avoid using strings in my code, cause in refactorization process I will bypass (skip) it accidentally.
But if I will have "hard coded" parameters that way, when I will later rename this parameter it will be automatically replaced in all instances by Eclipse IDE (Using LALT+LSHIFT+R)
Also my method: common.doingSomething() use parameter in runtime, So I won't get compilation error, which it makes hard to maintain this method.
I don't write unit test, cause I can't yet.
Please give me some help on this. Thanks
----------------- EDIT ------------------------
Real life usage.
I would like to have method to access database records in generic way.
Common database operation in my application is:
Getting records from TableName where Parameter = SomeValue
So I would like to have generic method for that in generic entity listed below:
#MappedSuperclass
public abstract class GenericModel<T extends GenericModel> {
#Transient protected Class<T> entityClass;
private List<T> getByParameterAndValue(String parameter, String value) {
List<T> entities = new ArrayList<T>();
String sqlString = "SELECT e FROM " + entityClass.getSimpleName() + " e WHERE e."+ parameter + " = :value";
TypedQuery<T> query = JPA.em().createQuery(sqlString, entityClass).setParameter("value", value);
try {
entities = query.getResultList();
} catch (NoResultException e1) {
entities = null;
} catch (Exception e) {
Index.toLog("error","Unsupported error in Generic model class in " + entityClass);
}
return entities;
}
which is extended by real entities f.e.:
public class User extends GenericModel<User> {
public String name;
public String email;
public String date;
public String department;
public List<User> getUsersByDepartments(String dep) {
return getByParameterAndValue("department", dep);
}
}
The problem is that in JPA TypedQuery:
TypedQuery<User> query = em.createQuery("SELECT u FROM User u WHERE u.department = :department", User.class);
return query.setParameter("department", department).getSingleResult();
First of all, I think you should reconsider your approach. Using field names like this (either by reflection or hard coded Strings) is not very robust. In general, reflection should be avoided if possible.
What are you trying to achieve? What will common.doingSomething be doing with the field name?
It might be better to model the importance explicitly with an accessor:
class Foo {
private Bar bar;
private Bar anotherBar;
private Bar yetAnotherBar;
public Bar getMostImportantBar() {
return bar;
}
}
To answer your question about generics. You can either select the field by its index or by its name. Both are not robust, for when you change the field name, the String used to get it via reflection will not change with it, and if you change the order of the fields, the index will be wrong.
Here's how to do it:
Class foo = Foo.class;
Field[] fields = foo.getFields();
// get by index
Field firstField = fields[0];
String firstFieldName = firstField.getName();
// get by name
Field barField = foo.getField("bar");
String barFieldName = barField.getName();
EDIT (after reading updated question):
In any Object Relational Mapping solution there is a boundary where the object-oriented realm ends and the relational realm begins. With your solution you are pulling that boundary a bit further into your code, in order to gain ease of use for your specific model classes and queries. The consequence of that is that you get more 'boiler plate' style code as part of your application (the GenericModel class) and that the boundary becomes more visible (the reference to a field by index or name using reflection). This type of code is generally harder to understand, test and maintain. On the other hand, once you get it right it doesn't change that often (if your assumption about the query type you usually need turns out to be valid).
So I think this is not a ridiculous use case for reflection, even though I myself would probably still stick to JPA and accept the similarity of the queries. With a good JPA framework, expressing these queries does not incur a lot of code.
About the hard-coded field names vs indexes, I advise you to go with the field names because they are easier to understand and debug for your successors. I would make sure the field name is expressed in the model class where the field resides, to make it as clear as possible that the two belong together, similar to the example you gave:
public class User extends GenericModel<User> {
public static final String FIELD_NAME = "name";
public static final String FIELD_EMAIL = "email";
public static final String FIELD_DATE = "date";
public static final String FIELD_DEPARTMENT = "department";
private String name;
private String email;
private String date;
private String department;
// the byXXX naming scheme is a quite common shorthand for lookups
public List<User> byDepartment(String department) {
return getByParameterAndValue(FIELD_DEPARTMENT, department);
}
BTW I think getByParameterAndValue cannot be private (must be at least default). Also I don't think you should initialize List<T> entities = new ArrayList<T>() at the start. You can do that in the catch(Exception e) to avoid unnecessary initialization if the query succeeds or returns no results. An your fields should be private (shown above).
Of course, this approach still results in one lookup method for each field. A different solution is to create a service for this and leave the model objects aenemic (without behavior):
public class DaoService {
public <T extends GenericModel> List<T> get(Class<T> entityClass, String fieldName, String value) {
List<entityClass> entities;
String sqlString = "SELECT e FROM " + entityClass.getSimpleName() + " e WHERE e."+ fieldName+ " = :value";
TypedQuery<T> query = JPA.em().createQuery(sqlString, entityClass).setParameter("value", value);
try {
entities = query.getResultList();
} catch (NoResultException e) {
entities = null;
} catch (Exception e) {
entities = new ArrayList<T>()
}
return entities;
}
}
Usage:
List<User> = daoService.get(User.class, User.FIELD_DEPARTMENT, value);
Here's another (slightly wild) idea I just had. Each model class is also a query template:
public abstract class ModelQuery<T extends ModelQuery> {
// TODO set from constructor
private Class<T> entityClass;
private Field[] allFields = entityClass.getFields();
private List<T> getByTemplate() {
List<Field> queryFields = new ArrayList<Field>();
String sql = selectFieldsAndCreateSql(queryFields);
TypedQuery<T> query = setQueryParameters(queryFields, sql);
return executeQuery(query);
}
private String selectFieldsAndCreateSql(List<Field> queryFields) throws IllegalAccessException {
StringBuilder sql = new StringBuilder();
sql.append("SELECT e FROM ")
.append(entityClass.getSimpleName())
.append("e WHERE ");
for (Field field : allFields) {
if (field.get(this) != null) {
sql.append("e.")
.append(field.getName())
.append(" = :")
.append(field.getName());
// keep track of the fields used in the query
queryFields.add(field);
}
}
return sql.toString();
}
private TypedQuery<T> setQueryParameters(List<Field> queryFields, String sql) throws IllegalAccessException {
TypedQuery<T> query = JPA.em().createQuery(sql, entityClass);
for (Field field : queryFields) {
query.setParameter(field.getName(), field.get(this));
}
return query;
}
private List<T> executeQuery(TypedQuery<T> query) {
List<T> entities;
try {
entities = query.getResultList();
} catch (NoResultException e1) {
entities = null;
} catch (Exception e) {
entities = new ArrayList<T>();
}
return entities;
}
}
Usage:
User userQuery = new User();
userQuery.setDepartment("finance");
List<User> results = userQuery.getByTemplate();
I guess there are more ways to skin this cat. Good luck with finding your optimal solution!
To get private field names
use foo.getDeclaredFields(); instead of foo.getFields();
Here are also you have some minor issue
fields[0] means, the first declared field, in which 0 is again hard coded
If you change the order of declaration then again it could be a trouble for you, which will never get refracted
I would recommend using
1.) The Class.forName() SPI logic where you can inject the expected business logic on the fly.
2.) The Spring DI with interfaces and implementations using auto wiring

Java class - how to pass Generic Object to a function

This is my basic function:
public static void main(String[] a) throws Exception {
Session sessione = HibernateUtil.getSessionFactory().openSession();
Query query = sessione.createSQLQuery("select * from User").addEntity(User.class);
List<User> rows = query.list();
Iterator it = rows.iterator();
while (it.hasNext()) {
User usr = (User) it.next();
System.out.println(usr.getEmail());
System.out.println(usr.getName());
System.out.println(usr.getIdUser());
System.out.println(usr.getUser());
}
This function is capable to connect and perform a query on my DB...
I want to create the same function but more general... The previous was specific for only one table (User), the new one must be able to accept as input a String parameter for the query, and the class type where the query will be executed. This will allow me to use only one row in order to perform a query.
It should be something like this:
public static void queryResult(String query, <ClassOfTable>) {
Session sessione = HibernateUtil.getSessionFactory().openSession();
Query qy = sessione.createSQLQuery(query).addEntity(<ClassOfTable>.class);
List<<ClassOfTable>> rows = qy.list();
Iterator it = rows.iterator();
while (it.hasNext()) {
<ClassOfTable> obj = (<ClassOfTable>) it.next();
}
}
Where you find ClassOfTable I don't know how to "generalize" the code...
I hope to have been clear...
P.S. ClassOfTable should be the class rappresentative of a table in DB (Hibernate).
Thanks.
public static <T> void queryResult(String query, Class<? extends T> clazz) {
Session session = HibernateUtil.getSessionFactory().openSession();
Query q = session.createSQLQuery(query).addEntity(clazz);
List rows = q.list();
Iterator it = rows.iterator();
while (it.hasNext()) {
T t = (T) it.next();
// do your work on object t
}
}
If your intention is to return resultset, use:
public static <T> List<T> queryResult(String query, Class<? extends T> clazz) {
Session session = HibernateUtil.getSessionFactory().openSession();
Query q = session.createSQLQuery(query).addEntity(clazz);
List<T> rows = (List<T>) q.list();
return Collections.unmodifiableList(rows);
}
// now call generic method
List<User> users = queryResult("select * from User", User.class);
users.forEach(usr -> {
System.out.println(usr.getEmail());
System.out.println(usr.getName());
System.out.println(usr.getIdUser());
System.out.println(usr.getUser());
});

Pagination with HibernateTemplate's findByNamedParam function

I've seen lots of examples of how to create pagination with some really simple queries. But I don't see any using HibernateTemplate's findByNamedParam method.
How can I set a query's firstResult and maxResult parameters while also using the findByNamedParam method?
Basically, I'm trying to add pagination to an hql query I'm creating via HibernateTemplate's findByNamedParam method.
Ok after a lot of research, I finally got what I wanted.
First, need to create a HibernateCallback implementation:
HibernateCallbackImpl.java:
import java.sql.SQLException;
import java.util.List;
import org.apache.poi.hssf.record.formula.functions.T;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.springframework.orm.hibernate3.HibernateCallback;
public class HibernateCallbackImpl
implements HibernateCallback<List<T>> {
private String queryString;
private String[] paramNames;
private Object[] values;
private int firstResult;
private int maxResults;
/**
* Fetches a {#link List} of entities from the database using pagination.
* Execute HQL query, binding a number of values to ":" named parameters in the query string.
*
* #param queryString a query expressed in Hibernate's query language
* #param paramNames the names of the parameters
* #param values the values of the parameters
* #param firstResult a row number, numbered from 0
* #param maxResults the maximum number of rows
*/
public HibernateCallbackImpl(
String queryString,
String[] paramNames,
Object[] values,
int firstResult,
int maxResults) {
this.queryString = queryString;
this.paramNames = paramNames;
this.values = values;
this.firstResult = firstResult;
this.maxResults = maxResults;
}
#Override
public List<T> doInHibernate(Session session) throws HibernateException,
SQLException {
Query query = session.createQuery(queryString);
query.setFirstResult(firstResult);
query.setMaxResults(maxResults);
// TODO: throw proper exception when paramNames.length != values.length
for (int c=0; c<paramNames.length; c++) {
query.setParameter(paramNames[c], values[c]);
}
#SuppressWarnings("unchecked")
List<T> result = query.list();
return result;
}
}
Then, I can just instantiate the new object and it will return what I want:
Example:
#SuppressWarnings("unchecked")
List<TitleProductAccountApproval> tpaas =
getHibernateTemplate().executeFind(
new HibernateCallbackImpl(
hql.toString(),
paramNames.toArray(new String[paramNames.size()]),
values.toArray(),
firstResult,
maxResult
)
);
The solution by #Corey works great but it includes a problem inside the for-loop where query.setParameter(...) is called.
The problem is that it doesn't account for parameters which are either a collection or an array and this will result in weired ClassCastExceptions because Hibernate tries to determine the ID by calling getId() on the collection or array (which is wrong). This happens e.g. if you are using an IN-clause (e.g. ...WHERE department IN (:departments) ...) where 'departments' is an array or collection of Department entities.
This is because collections or arrays need to use 'query.setParameterList(paramName, (Object[]) value)' or 'query.setParameterList(paramName, (Collection) value)'
Long story short:
I modified the version by #Corey by adding an 'applyNamedParameterToQuery()' method which I borrowed from org.springframework.orm.hibernate3.HibernateTemplate.applyNamedParameterToQuery(Query, String, Object):
import java.sql.SQLException;
import java.util.List;
import org.apache.poi.hssf.record.formula.functions.T;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.springframework.orm.hibernate3.HibernateCallback;
public class HibernateCallbackImpl
implements HibernateCallback<List<T>> {
private String queryString;
private String[] paramNames;
private Object[] values;
private int firstResult;
private int maxResults;
/**
* Fetches a {#link List} of entities from the database using pagination.
* Execute HQL query, binding a number of values to ":" named parameters in the query string.
*
* #param queryString a query expressed in Hibernate's query language
* #param paramNames the names of the parameters
* #param values the values of the parameters
* #param firstResult a row number, numbered from 0
* #param maxResults the maximum number of rows
*/
public HibernateCallbackImpl(
String queryString,
String[] paramNames,
Object[] values,
int firstResult,
int maxResults) {
this.queryString = queryString;
this.paramNames = paramNames;
this.values = values;
this.firstResult = firstResult;
this.maxResults = maxResults;
}
#Override
public List<T> doInHibernate(Session session) throws HibernateException,
SQLException {
Query query = session.createQuery(queryString);
query.setFirstResult(firstResult);
query.setMaxResults(maxResults);
// TODO: throw proper exception when paramNames.length != values.length
for (int c=0; c<paramNames.length; c++) {
applyNamedParameterToQuery(query, paramNames[c], values[c]);
}
#SuppressWarnings("unchecked")
List<T> result = query.list();
return result;
}
/**
* Code borrowed from org.springframework.orm.hibernate3.HibernateTemplate.applyNamedParameterToQuery(Query, String, Object)
*
* Apply the given name parameter to the given Query object.
* #param queryObject the Query object
* #param paramName the name of the parameter
* #param value the value of the parameter
* #throws HibernateException if thrown by the Query object
*/
protected void applyNamedParameterToQuery(Query queryObject, String paramName, Object value)
throws HibernateException {
if (value instanceof Collection) {
queryObject.setParameterList(paramName, (Collection) value);
}
else if (value instanceof Object[]) {
queryObject.setParameterList(paramName, (Object[]) value);
}
else {
queryObject.setParameter(paramName, value);
}
}
}

Categories