@Entity(name = "Action")The code looks like this, because when doing named queries in JPA the name need to be unique for the WHOLE persistence unit. So, we agreed about the naming convention "[entity name].[finder name]" for the name of the query.
@NamedQueries({
@NamedQuery(name = "Action.findAll", query = "SELECT o FROM Action o"),
@NamedQuery(name = "Action.findByExtCode", query = "SELECT o FROM Action o WHERE o.externalCode = ?1"),
@NamedQuery(name = "Action.findByDescription", query = "SELECT o FROM Action o WHERE o.description = ?1"),
@NamedQuery(name = "Action.findManualActions", query = "SELECT o FROM Action o WHERE o.manual=?1"),
@NamedQuery(name = "Action.findSelectedActions", query = "SELECT o FROM Action o WHERE o.actionType.id <> 3"),
@NamedQuery(name = "Action.findFlowActions", query = "SELECT o FROM Action o WHERE o.actionType.id=3"),
@NamedQuery(name = "Action.findByActionFlowId", query = "SELECT o FROM Action o JOIN o.actionFlows af WHERE af.id = ?1")
})
It is actually quite nicer an more manageable than EJBQL in xml files, but still there is quite a bunch of copy/paste, String that are not constants, and the usage of named queries is here more problematic than EJB 2.0 home interfaces.
The usage looks like:
Query namedQuery = em.createNamedQuery("Action.findByExtCode");And this is for only one parameter...
namedQuery.setParameter(1, "001");
ActionBean actionBean = (ActionBean) namedQuery.getSingleResult();
The possible code errors (due to lack of static typing) we get here are:
- Errors on the string name for the namedQuery
- Errors on the parameter position (the named queries annotation is in the model not close to the business logic executing queries)
- Errors in Casting
It is quite clear looking at the code above, a JPA named query can be defined as an interface method. It has:
- a name (entityName + methodName),
- a list of parameters (ordered or named),
- and a result (list or single).
ActionQuery actionQuery = NamedQueriesFactory.getQueryProxy(ActionQuery.class, em);And the Queries interface:
ActionBean action = actionQuery.findByExtCode("001");
@JpaQueriesInterface(prefix = "Action")Which gets all the advantage of strong Java typing.
public interface ActionQuery {
public Collection<ActionBean> findAll();
public ActionBean findByExtCode(String extCode);
@JpaQueryUseParamNames
public ActionBean findByDescription(@JpaParamName("description")String description);
public Collection<ActionBean> findManualActions(boolean manual);
public Collection<ActionBean> findSelectedActions();
public Collection<ActionBean> findFlowActions();
public Collection<ActionBean> findByFlowActionId(long flowActionId);
}
So, the code of my small running example is here:
http://subversion.jfrog.org/jfrog/greenhouse/jpa/trunk/ and it's using maven of course...
Now, the next step is to use the Annotated Query Interface has NamedQuery provider so it will really look like JDBC 4.0.
6 comments:
I haven't finished reading through yet, but point #2 on the parameters can be avoided by having named parameters in the query e.g.
SELECT o FROM Action o WHERE o.externalCode = :externalCode
and then query.setParameter("externalCode", "001");
What I normally do is to add public static Query createQueryWhereXXX(Entity Manager em, String xxxValue, ...); to the entity itself. That way the producer and consumer of NamedQueries remains within the same class.
Small suggestion.
In your proxy, you might want to cache your work since the definition of your interface isn't likely to change during the running of your code.
@Anonymous: About static method in Bean.
I really like this solution too, and maintenance and mangement is quite nice and easy. Like you say, what's important is to make the SQL query close to the java code activating it.
The issues I have with it are:
- Static is less OO than interfaces (but that really kind of annoying academic argument)
- You still have 3 times the same name of the query, and you cannot inforce the naming convention [entity name].[query name]
- Does not remove the copy paste of execution of the name query.
Concerning "Errors on the string name for the namedQuery": Why not use constants? E.g.
@NamedQuery(name = Action.Q_FIND_BY_EXT_CODE, query = "SELECT o FROM Action o")
public class Action{
public static final String Q_FIND_BY_EXT_CODE = "Action.findAll";
}
Hey Fred,
just rediscovered this blog post via twitter :). You told me about your approach o the Roundup already. Just wanted to leave a note that the OpenSource Hades library (we were chatting about this at the Roundup, too) is still growing that approaches the same issues by introducing a generic DAO interface. This allows defining and executing queries in a very sophisticated manner. See an explaining DAO interface from the Hades sample app here
The actual project home is here: http://hades.synyx.org.
Hope to see you at one of the upcoming conferences or at least at the Roundup next year :).
Regards,
Ollie
Post a Comment