Spring Data also provides advanced integration with third-party projects and technologies.
Querydsl
The Querydsl project (http://querydsl.org) aims to bring LINQ-style (language integrated queries) capabilities to the Java platform. It is a bit like the JPA criteria API but not tied to JPA, a lot less verbose and thus much more comfortable to use.
Querydsl is centered around a meta-model generated from domain classes that can be used to define predicates over the entities. These predicates can then be executed via a Spring Data Repository.
Metamodel Generation
The meta-model is generated through a step in the build process. Querydsl currently provides integration for Maven and Ant. Here's the sample configuration for JPA and Maven:
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>apt-maven-plugin</artifactId>
<version>1.0.9</version>
<executions>
<execution>
<goals>
<goal>process</goal>
</goals>
<configuration>
<outputDirectory>
target/generated-sources/java
</outputDirectory>
<processor>
com.mysema.query.apt.jpa.JPAAnnotationProcessor
</processor>
</configuration>
</execution>
</executions>
</plugin>
Predicate Definition
This configuration will inspect the JPA mapping annotations and create meta-model classes that can now be used as follows:
QCustomer customer = Qcustomer.customer;
Predicate predicate = customer.lastname.endswith(…).
or(customer.firstname.startsWith(…));
You see we can easily define constraints on the entities using a fluent API.
Predicate Execution
To eventually execute the predicates just defined, Spring Data provides a QueryDslPredicateExecutor interface that your repository can extend to expose the necessary API to your clients:
interface CustomerRepository extends Repository<Customer, Long>,
QueryDslPredicateExecutor<Customer, Long> {
}
A client would then look as follows:
class RepositoryClient {
@Autowired CustomerRepository repository;
public void buisnessMethod(String firstname, String lastname) {
QCustomer customer = Qcustomer.customer;
List<Customer> customers = repository.findAll(
customer.lastname.endswith(lastname).or(
customer.firstname.startsWith(firstname));
…
}
}
CDI Integration
The Spring Data modules discussed here all ship with a CDI extension that allows using the repository abstraction in a JavaEE environment that uses CDI for dependency injection.
- Have the necessary Spring Data JARs in your classpath.
- Declare necessary infrastructure components as CDI beans using the @Produces/@Disposes annotation.
- Declare repository interfaces as described above.
- Inject the repository instances into your CDI managed beans using @Inject.
JPA
In JPA the core infrastructure abstraction is the EntityManager. Working in a standalone CDI environment the EntityManager is created by an EntityManagerFactory. It can be exposed to the infrastructure as follows:
class EntityManagerFactoryProducer {
@Produces
@ApplicationScoped
public EntityManagerFactory createEntityManagerFactory() {
return Persistence.createEntityManagerFactory("my-pu");
}
public void close(
@Disposes EntityManagerFactory entityManagerFactory) {
entityManagerFactory.close();
}
}
Once that is done, you can get a hold of your repository instance by injecting it into your CDI bean:
class RepositoryClient {
@Inject
CustomerRepository repository;
public void businessMethod(EmailAddress email) {
Customer customer = repository.findByEmailAddress(email);
…
}
}
MongoDB
In the case of MongoDB, essentially all you need to change is the infrastructure setup. Instead of configuring an EntityManagerFactory you expose a MongoTemplate instance to CDI:
class MongoTemplateProducer {
@Produces
@ApplicationScoped
public MongoOperations createMongoTemplate() throws
UnknownHostException, MongoException {
MongoDbFactory factory =
new SimpleMongoDbFactory(new Mongo(), "database");
return new MongoTemplate(factory);
}
}
The injection of the repository into the client looks identical to the code shown for JPA:
class RepositoryClient {
@Inject
CustomerRepository repository;
public void businessMethod(EmailAddress email) {
Customer customer = repository.findByEmailAddress(email);
…
}
}
Integration with Spring and SpringMVC
The Spring Data Commons module ships with a variety of support classes that ease the general interaction with the core Spring framework and Spring MVC in particular. Here are the interesting types:
Base interface |
Description |
DomainClassConverter |
Allows conversion of a String ID into the entity with the given ID using the findOne(…) method on the repository registered for the domain type. |
DomainClassPropertyEditor |
Legacy version of DomainClassConverter. |
SortHandlerMethodArgumentResolver |
Automatically creates a Sort instance from HttpServletRequest parameters. |
PageableHandlerMethodArgumentResolver |
Automatically creates a Pageable instance from HttpServletRequest parameters. |
Converter / PropertyEditor Support
SpringMVC controller methods are extremely flexible and allow you to refer to URI path segments, request parameters etc. in a type-safe way. With the DomainClassConverter registered in the application context, you can write a controller method like this:
@Controller
public class CustomerController {
@RequestMapping("/customers/{id}")
public String showUserForm(
@PathVariable("id") Customer customer, Model model) {
…
}
}
The interesting part here is that for a call to /users/47, you immediately get the User instance with ID 47 handed into the method in case it is managed by a Spring Data repository exposing a findOne(…) method. You can simply register the converter through the appropriate callback method in your SpringMVC configuration:
class WebConfiguration extends WebMvcConfigurationSupport {
@Bean
public DomainClassConverter<?> domainClassConverter() {
return new DomainClassConverter<FormattingConversionService>(
mvcConversionService());
}
}
Pagination and Sorting
The same approach can be used to transparently create instances for Sort and Pageable from request parameters. This doesn't even require the usage of the repository abstraction. All you need to do is configure the appropriate HandlerMethodArgumentResolver implementations in your SpringMVC configuration:
class WebConfiguration extends WebMvcConfigurationSupport {
protected void addArgumentResolvers(
List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new SortHandlerMethodArgumentResolver());
resolvers.add(new PageableHandlerMethodArgumentResolver());
}
}
This allows SpringMVC controller methods that look like this:
@Controller
public class UserController {
@Autowired CustomerRepository repository;
@RequestMapping("/users")
public String showUserForm(Pageable pageable, Model model) {
Page<Customer> customers = repository.findAll(pageable);
…
}
}
The resolver implementations can be customized as you see fit but by default they will lookup the following request parameters to build up the Pageable and Sort instances:
Parameter |
Description |
page (0…1) |
The number of the page to be retrieved. (0-indexed, defaults to 0) |
size (0…1) |
The size of the page to be retrieved. (defaults to 20) |
sort (0…n) |
A sort expression with comma-separated field references, terminated by asc or desc to define the order (optional). E.g /?sort=firstname,asc&lastname=desc |
To be able to selectively customize the defaults for Pageable and Sort instances, the controller method arguments can be annotated with the following annotations
Annotation |
Description |
@Qualifier |
Allows definition of a qualifier in case multiple pages or sorts need to be handed into a single method. Will get prepended to the parameters (foo_page, foo_size etc.). |
@PageableDefault |
Customizes the defaults to be used for the Pageable instance in case no request parameters are present. If not set, global defaults apply. |
@SortDefault |
Customizes the defaults to be used for the Sort instance in case no request parameters are present. If not set, the Sort will be null. |
{{ parent.title || parent.header.title}}
{{ parent.tldr }}
{{ parent.linkDescription }}
{{ parent.urlSource.name }}