We can generate some Java classes to easily build HQL queries of type HQLQueryBuilder (this is a set of classes specific to the Gazelle project and available in gazelle-seam-tools module)
Three examples
- We want to select some entities of a given type:
private SystemActorProfiles getSap(String systemKeyword, String actor, String profile, String option){
SystemActorProfilesQuery sapQuery = new SystemActorProfilesQuery(em);
sapQuery.system().keyword().eq(systemKeyword);
sapQuery.actorIntegrationProfileOption().actorIntegrationProfile().actor().keyword().eq(actor);
sapQuery.actorIntegrationProfileOption().actorIntegrationProfile().integrationProfile().keyword().eq(profile);
sapQuery.actorIntegrationProfileOption().integrationProfileOption().keyword().eq(option);
return sapQuery.getUniqueResult();
}
or (a more complex example) :
TestRolesQuery trQuery = new TestRolesQuery(entityManager);
trQuery.roleInTest().testParticipantsList().tested().eq(Boolean.TRUE);
trQuery.test().testType().keyword().eq(TestType.TYPE_CONNECTATHON_STRING);
trQuery.test().testStatus().keyword().eq("ready");
trQuery.roleInTest().addFetch();
trQuery.roleInTest().testParticipantsList().addFetch();
trQuery.roleInTest().testParticipantsList().actorIntegrationProfileOption().addFetch();
trQuery.roleInTest().testParticipantsList().aipo().addFetch();
trQuery.roleInTest().testParticipantsList().aipo().systemActorProfiles().system().eq(system);
trQuery.roleInTest().testParticipantsList().actorIntegrationProfileOption().eq(aipo);
trQuery.test().keyword().order(true);
List<TestRoles> roles = trQuery.getList();
}
- A HQLQueryBuilder is provided (using a method parameter for example, here with a FilterDataModel) :
@Override
public void appendFiltersFields(HQLQueryBuilder<TestInstance> queryBuilder) {
super.appendFiltersFields(queryBuilder);
TestInstanceQuery testInstanceQuery = new TestInstanceQuery(queryBuilder);
if (testingSessionIsNull){
testInstanceQuery.testingSession().isNull();
}
if (testType != null){
testInstanceQuery.test().testType().eq(testType);
}else{
testInstanceQuery.test().testType().keyword().eq(TestType.TYPE_CONNECTATHON_STRING);
}
}
Two classes are generated for each Hibernate entity, for example for the TestInstance entity:
- TestInstanceQuery
- Class used to execute queries
- Can be built "from scratch", with an entity manager
- Can built from a HQLQueryBuilder, to add constraints
- Implements HQLQueryBuilderInterface<TestInstance>, gathering all the public methods from HQLQueryBuilder
- Inherit from TestInstancePath
- TestInstancePath
- Allow to browse the attributes of the TestInstance entity
- Each attribute is mapped by a method which returns a path (ex : test(), lastStatus(), ...)
- The paths can be browsed by stringing the methods together (testInstanceQuery.test().testType().eq(testType);)
- Paths are strongly typed, we cannot discard it
- Inherit from HQLSafePathEntity (owned a method addFetch()), or from the class "Parent"Path, in order to inherit of its paths.
- A simple path (ex : HQLSafePathBasic<String> description()) is a HQLSafePathBasic, we allow to perform
- a like on String
- a comparison on dates/numbers
- an ordering (order(boolean ascending))
Usage
Available from gazelle-seam:1.53
Declare the plugin in the pom of each modules which create entities :
<build>
<plugins>
<plugin>
<groupId>org.bsc.maven</groupId>
<artifactId>maven-processor-plugin</artifactId>
</plugin>
</plugins>
</build>
Advantages
- Static analysis of the requests at compilation time : existing path, check the type of parameters (In the example above, the type of testType is checked)
drawbacks
- Longer compliation time (generation and compilation of generated classes)
Design
- The creation of an interface for the HQLQueryBuilder (HQLQueryBuilderInterface) shall enable the developer to handle the concept of "requestor" without using the class HQLQueryBuilder. This interface shall also, at the very end, containts the documentation of the API.
- The HQLQueryBuilder remains based on paths, the generated code shall uniquely allow to replace the String by stronly typed Java objects.
- A path always inherit from the HQLSafePath. This class is a path from the HQLQueryBuilder. A lot operations are already available on this path (list of distinct items, eq(), isNull(), ...)
- The paths to the basic types are of type HQLSafePathBasic, which include comparators (ge, like, ...) and the ordering (order).
- The paths to the entities are of type HQLSafePathEntity, the particularity of which is the capability to be fetched at runtime (storage at session level and later retrieval, to avoid the "lazy exception")
Then, the plugin using the Hibernate annotations found in the code to generate those classes.ons.
Classes are generated :