Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 16 Next »

Getting Started with gquery (Hibernate)

This is the getting started guide for the Generic Query (gquery) component of the grepo framework.It's not supposed to be a complete reference manual - the goal is to show a basic usage and configuration scenario of grepo's gquery component (using hibernate). If you have problems understanding parts of this guide or the framework in general or if you have any suggestions, good ideas or if you have found potential bugs please let us know. So let's get started!

Version Information

Icon

This is the Getting Started guide for the generic query (hibernate) component of the grepo framework version 1.5.x.

Older versions of this guide:

Download the demo project

The demo project for this guide can be checked out from our SVN repository as follows:

$ svn checkout http://svn.codehaus.org/grepo/trunk/demo-grepo-query-hibernate demo-grepo-query-hibernate

The demo project is a maven project and we highly recommend that you use maven to set up the project. If you don't want to use maven you can also set up the project manually. If you use maven and eclipse you can easily make an eclipse project using the following command in the demo project's root directory:

$ mvn eclipse:eclipse

You can now import the project in your eclipse workspace.

After you have imported the project you should now be able to run the UserRepositoryTest JUnit test. You can also run the test using maven from command line:

$ mvn test

Let grepo tell you what's going on

Grepo uses the slf4j logging api. If you set the logger level for the package org.codehaus.grepo to DEBUG in (src/test/resources/log4j.xml) like this:

Grepo should print out information similar to this:

15:11:51,211 DEBUG [GenericQueryMethodInterceptor:56] - Invoking method 'loadByUsername'
15:11:51,218 DEBUG [QueryExecutorNamingStrategyImpl:62] - Resolved executor name: load
15:11:51,225 DEBUG [QueryExecutorFindingStrategyImpl:55] - Found queryExecutor
                   'org.codehaus.grepo.query.hibernate.executor.LoadQueryExecutor' for execution of method
                   'loadByUsername'
15:11:51,241 DEBUG [GenericRepositorySupport:105] - Executing query without using transaction template
15:11:51,260 DEBUG [QueryNamingStrategyImpl:86] - Resolved named-query: demo.domain.User.ByUsername
15:11:51,265 DEBUG [AbstractHibernateQueryExecutor:168] - Using query: from User where username = ?
15:11:51,267 DEBUG [AbstractHibernateQueryExecutor:456] - Setting parameter '0' to 'max'
15:11:51,312 DEBUG [DefaultHibernateRepository:155] - Query result is
                   'demo.domain.User@24a09e41[id=0,username=max]'
15:11:51,333 DEBUG [GenericQueryMethodInterceptor:72] - Invocation of method 'loadByUsername'
                   took '0:00:00.010'

Demo application

The teeny-weeny demo application consists of one database table (USERS) which is mapped using Hibernate to one Java entity (demo.domain.User). Note that the object relational mapping is done using standard Hibernate mechanisms and is not grepo specific. The mapping can be found in src/main/resources/META-INF/hibernate/User.hbm.xml. The USERS table contains the following columns:

COLUMN

PROPERTIES

ID

not null, primary key

FIRSTNAME

nullable

LASTNAME

nullable

USERNAME

not null, unique

EMAIL

nullable, unique

Using the grepo framework

In order to use the gquery component with Hibernate you need the following grepo artifacts (jars) in your project's classpath:

  • grepo-core-<VERSION>.jar
  • grepo-query-commons-<VERSION>.jar
  • grepo-query-hibernate-<VERSION>.jar

Somewhere in your Spring application context (xml) you have to import the default Hibernate configuration of the grepo gquery component.

In the demo project this is done in src/main/resources/META-INF/spring/application-context.xml. Note that you may not need to import that file if you decide to setup grepo with special/custom configuration - you could for instance configure the required "grepo" beans directly in your application context.

Furthermore you need to define a datasource and a Hibernate session factory. This configuration is "standard" Spring/Hibernate and is not grepo specific. In the demo project the hsql datasource and session factory are configured in src/main/resources/META-INF/spring/db-environment.xml.

The next step is to define a basic configuration for the data access layer. This configuration may look like this:

Using the grepo namespace the configuration above looks like:

Here we define an abstract factory bean which will be used to create all instances of our concrete repositories (DAOs). Its a good idea (although not required) to use an abstract factory bean because this makes configuration of concrete repository beans simpler and furthermore you just have to modify the abstract factory bean if you need to change the basic configuration for your data access layer in the future. Note that the abstract factory bean above uses a really simple configuration approach (the rest of the configuration will be done by the framework behind the scenes). For custom/special needs the abstract bean definition (properties) may be more comprehensive. The following is a simple example of an abstract bean which uses sets the sessionFactory property accordingly:

