Although my question is about Java generics, I put some JPA code to show you the real context.
I work with queries based on JPA 2.0 and the Criteria API. All my requests follow the same pattern (comparison of simple attributes, lack of navigation along the way), so I'm trying to write a general class for working with JPA, keeping the business logic in separate classes. My goal is to have a method that, given the type of Entity and Map, which stores the pairs (field name β desired value) that define the criteria, returns a bean (or a set of beans) with the value of some fields of the object.
All my objects implement the Persistible interface, and all my transfer objects inherit from QueryBean . I am considering classes that are not related to the problem, so I skip their code.
The following code is a snippet of my first approach (suppose cb is a valid instance of CriteriaBuilder):
protected <T extends QueryBean, TT extends Persistible> Collection<T> executeQueryEntity (Class<T> type, Class<TT> rootType, QueryConfig queryConfig, Map<String, Object> parameters) { // (...) Initialization goes here CriteriaQuery<T> c = cb.createQuery(type); // FROM CLAUSE Root<TT> root = c.from(rootType); // SELECT CLAUSE List<String> constructorParams = queryConfig.getTargetAttributes(); Selection<?>[] selectArray = new Selection<?>[constructorParams.size()]; for (int i = 0; i < constructorParams.size(); i++) { selectArray[i] = root.get(constructorParams.get(i)); } c.select(cb.construct(type, selectArray)); // WHERE CLAUSE for (String filter : parameters.keySet()) { if (queryConfig.getFieldConfiguration().get(filter).compareUsingEquals()) { // SOME PREDICATE } else { // SOME OTHER PREDICATE } } // (...) Rest of the code goes here }
The My QueryConfig interface is as follows:
public interface QueryConfig { List<String> getTargetAttributes(); Map<String, FieldConfiguration> getFieldConfiguration(); }
Since I already use the QueryConfig class, which provides query information (for example, the parameters needed for the QueryBean constructor or information about the entity fields), I thought it would be nice to get the entity type from this instead of passing it as a class parameter. I get out of this question that this cannot be done directly, so I tried the following workaround:
Adding a method to QueryConfig as follows:
Class< ? extends Persistible> getTargetEntity();
Adding an intermediate method as follows:
public <T extends QueryBean> Collection<T> queryMany(Class<T> type, QueryConfig config, Map<String, Object> parameters) { executeQueryEntity(type, config.getTargetEntity(), parameters); }
But it will not compile. I believe the reasons are explained here: Type mismatch for Class Generics , but I really didn't understand the answer. My questions are: is there a way to avoid passing the Class< TT > parameter to the execution method? Is this a good way to deal with the problem, or should I reorganize all this? Any improvements to the code are also welcome!