The Anqu Method Pattern
The
anqu method pattern for Java suggests to have a central method to access a @NamedQuery or @NamedNativeQuery.
In short the pattern proposes the following construction for a given query:
You may already be using this pattern in some variation - you could call it common sense.
What is not shown is the method body containig the glue code to execute the query. It is cumbersome to implement and
the API to use is one of the most general APIs you can think of. Nearly every parameter is either just Object or at best
String. Writing that code is error-prone.
Building the bridge between the textual nature of the @NamedQuery statement and the modeled Java world early reduces risks and enables you to
use compiler based tooling to increase coding comfort.
Driven by this the anqu method pattern consists of three principles:
Single Point of Transition:
Have a single method in your code to do the transition to the modeled Java world for a query. Do not repeat the glue code somewhere else and do not let this method do anything else.
Early Transition:
Do the transition from the query statement to the Java world as early/deep in your layers as possible.
Test the Contract:
Have tests to ensure that the three parts dealing with the query (the Java model, the query statement, and the glue code) always fit together.
If you use Eclipse as IDE, the anqu method plug-in can generate the Java method in a few mouse clicks. You get a
specialized, typed, already implemented, and unit-tested API for every query. The plug-in helps to
implement all steps mentioned above and has addtional features to increase productivity.
Please note that in the example above the EntityManager is provided as a parameter of a static method. Though
this is optional/configurable, this variation is considered to be the earliest transition you can come up with.
Plug-In Capabilities
The following picture shows how the main functionalities of anqu method improve the typical workflow of creating and using a named (maybe native) query:
anqu method generates access methods to named queries. The derived methods will encapsulate query creation
and parameter preparation. The parameters of the query will be the parameters of the method
- and they will be typed. In the most basic version the method will return the created query.
You have to execute it yourself.
If you let anqu method try better, you can let it generate a method that not only creates and configures the
query but also executes it. In case of a Select you will get a List as a result. anqu method will try to derive
the type of objects in the List and provide a typed List. Deriving the return
type is not always possible. You may end up with Object as best guess.
In case of an update or delete you will get the number of affected rows as a result.
If you know that the query will at most return one result object, you can let anqu method
generate a single/first result method. It will return null if there was no result.
Another possibility is to generate methods with pagination support. There will be
additional parameters for starting point and page size.
anqu method can also generate JUnit 4 tests for the productive methods providing full code coverage and
regression safety to some extend.
In addition to that the plug-in supports you in adding a @NamedQuery to an existing class. It will
generate such an annotation and if there already is one, it will wrap around the necessary @NamedQueries
annotation automatically.
Even writing the query statement can be omitted if the query name follows some simple rules. In this case just
derived the statement from the query name. Most simple examples are "findByName" and "Xyz.deleteAll".
It is also possible to generate the glue code to access a @NamedQuery from a Spring Data or Apache DeltaSpike Data repository.
Mocking the EntityManager (and Query) in unit tests to avoid building a container environment is a common strategy.
But programming the mocks is cumbersome. Anqu can create Mock Utility classes with static helper methods
to program the EntityManager mock easily. Generate a Mock Utility specific for a query or create a generic Mock Utility
for all queries, hence with a less comfortable API.
If the query statement is simple enough to be derived from the query name, that query name is the only thing which has
to be written by the programmer. All the other code can be generated.
Usage
Auto Completion
The most important features are now available via Eclipse auto completion. Functionality depends on the context:
Outside a @NamedQuery annotation
|
Add @NamedQuery annotation to code.
|
Inside @NameQuery annotation, directly after dot/period in the name attibute
|
Get proposals for query names, e.g. findAll and find-methods for every field.
Selecting a proposal also derives the statement and fills the query attribute.
|
Inside @NameQuery annotation, at first position of the query attribute
|
Derive the statement from the name attribute.
|
Anywhere else inside the @NameQuery annotation
|
Get proposals for query execution methods.
|
Adding a Named Query
To add a named query to an existing class open the context menu from the Java source code editor
and select Anqu Method, then select New Named Query or New Named Native Query as shown below:
A new and mostly empty @NamedQuery or @NamedNativeQuery annotation will appear in the source code ready to be edited.
Deriving Statements
In simple cases the name of the query is enough information to automatically derive the desired statement from it. Note that this only works for JP QL queries - not for native queries.
Here is a short (reduced) description the grammar behind the recognition algorithm.
Select all objects
|
getAll, findAll, readAll, selectAll
|
Delete all objects
|
deleteAll, delAll, removeAll
|
Select objects
|
get, find, read, select
|
Delete objects
|
delete, del, remove
|
Field names
|
Camel case field name of the current entity, first character upper case.
|
Update objects
|
update, set
|
Parameters for e.g. restrictions
|
Given + camel case parameter name
|
Restrictions
|
Where, By + field name; And, Or for chaining; Operators e.g. LessThan, EqualTo, IsNotNull and more!
|
Sorting
|
Order, Sort, Asc, Ascending, Desc, Descending, By
|
Generating Methods
To distinguish the created methods the terms productive methods and test methods are used in this section.
When creating methods, you need to set the cursor into the @NamedQuery or @NamedNativeQuery and open the context menu.
Please note that just opening the context menu while the mouse pointer is over the query will not work. It
really depends on where the cursor is in the editor.
Now choose Anqu Method and select the desired action in two steps:
- What kind of method to generate
- Method executing the query
- Method executing the query, returning one (the first) result
- Method creating the query and returning it
- First/Max method executing the query - with firstResult and maxResults as parameters
- First/Max method creating the query and returning it - with firstResult and maxResults as parameters
- Productive and/or test code and the target(s)
- (Productive) Method to source file holding the query and tests as configured (see settings)
- (Productive) Method to source file holding the query
- (Productive) Method to clipboard
- Tests as configured (see settings)
You can open the help page of this plug-in via the context menu as well as its preference page.
See the next section on possible configurations.
Removing Code
You can remove a @NamedQuery, the corresponding glue code, the generated tests, and the generated MockUtility with this functionality. It is also possible to remove all the code but the @NamedQuery itself.
This will not remove code that has been generated into the clipboard and has been pasted somewhere. Only the following locations are cleaned up if present:
- Glue code in the Entity (annotation and/or method are removed)
- Test class in the same package on a 'one class per query' basis (source file is removed)
- Test class in the same package on a 'one class for all queries' basis (methods are removed)
- MockUtilty in the same package (source file is removed)
Settings
All settings described in this section are global. The ?-buttons give descriptions of the options.
Options for Generated Methods
- The modifier of generated methods is public static by default.
- The insertion position of a method which is generated into source code.
- Parameter order of generated methods is alphabetical by default. This can be changed to the order of appearance in the query. The EntityManager always is the first parameter in both cases.
- If a method returning the query is generated, the method's name is this prefix followed by the query name.
- Generated methods have English JavaDoc comments by default.
- Generated code uses generics by default.
- Generated code does not contain fully qualified names by default. This setting does not apply to static references on test code.
- The EntityManager is expected to be a method parameter by default. The alternative is to expect a field to hold the EntityManager. For both cases the name of the parameter/expected field can be defined here.
Options for Test Code
- If the EntityManager is provided by a field, the generated test code needs to set this field to a mock. This can normally be done by reflection.
- If the EntityManager is provided by a field and is not set by reflection (see above) an auxiliary method is needed. This method has to be implemented in every test class.
- If the generated method is not static, the generated test code depends on an auxiliary method to get an instance of the class under test (to make the call on). The name of the expected method can be defined.
- Test code can be generated to the clipboard or into a JUnit test file (existing or new).
- If test code is generated to a file, the target source folder can be defined. It will not be created automatically.
- If test code is generated to a file, the target class name can be chosen between {entity}Test.java and {entity}{method}Test.java where {entity}
is the entity's class name and {method} is the generated productive method's name. This allows one test class per entity as well as one test class per method.
The package will always be the package of the entity.
- If test code is generated to a file, a query specific MockUtility class can automatically be created in the same package.
Suppression of Warnings
- The generated code might cause warnings depending on the compiler settings of Eclipse. The anqu method plugin tries to overrule any potential warning by applying @SuppressWarning tags in the generated code. However too many suppressions also cause warnings. So it is possible to configure which warnings can appear and thus can by suppressed.
Deriving Statements
- The keyword style of generated statements can be defined.
Removing Code
- Bring up a confirmation dialog before removing a query and/or the generated code?
JPA Version Specific Features
- Activate repeatable annotations when adding a query. No wrapper annotation for @NamedQuery or @NamedNativeQuery will
be generated. Instead a new @Named(Native)Query annotation will appear. Note that if there already is such a wrapper annotation,
it will be used in any case.
Unit Tests
The generated test methods have the following goals:
- Ensure that the productive method has not been changed
- Ensure that the query has not been changed
- Ensure that the relevant entity code has not been changed
- 100% test coverage for the productive methods
Several test methods are generated for a single productive method. Of course all of them take part in the coverage goal
(which is the only goal that can be reached for sure). Some tests try to protect the code from becoming inconsistent due to
manual changes. This is based on some heuristics. Especially checking that the method's code is unchanged is done in a rather
optimistic way.
If a test fails, first thing to do is to check the query semantically. If it is correct, re-generate the productive method
and the test methods. If a newly generated test fails, this plug-in has a bug.
For clarification: The generated test methods do not test the query's semantics, not even if the
query is syntactically correct.
Mock Utilities
Anqu method can simplify mocking query executions by creating Mock Utility helper classes. A query specific
Mock Utility class contains static methods to program a given EntityManager mock to return the provided values
or to throw the provided RuntimeException.
Also a generic Mock Utility can be generated being able to mock any @NamedQuery execution. The API of the generic
Mock Utility is powerful, yet not as comfortable to use as a query specific one. The generic utility is considered
a fallback.
Naming Recommendations
This plug-in generates methods from queries just like 'Generate getters and setters' generates methods from fields.
In both cases names have to be found. As a query's name has big impact on the name of the generated method, please consider the following
recommendations:
- Use a prefix when naming your queries to make the names unique (e.g. use class name as prefix)
- Use a character that is not allowed in method names (e.g. fullstop, blank) to append a query name to the prefix
- Let the query name (suffix) describe the effect of the query, that is make it sound like a method name
This plug-in will split the full query name and try to find the suffix using the assumption that the above recommendations have been met.
You will get well named methods.
Example: The query names "MyEntity.findByName", "MyEntity findByName" "MyEntity:findByName" will all result in a method named "findByName".
If you generate a method to return the query object itself (without executing the query) the method's name will be prefixed by the text 'getQuery' or whatever you defined in the preferences.
Known Limitations
- This plug-in is based on the JPA 2.0 specification. Special extensions like such of Hibernate are not supported explicitly.
- The annotation attribute 'name' of the annotation @Entity is ignored, finding types is based on class names.
- Test code can always be generated even if the visibility settings for the productive methods makes these methods unvisible to the test code.
- Trying to create a single result method for an update or delete will create a normal method executing the query. Same holds for test generation. This is more a feature than a limitation.
- Creating an first result/max results method for update or delete statement is possible but does not really make sense.
- MockUtility classes do not work for JPA 1.0
- For @NamedNativeQuery methods only @SqlResultSetMapping annotations in the entity class itself are considered.
- In @NamedNativeQuery methods all parameters will be of type Object (sad but true, the are untyped).
- Removing a @NamedNativeQuery does not remove a related SqlResultSetMapping.