Using the grepo namespace the configuration above looks like:

Creating the first repository

Now its time to create the generic repository for the User entity. For this we just have to define an empty interface (demo.repository.UserRepository), which looks like:

Note that we want a read-write repository (this means that we want a repository with basic CRUD operations) and thus have to extend from org.codehaus.grepo.query.hibernate.repository.ReadWriteHibernateRepository. For a read-only repository we would exend from org.codehaus.grepo.query.hibernate.repository.ReadOnlyHibernateRepository instead. The first generic type is the Java entity (User) and the second one is the type for the primary key property of the entity (Long).

The next step is to configure a repository bean in our Spring application context:

Using the grepo namespace the configuration above looks like:

The userRepository bean uses the abstractRepository bean as parent and thus inherits the basic configuration from the parent. We have to set (at least) one property:

  • proxyClass: This property must be set to the interface which should be "proxied". In our case we provide the fully qualified name of the demo.repository.UserRepository interfaces which we have created previously.

Using repository scanning

You could also grepo's repositiory-scan feature (instead of configuring your repository beans manually) in order to automatically detect concrete repository beans. This functionality is similar (based on) Spring's component-scan feature.

Using the grepo namespace the configuration looks like:

Note: That the repository interfaces have to be annotated with Spring's Repository annotation in order to be detected by the repository-scanner.

Finished! We can now inject our userRepository bean wherever we need basic CRUD operations for the User entity.

Executing generic queries

We can now add various methods to our UserRepository interface in order to execute queries (either HQL or SQL). In our application we might want to load a user from database via username property. Therefore we would add the following method to our UserRepository interface:

And thats it! We can now inject our userRepository bean and use the getByUsername method to fetch User entities from database by username. As you can see we use grepo's GenericQuery annotation to tell the framework that the method has to be executed (or handled) dynamically (remember that we didn't have to provide and implementation of our UserRepository interface). The query property provides the HQL query to be executed when the method gets invoked. We can also execute SQL queries:

Here we have to use grepo's HibernateQueryOptions annotation to tell the framework that an instance of User has to be returned.

Queries with named-parameters

We could also use Hibernate's named-parameter style for placeholders in our query (instead of the JDBC style which uses question marks as placeholders):

Note that we also have to annotate the method's parameters with grepo's Param annotation. This is necessary to associate method parameters with named-parameters in the query. Even though using named-parameters requires a little more configuration in your interface (because of the Param annotation), there are several reasons why someone would (and should) prefer named-parameter in queries:

  • The sequence and number of query-parameters does not have to match the method's parameters. (Using question marks as placeholders generally requires sequence and number of query-parameters to match the method's parameters).
  • You can use one method parameter for multiple query parameters.
  • Using named-parameter queries you can provide a java.util.Collection as method parameter and use it for IN-Clauses in your query. For instance:

How it works

The attentive reader may wonder how grepo can return the appropriate types. The getByUsername method returns an instance of User while findByUsernames returns a list of User entities. The answer is conventions. Grepo is configured with a set of org.codehaus.grepo.query.commons.executor.QueryExecutors. As the name implies a QueryExecutor is responsible for executing queries. In the default configuration grepo uses the following implementations of the QueryExecutor interface:

  • org.codehaus.grepo.query.hibernate.executor.GetQueryExecutor: This implementation executes a query using the uniqueResult method of the Hibernate API and thus returns null or the instance. If multiple records were found, Hibernate throws an unchecked exception.
  • org.codehaus.grepo.query.hibernate.executor.LoadQueryExecutor: Same as GetQueryExecutor with the difference that this executor never returns null - if no records were found then an NoResultException will be thrown.
  • org.codehaus.grepo.query.hibernate.executor.ListQueryExecutor: This implementation executes a query using the list method of the Hibernate API and thus returns instances of java.util.List. This executor never returns null - if no records were found an empty list is returned.
  • org.codehaus.grepo.query.hibernate.executor.UpdateQueryExecutor: This implementation executes update- or delete-queries, that is queries that update or delete records in database. It uses the executeUpdate method of the Hibernate API and thus returns an int indicating how many records where affected by the (update- or delete-) operation.
  • org.codehaus.grepo.query.hibernate.executor.IterateQueryExecutor: This implementation executes a query using the iterate method of the Hibernate API and thus returns an instance of java.util.Iterator.
  • org.codehaus.grepo.query.hibernate.executor.ScrollQueryExecutor: This implementation executes a query using the scroll method of the Hibernate API and thus returns an instance of org.hibernate.ScrollableResults.

