The finder
list
command shows the candidate dynamic finder method names whose implementations Roo can automatically generate. In this recipe, we will look at how to add dynamic finder methods to a persistent entity using the finder
add
command. As an example, we will add the findFlightsByDestinationLikeAndOriginLike
method to a Flight
entity.
Refer to the Viewing candidate dynamic finder methods recipe, to create the flight-app
Roo project.
Start the Roo shell from the C:
oo-cookbookch03-recipes
directory.
To add dynamic finder methods, follow the given steps:
Flight
entity using a focus
command:roo> focus --class ~.domain.Flight
findFlightsByDestinationLikeAndOriginLike
dynamic finder method to the Flight
entity using the finder
add
command:.. roo> finder add findFlightsByDestinationLikeAndOriginLike Updated SRC_MAIN_JAVAsample ooflightappdomainFlight.java Created SRC_MAIN_JAVAsample ooflightappdomainFlight_Roo_Finder.aj
The finder
add
command adds a dynamic finder method implementation to a persistent entity. This feature saves the effort of writing your own JPA-QL queries for the finder methods. The finder
add
command adds the name of the finder method to the finders
attribute of the @RooEntity
annotation (refer to Chapter 2 for more details). The presence of the finders
attribute in the @RooEntity
annotation triggers the creation of a *_Roo_Finder.aj
ITD file (if it doesn't already exist for the entity) and auto-generation of the finder method implementation in the *_Roo_Finder.aj
ITD file. *_Roo_Finder.aj
adds finder method implementation to the corresponding JPA entity class.
The following figure shows how Roo adds dynamic finder methods to a JPA entity when the finder
add
command is executed:
The given figure shows that when the finder
add
xyz
command is executed, the Finder add-on
of Roo adds the xyz
method name to the finders
attribute of the @RooElement
annotation in the Flight.java
file. As Spring Roo monitors Java files that are annotated with Roo's annotations, Roo uses the Finder add-on to add the xyz
dynamic finder method implementation to the Flight_Roo_Finder.aj
file. If Flight_Roo_Finder.aj
doesn't exist, the Finder add-on creates it. Now, if you define the xyz
method in the Flight.java
file (because you may want to customize the implementation of the xyz
method generated by Roo), the Finder add-on removes it from the Flight_Roo_Finder.aj
file.
The following code shows the modified @RooEntity
annotation of the Flight
entity, after the finder
add
command is executed:
@RooEntity(identifierType = FlightKey.class, table = "FLIGHT_TBL", finders = { "findFlightsByDestinationLikeAndOriginLike"}) public class Flight { ... }
The following code shows the auto-generated implementation of the findFlightsByDestinationLikeAndOriginLike
finder method in the Flight_Roo_Finder.aj
file:
import javax.persistence.EntityManager; import javax.persistence.TypedQuery; ... public static TypedQuery<Flight> Flight.findFlightsByDestinationLikeAndOriginLike( String destination, String origin) { if (destination == null || destination.length() == 0) throw new IllegalArgumentException("The destination argument is required"); ... if (origin == null || origin.length() == 0) throw new IllegalArgumentException("The origin argument is required"); ... EntityManager em = Flight.entityManager(); TypedQuery<Flight> q = em.createQuery("SELECT Flight FROM Flight AS flight WHERE LOWER(flight.destination) LIKE LOWER(:destination) AND LOWER(flight.origin) LIKE LOWER(:origin)", Flight.class); q.setParameter("destination", destination); q.setParameter("origin", origin); return q; }
The given code shows the following:
origin
and destination
arguments must be supplied to the finder method or an exception is thrown. In general, the dynamic finder method implementation generated by Roo requires that the arguments passed to the method are not null
. If an argument type is String
, the dynamic finder method implementation requires that the argument must not be null
or blank, as shown in this code.javax.persistence.TypedQuery<Flight>
. You can call the getResultList
method of the TypedQuery
object from your web controller class to execute the SELECT
query and obtain the result.Let's now look at how we can add a custom finder method to an entity, perform integration testing of dynamic finder methods, and use a @RooEntity
annotation to trigger auto-generation of a dynamic finder method implementation:
You may want to add custom finder methods if the dynamic finder methods offered by Roo don't meet your application's requirements. In such cases, you can either perform push-in refactoring of Roo-generated dynamic finder methods and modify their implementation (not their name or signature) in the corresponding persistent entity Java class or you can define the method in the persistent entity Java class (which will result in removing the method from the *_Roo_Finder.aj
AspectJ ITD) or you can create your own AspectJ ITD to introduce your custom finder methods into the persistent entity Java class.
If you want to change the implementation of a dynamic finder method, then you can either use push-in refactoring or you can define the method in persistent entity Java class. The effects of using push-in refactoring or defining the method in persistent entity Java class are the same. In push-in refactoring, you use the IDE to move a declaration from AspectJ ITD file to the target Java class and then modify it. And, in case you decide to define the method in the Java class itself, you will probably do a copy-paste from AspectJ ITD to the Java class, and let Roo remove the method declaration from AspectJ ITD.
The following figure shows what happens when you use push-in refactoring to move a method declaration from AspectJ ITD to the target Java ss:
The given figure shows that when you perform push-in refactoring of the findFlightsByDestinationLikeAndOriginLike
method, the IDE simply moves the method from the AspectJ ITD file to the target Flight.java
class. So, if you have to customize a method such as findFlightsByDestinationLikeAndOriginLike
by writing it in the Flight.java
class, it will be much simpler if you perform push-in refactoring or simply copy the method from the AspectJ ITD file and paste it in the Flight.java
class (followed by removing the Flight.
that is prefixed to the method name). Copy-pasting from AspectJ ITDs to target Java classes isn't efficient if you are planning to move all the declarations in all the AspectJ ITDs to target Java classes. This is where push-in refactoring is helpful, as we will see in Chapter 7, Developing Add-ons and Removing Roo from Projects.
If you want to add a custom finder method whose name or signature is different from the dynamic finder methods offered by Roo, then you can either define the method in the persistent entity Java class or you can create a new AspectJ ITD and declare your method in it. Both these approaches are fine, and it depends upon how comfortable you are with writing AspectJ ITDs. Roo generates AspectJ ITDs so that cross-cutting concerns are separate from the Java classes. So, if you feel that writing finder methods in your persistent entity Java class pollutes it, you should consider writing AspectJ ITDs. It is important to note that the custom AspectJ ITDs that you create in your project are not managed by Roo.
The following code shows an example of the Flight_MyCustom_Finder.aj
AspectJ ITD,
which introduces the searchFlights(SearchCriteria criteria)
finder method into Flight.java
:
import javax.persistence.EntityManager; import javax.persistence.TypedQuery; privileged aspect Flight_MyCustom_Finder { public static TypedQuery<Flight> Flight.searchFlight( SearchCriteria criteria) { EntityManager em = Flight.entityManager(); TypedQuery<Flight> q = em.createQuery("SELECT Flight ..", Flight.class); q.setParameter("destination", criteria.getDestination()); q.setParameter("origin", criteria.getOrigin()); return q; } }
As the given code shows, you can create your own AspectJ ITD and add custom finder methods to JPA entities. As custom AspectJ ITDs are not managed by Roo, if you create the searchFlight
method in the Flight.java
class, Roo will not remove it from the Flight_MyCustom_Finder.aj
file.
Roo doesn't create integration tests for the auto-generated dynamic finder methods. To test finder methods, write the test method in the *IntegrationTest.java
file corresponding to the persistent entity.
The @RooEntity
annotation accepts a finders
attribute, which contains an array of string values identifying names of dynamic finder methods that must be generated for the persistent entity. The finders
add
command adds the finder method name to the finders
attribute, which in turn results in the generation of dynamic finder method by Roo. As the addition of finder method name to finders
attribute triggers generation of dynamic finder method implementation, instead of using the finder
add
command you can directly add the name of the finder method to the finders
attribute using IDE, which in turn will trigger Roo to generate the finder method implementation.
@RooEntity
annotationfinder
list
command is used to show the names of candidate dynamic finder methods3.142.114.19