vendredi 16 octobre 2015

Build HQL where clauses dynamically - Get class attribute value by reflection

I'm working on a project using JPA/Hibernate. In the DAO layer I'm developing a method that generates HQL where clauses depending on the entity class invoked. I mean:

I have this entity:

@Entity
@Table( name = "A" )
public class A{

@Id
private Long id;

@ManyToOne( cascade = { CascadeType.DETACH, CascadeType.MERGE,   CascadeType.PERSIST, CascadeType.REFRESH })
@JoinColumn( name = "B_ID" )
@org.hibernate.annotations.ForeignKey( name = "FK_B" )
private B b;

private String name;

private Boolean isActive;
//...
//getters, setters, equals & hashcode methods
//...
}

All my DAOs extend a GenericDAO declaring the EntityManager, the entityClass and basic CRUDs operations:

public abstract class GenericDaoImpl<T, PK extends Serializable> implements GenericDao<T, PK>{

protected Class<T> entityClass;

@PersistenceContext
protected EntityManager em;

public T create( T t ){
    final T tUpdated = update( t );

    this.em.persist( tUpdated );

    return tUpdated;
}

/**
 * {@inheritDoc}
 */
public T read( PK id ){
    return this.em.find( entityClass, id );
}

//...some crud methods
}   

So, I'm trying to add a method to this class that generates a complete HQL select clause depending on all entity class attributes. First of all, this depends of the scanned entity attribute type, and then another part of query is added (like value, = value, is value ...).

A use case:

A a1 = new A(125, new B(), "test" );
A a2 = new A(200, new B(), null );
String hqlQuery = a.getHqlWhereClauses();
String hqlQuery2 = a.getHqlWhereClauses();

The hqlQuery should be equal to: id = 125 AND B.id = b.id AND name like 'test'; The hqlQuery2 should be equal to: d = 125 AND B.id = b.id;

To do this I started a draft method in my GenericDaoImpl, using java reflection, to scan the entityClass attributes like this with a first String case (test on String attribute type):

public String getHqlWhereClauses() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, InstantiationException{

    StringBuilder result = null;
    final Field[] declaredFields = entityClass.getDeclaredFields();

    if (declaredFields.length > 0) {
        result = new StringBuilder();
        for (Field field : declaredFields) {
            if (field.getType().isAssignableFrom(String.class)) {
                field.setAccessible(true);
                if (XXXXXXXX) {result.append(field.get(entityClass).getClass().getName() 
                + " like " + field.get(entityClass));
                }
            }
        }
    }
    return result.toString();
}

The problem is that: I want to get the value of the attribute scanned in place of XXXXXXXX and test on it (empty or not) in order to put it in the second part of my HQL query. But, I'm unable to do that, I'm facing a cast exception (String->Class) since the entity type is Class when I do this (String) field.get(entityClass).

java.lang.IllegalArgumentException: Can not set java.lang.String field com.xxx.xxx.model.A.name to java.lang.Class

Any suggestions are welcome !

thanks in advance..

cheers





Aucun commentaire:

Enregistrer un commentaire