So obviously grepo used the ListQueryExecutor for the findByUsernames and the GetQueryExecutor for the getByUsername method. But how did grepo know which executor has to be used for which method (Note that we didn't tell grepo which executor has to be used). The answer is again conventions. The framework uses a org.codehaus.grepo.query.commons.executor.ExecutorFindingStrategy which is responsible for finding the appropriate executor for a given generic method. In its default configuration grepo uses org.codehaus.grepo.query.commons.executor.QueryExecutorFindingStrategyImpl. This implementation uses a org.codehaus.grepo.query.commons.executor.QueryExecutorNamingStrategy which is responsible to retrieve an executor name for a given method. Furthermore the QueryExecutorFindingStrategyImpl has a registry (basically a Map) which maps executor names to executor classes. The org.codehaus.grepo.query.commons.executor.QueryExecutorNamingStrategyImpl (which is the one grepo uses in its basic configuration) resolves executor names according to the following rules:

If the name of the method matches the pattern

^(is|has|find|get|load|scroll|iterate|delete|update)

then the executor name is the matching prefix. This means that if the method starts with is, has, get, load, scroll, iterate, delete or update this rule will be applied.

If the method name does not match the pattern above, then null will be returned, meaning that the strategy didn't find the appropriate executor name for this method.

Feel free to provide your own implementation if desired. Note also that it is always possible to write a custom QueryExecutor without grepo being aware of that implementation. So you can use grepo's default configuration and write your own QueryExecutor and tell it to use this executor like this:

Using this approach is straight forward, because you tell grepo what executor has to be used and thus the framework doesn't use the QueryExecutorNamingStrategy at all. That's good if you have only a few methods which require a special/custom executor. If you want to use your custom executors more frequently then it's probably better to configure the framework accordingly.

Separating queries from Java code

You may not want to have the queries directly in your Java code. Grepo uses Hibernate's concept of named queries to achieve that. This would make your repository methods even easier:

In our mapping file (src/main/resources/META-INF/hibernate/User.hbm.xml) we would define the the HQL query like this:

An appropriate SQL query would look like:

How it works

You may now wonder how grepo is able to find the correct query to execute (you will most likely have several named queries defined within your Hibernate session factory). The answer is conventions. Grepo uses a org.codehaus.grepo.query.commons.executor.QueryExecutorFactory which is responsible for creating QueryExecutor instancess. Grepo's default implementation is org.codehaus.grepo.query.commons.executor.QueryExecutorFactoryImpl which uses a org.codehaus.grepo.query.commons.naming.QueryNamingStrategy. You could write your own implementation and tell grepo to use that strategy instead. Grepo's default implementation is org.codehaus.grepo.query.commons.naming.QueryNamingStrategyImpl which resolves query names for generic methods according to the following rules:

If the queryName property is set for the GenericQuery annotation, then this value is used as the query name. For instance:

If the name of the method matches the pattern

^(is|has|find|get|load|scroll|iterate|delete|update)

then the query name is composed of the fully qualified entity class name and the method name (with removed prefix). This means that if he method starts with is, has, get, load, scroll, iterate, delete or update this rule will be applied. The example above shows how the query for the getByUsername method was resolved to "demo.domain.User.ByUsername". Note that the matched prefix is removed.

If the name of the method does not match the pattern above, then the query name is composed of the fully qualified entity class name and the complete method name (without removed prefix).

Using dynamically generated queries

So far we have only used static queries. But you may also want to generate your queries dynamically depending on the given method (input) parameters. In our application we may want a method which finds users via their firstname and/or lastname. For this we can either just implement the method ourselves (meaning not annotating the method with GenericQuery) or we could write an implementation of the org.codehaus.grepo.query.hibernate.generator.HibernateQueryGenerator interface. A simple implementation which generates a HQL query looks like:

In our UserRepository we would define a method like this:

Note that it is required to generate queries with named-parameter style (as JDBC style does not work). If you want to generate a SQL (native) query instead of HQL you would just use org.codehaus.grepo.query.hibernate.generator.AbstractHibernateNativeQueryGenerator as the base class for your generator. Furthermore you can also use the Hibernate Criteria API like this:

The method would be defined in the UserRepository as follows:

Additional functionality

Paging

Hibernate offers paging functionality which can be used with grepo's MaxResults and FirstResult annotations like this:

It's also possible to use the firstResult and maxResults properties of the GenericQuery annotation.

Result conversion

The framework supports result conversion functionality. For this grepo uses implementations of the org.codehaus.grepo.core.converter.ResultConverter interface. ResultConverters can be used to convert the result of a query invocation and may be configured for methods using grepo's GenericQuery annotation, like this:

Hibernate also offers a similiar feature known as result transformation. You can use this feature using grepo's HibernateQueryOption annotation by configuring the resultTransformer property accordingly.

Implicit result conversion

Furthermore grepo supports so called implicit result conversion, which means that in some cases (if necessary) the result will be converted automatically (without configuration). The framework uses a org.codehaus.grepo.core.converter.ResultConverterFindingService. Grepo's default implementation org.codehaus.grepo.core.converter.ResultConverterFindingServiceImpl uses a registry (basically a Map) to map (return) types to ResultConverter classes. The ResultConverterFindingService checks if conversion is required (for instance if query result is not compatible with method return type) and uses the registry in order to retrieve the appropriate converter to use. In its default configuration grepo knows the following ResultConverters:

  • org.codehaus.grepo.core.converter.ResultToBooleanConverter: This implementation is used to convert objects to instances of java.lang.Boolean.
  • org.codehaus.grepo.core.converter.ResultToLongConverter: This implementation is used to convert objects to instances of java.lang.Long.
  • org.codehaus.grepo.core.converter.ResultToIntegerConverter: This implementation is used to convert objects to instances of java.lang.Integer.

Suppose we need functionality for our demo application wich allows to register new users. As we have seen the email property is unique and thus must not already exist in the database. We could use the following method so we can check for existing email-addresses:

Note that the query returns 0 or 1 (because email is unique) and the method's return type is boolean. Executing this method (and having grepo logger level set to DEBUG) the following will be logged:

15:43:30,212 DEBUG [DefaultHibernateRepository:155] - Query result is '0'
15:43:30,222 DEBUG [ResultConverterFindingStrategyImpl:97] - Found converter
                   'org.codehaus.grepo.core.converter.ResultToBooleanConverter' for conversion from
                   'java.lang.Long' to 'boolean'
15:43:30,223 DEBUG [ResultConversionServiceImpl:51] - Doing conversion for result (class=java.lang.Long, value=0)
                   with converter 'org.codehaus.grepo.core.converter.ResultToBooleanConverter'
15:43:30,227 DEBUG [ResultConversionServiceImpl:57] - Conversion result is 'false'

Result validation

The framework supports result validation functionality. For this grepo uses implementations of the org.codehaus.grepo.core.validator.ResultValidator interface. ResultValidators can be used to validate the result of a method invocation. Note that result validation is performed after result conversion. ResultValidators can be configured for methods using grepo's GenericQuery annotation like this:

Transaction Handling

By default grepo does not handle transactions at all (this should be done by your service layer really) - you configure transaction handling for your repository objects (DAOs) as you would do normally with plain Spring/Hibernate. However grepo also optionally supports transaction handling using spring's transaction template. If you don't want grepo to handle transactions you just do not configure the transactionTemplate property:

Additionally you can configure the readOnlyTransactionTemplate property. Doing so grepo will use the read-only template for executing read-only operations. If no read-only template is defined, then grepo will use the transactionTemplate (if configured) for both read- and write-operations. Here is an example of the abstractRepository bean with both templates defined:

A word about conventions

The grepo framework was designed around the convention over configuration paradigm. You configure the grepo framework with your guidelines (if the default configuration does not meet your needs) for your data access layer and grepo will then apply those rules transparently. For instance, we have seen that grepo can guarantee that all methods which execute "list-queries" have to start with the prefix "list" and also have to return an instance of java.util.List. If you want to break the rules you could for instance configure a method as follows:

Note that the method name does not meet the conventions. The method starts with "get" but actually a "list-query" is required because of the method's return type (java.util.List). Here you can see the power of the convention over configuration paradigm and grepo in general. Breaking the rules is still possible but you have to know what you're doing and furthermore have additional configuration overhead.

  • No labels