Download
Logo
Overview
News
Download
Motivation
Examples
Troubleshoot

The Evolution of Glue Code

This (polemic) section describes the motivation behind the plug-in in an evolutionary way starting from a given query ending in a mess. The section is based on experience... All the problems come from the reflective nature of named queries. Spoiler alert: The story has a happy ending.

Here is the query:

@NamedQuery(name = "User.findByName", 
            query = "SELECT u FROM User u WHERE u.name = :name")
First thing many people do is defining the name as constant to publish it.
@NamedQuery(name = User.FIND_BY_NAME, 
            query = "SELECT u FROM User u WHERE u.name = :name")
public class User {

public static final String FIND_BY_NAME = "User.findByName";
 
}
Every caller of the query needs to know the parameter, so next step is another constant:
@NamedQuery(name = User.FIND_BY_NAME, 
            query = "SELECT u FROM User u WHERE u.name = :" + User.PARAM_NAME)
public class User {

public static final String FIND_BY_NAME = "User.findByName";
public static final String PARAM_NAME = "name";
 
}
Now the caller has to provide this:
Query query = entityManager.createNamedQuery(User.FIND_BY_NAME);
query = query.setParameter(User.PARAM_NAME, "theName");
Remember that final String values are compiled into byte code. If the caller is not compiled in the same run with the query (it might be in another project), tools like usage analysis will not work. You will not find the caller by compiler based tooling!

Same problem: Renaming the query or a parameter consistently in class User forces all callers to compile again. Otherwise during runtime they will use wrong String values. Accessing the constants through methods is a way to fix this:

@NamedQuery(name = User.FIND_BY_NAME, 
            query = "SELECT u FROM User u WHERE u.name = :" + User.PARAM_NAME)
public class User {

static final String FIND_BY_NAME = "User.findByName";
static final String PARAM_NAME = "name";

public static String getParamName() {
    return PARAM_NAME;
}

public static String getQueryNameFindByName() {
    return FIND_BY_NAME;
}
 
}
Now there is compiler safety and tooling support such as call hierarchy analysis of the static methods. If the caller works with this API as expected, the code is something like:
Query query = entityManager.createNamedQuery(User.getQueryNameFindByName());
query = query.setParameter(User.getParamName(), "theName");

What a Mess

But there still are problems:
  • A lot of time has been wasted
  • The example is not even documented
  • The query becomes illegible - especially with more parameters
  • All those constants and methods make the class illegible
  • The caller needs to know which parameters to set
  • The caller needs to know which parameter types can be used
  • When someone changes the query, code has to be changed manually
  • The code needs test coverage

Cleaning Up

All you really wanted was an compiler checked API:
public static Query getQueryFindByName(EntityManager entityManager, String name)
anqu method creates this API for you. Typed, implemented, optionally documented and even tested.

The caller can now write the following:

Query query = User.getQueryFindByName(entityManager, "theName");
There is no need to introduce constants ruining the query. The generated code is the only location where the query name and parameter names are needed. The code is in sync with the query. If this contract is broken, a test fails. Just re-create the methods and tests to fit the query again.


top