In some cases we have the need to transform an object in an hql query considering only the valued properties, going in introspection also. For example, we have the following class:
class Bean{
Integer id;
String description;
String other;
// getter and setter..
}
The scope is to include in the HQL query only the valorized properties of this bean.
then we immediately think of a cascade of if:
String hql = "Select Bean from Bean";
boolean isWhere = true;
if(bean.getId() != null) {
if(isWhere){
hql += " Where ";
isWhere = false;
}else{
hql += " and ";
}
hql += "Bean.id = " + bean.getId();
}
if(bean.getDescription() != null) {
if(isWhere){
hql += " Where ";
isWhere = false;
}else{
hql += " and ";
}
hql += "Bean.id = " + bean.getDescription();
}
// etc..
The result is a HQL query generated at runtime, but the code written isn't generic and you must in all cases write custom code.
Follows an algorithm created by me, a recursive function that permits to write hql query in every situation:
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Date;
class Generate {
private boolean isWhere;
public String hql(Object obj){
isWhere = true;
String alias = ob.getClass().getSimpleName().toLowerCase();
return hql(obj, alias, true);
}
private String hql(Object ob, String alias, boolean isFirst){
Class<? extends Object> obClass = ob.getClass();
StringBuilder sb = new StringBuilder();
if (isFirst)
sb.append("SELECT "+alias+" FROM "+obClass.getSimpleName()+" as "+alias);
try {
for (Field field : obClass.getDeclaredFields()) {
Class<?> fieldType = field.getType();
String fieldName = field.getName();
if(!isCollection(fieldType) && !isSerialVersionUID(fieldName)){
Object fieldValue = null;
try{ fieldValue = obClass.getMethod(mGet(fieldName)).invoke(ob);
}catch(Exception e){
// some fields don't have get method
// for example serialVersionUID
}
if (fieldValue != null)
// if the field is complex type it goes into introspection
if (isFk(fieldType))
sb.append(hql(fieldValue, alias+"."+fieldName, false));
else {
if (isWhere) {
sb.append(" WHERE ");
isWhere = false;
} else
sb.append(" AND ");
sb.append(alias+"."+fieldName+" = "+getValue(fieldValue, fieldType));
}
}
}
} catch (Exception e) { /** to handle **/ }
return sb.toString();
}
private String getValue(Object value, Class<?> field) {
StringBuilder sb = new StringBuilder();
try { if (field.equals(String.class)
|| field.equals(Character.class)
|| field.equals(char.class)) sb.append("'"+value+"'");
else
if(field.equals(Date.class)) sb.append("to_timestamp('"+value+"')");
else sb.append(value);
} catch (Exception e) { /** to handle **/ }
return sb.toString();
}
public static final ArrayList basicTypes =
new ArrayList(){
private static final long serialVersionUID = -5567529960231273742L;
{
add(byte.class.getName());
add(short.class.getName());
add(int.class.getName());
add(long.class.getName());
add(float.class.getName());
add(double.class.getName());
add(char.class.getName());
add(boolean.class.getName());
add(Byte.class.getName());
add(Short.class.getName());
add(Integer.class.getName());
add(Long.class.getName());
add(Float.class.getName());
add(Double.class.getName());
add(Character.class.getName());
add(Boolean.class.getName());
add(String.class.getName());
add(Date.class.getName());
}
};
private boolean isCollection(Class<?> aClass){
return Collection.class.isAssignableFrom(aClass);
}
private String mGet(String s) {
return "get" + s.substring(0, 1).toUpperCase() + s.substring(1);
}
private boolean isSerialVersionUID(String field) {
return field.equals("serialVersionUID");
}
private boolean isFk(Class<?> field) {
return !basicTypes.contains(field.getName());
}
}
The exceptions aren't handled so to focalize the attention on the algorithm.
An example of use:
Bean bean = new Bean(2013,"description");
String hql = new Generate().hql(bean);
assertEqual("SELECT bean FROM Bean as bean WHERE bean.id = 2013 and bean.description = 'description'", hql);
This is almost the same thing, but already built in Hibernate framework, and much more elegant. http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/querycriteria.html#querycriteria-examples
RispondiEliminaAnyway, for some special query types only hql can be used, so I understand why you did this. I had to do something similar but I took another approach; this is more generalized than what I did.