Dynamic Finders ::

Find it! Warp it! Wideplay.com. new!

DynamicFinders is currently version 1.0 and licensed under an Apache 2.0 license.

[   Warp    Source    Download    Troubleshooting    Email   ]

Wideplay's Dynamic Finders

Dynamic Finders is a part of warp-persist and the Warp Framework's Hibernate and JPA integration. It is a powerful query abstraction as you will see in a second =) 

Dynamic Finders is already bundled with Warp's Persistence module. If you've got that working, creating Dynamic Finders and accessors is a breeze. Of course you can use it in any guice application.

Dynamic Finders for Concrete classes

Any Guice-managed object is a candidate for Dynamic Finders. You can add finders to any class whose objects are obtained via guice (in the form of HQL or JPAQL queries) with a simple annotation:

public class PersonRepository {
	@Finder(query="from Person")
	public List<Person> listAll() { return null; }

Okay, that's it! Now if you obtain a PersonRepository instance via Guice, you can invoke listAll() and expect it to execute the HQL query bound to the @Finder annotation. Notice that we return null as a dummy implementation that will be overridden by warp-persist. Dynamic Finders also helps with neater typing by hiding unchecked cast warnings this way. Sweet!

Dynamic Finders for Accessors (Daos)

Now, dummy implementations (even simple ones like returning null) are a bit kludgey. Where's my Dao?! Although you dont really need Daos when working with Hibernate Sessions (or JPA), it is often useful to group similar functions/finders into a accessor service. Dynamic Finders provides a way for you to get all of the functionality of such a service using interfaces (that you provide):

public interface PersonAccess {
	@Finder(query="from Person")
	List<Person> listAll();

Then, in your injector configuration (the place where you configured warp-persist):

Injector injector = Guice.createInjector(..., PersistenceService.usingHibernate()
		.across(UnitOfWork.TRANSACTION) //etc.

...and you're ready to go. Now simply inject PersonAccess into any of your objects and start using it as though the implementation were already written for you (which it is)!! Accessors, of course, run transactionless and are thus well-behaved when called from more coarse-grained transactional methods (for more details on transactions, see Warp Persist).

Named Queries and Parameter binding

Dynamic Finders has full support for named queries (a way to externalize hql/jpaql queries) when using either Hibernate or JPA:

public interface PersonAccess {
	@Finder(namedQuery="all people")
	List<Person> listAll();

I strongly encourage you to use named queries as they are good for detecting errors early (at deploy time) and don't require recompilation to make minor changes. 

Similarly, named parameters (an awesome feature of hibernate) are supported using Guice's @Named binding annotation:

public interface PersonAccess {
	@Finder(query="from Person where firstName = :firstName")
	Person find(@Named("firstName") String name);

Notice that it doesn't matter what the methods or their parameters are called or what order they appear in. More interestingly, in this example Ive returned just a Person (not a List)--Dynamic Finders is smart enough to look for a unique result when your return type is not a Collection or Array.

If for some odd reason you prefer to use indexed parameters (JDBC-style), you may do so with alacrity:

public interface PersonAccess {
	@Finder(query="from Person where firstName = ?")
	Person find(String name);

Dynamic Finders will assume you are using indexed parameter binding unless there is atleast one parameter tagged with an @Named annotation.

Collecting results (using the Java Collections Framework)

By default, if you want a collection of items you get whatever the underlying implementation provides (generally some implementation of java.util.List). However, it is sometimes useful to obtain various collection types instead--a Set for instance (of unique results). Dynamic Finders can auto-box returned results into arbitray Collections classes for you:

public interface PersonAccess {
	@Finder(query="from Person", returnAs = LinkedHashSet.class)
	Set<Person> listUniquePersons();

In this case, I've returned a LinkedHashSet (which preserves insertion order and removes any duplicates). You are welcome to specify any implemention of java.util.Collection so long as it has a nullary (default) constructor and matches the return type (obviously). 

Paging results (aka 'limit' in SQL)

It is not always desirable to have a static window size of results (i.e. hard-coded in your query). Both HQL and JPAQL allow you to specify a first result index and a maximum results integer limit. Dynamic Finders lets you freely mix these into your finder declarations:

public interface PersonAccess {
	@Finder(query="from Person")
	List<Person> listAll(@FirstResult int first, @MaxResults int max);

Note that the order in which these annotated parameters appear is irrelevant. You can freely mix them in and around other named or bound parameters as you like. They also do not have to appear together, you can use each independently.

Contribute to Dynamic Finders and the Warp Framework

If you like what you see on this site, please do help out. The easiest way is to join the Warp mailing list.