Added general repository documentation and included into the main documentation source file.
This commit is contained in:
@@ -15,6 +15,10 @@
|
||||
<firstname>Thomas</firstname>
|
||||
<surname>Risberg</surname>
|
||||
</author>
|
||||
<author>
|
||||
<firstname>Oliver</firstname>
|
||||
<surname>Gierke</surname>
|
||||
</author>
|
||||
</authorgroup>
|
||||
|
||||
<legalnotice>
|
||||
@@ -38,6 +42,7 @@
|
||||
This part of the reference documentation details the ...
|
||||
</para>
|
||||
</partintro>
|
||||
<xi:include href="repositories.xml" />
|
||||
<!-- <xi:include href="data-commons.xml"/> -->
|
||||
</part>
|
||||
</book>
|
||||
|
||||
673
src/docbkx/repositories.xml
Normal file
673
src/docbkx/repositories.xml
Normal file
@@ -0,0 +1,673 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
|
||||
<chapter id="repositories">
|
||||
<title>Repositories</title>
|
||||
|
||||
<section id="introduction">
|
||||
<title>Introduction</title>
|
||||
|
||||
<para>Implementing a data access layer of an application has been
|
||||
cumbersome for quite a while. Too much boilerplate code had to be written.
|
||||
Domain classes were anemic and haven't been designed in a real object
|
||||
oriented or domain driven manner.</para>
|
||||
|
||||
<para>Using both of these technologies makes developers life a lot easier
|
||||
regarding rich domain model's persistence. Nevertheless the amount of
|
||||
boilerplate code to implement repositories especially is still quite high.
|
||||
So the goal of the repository abstraction of Spring Data is to reduce the
|
||||
effort to implement data access layers for various persistence stores
|
||||
significantly</para>
|
||||
|
||||
<para>The following chapters will introduce the core concepts and
|
||||
interfaces of Spring Data repositories.</para>
|
||||
</section>
|
||||
|
||||
<section id="core-concepts">
|
||||
<title>Core concepts</title>
|
||||
|
||||
<para>The central interface in Spring Data repository abstraction is
|
||||
<interfacename>Repository</interfacename> (probably not that much of a
|
||||
surprise). It is typeable to the domain class to manage as well as the id
|
||||
type of the domain class and provides some sophisticated functionality
|
||||
around CRUD for the entity managed.</para>
|
||||
|
||||
<example id="repository">
|
||||
<title>Repository interface</title>
|
||||
|
||||
<programlistingco>
|
||||
<areaspec>
|
||||
<area coords="3" id="repository.save" />
|
||||
|
||||
<area coords="5" id="repository.find-by-id" />
|
||||
|
||||
<area coords="7" id="repository.find-all" />
|
||||
|
||||
<area coords="9" id="repository.find-all-pageable" />
|
||||
|
||||
<area coords="11" id="repository.count" />
|
||||
|
||||
<area coords="13" id="repository.delete" />
|
||||
|
||||
<area coords="15" id="repository.exists" />
|
||||
</areaspec>
|
||||
|
||||
<programlisting language="java">public interface Repository<T, ID extends Serializable> {
|
||||
|
||||
T save(T entity);
|
||||
|
||||
T findById(ID primaryKey);
|
||||
|
||||
List<T> findAll();
|
||||
|
||||
Page<T> findAll(Pageable pageable);
|
||||
|
||||
Long count();
|
||||
|
||||
void delete(T entity);
|
||||
|
||||
boolean exists(ID primaryKey);
|
||||
|
||||
// … more functionality omitted.
|
||||
}</programlisting>
|
||||
|
||||
<calloutlist>
|
||||
<callout arearefs="repository.save">
|
||||
<para>Saves the given entity.</para>
|
||||
</callout>
|
||||
|
||||
<callout arch="" arearefs="repository.find-by-id">
|
||||
<para>Returns the entity identified by the given id.</para>
|
||||
</callout>
|
||||
|
||||
<callout arearefs="repository.find-all">
|
||||
<para>Returns all entities.</para>
|
||||
</callout>
|
||||
|
||||
<callout arch="" arearefs="repository.find-all-pageable">
|
||||
<para>Returns a page of entities.</para>
|
||||
</callout>
|
||||
|
||||
<callout arearefs="repository.count">
|
||||
<para>Returns the number of entities.</para>
|
||||
</callout>
|
||||
|
||||
<callout arearefs="repository.delete">
|
||||
<para>Deletes the given entity.</para>
|
||||
</callout>
|
||||
|
||||
<callout arearefs="repository.exists">
|
||||
<para>Returns whether an entity with the given id exists.</para>
|
||||
</callout>
|
||||
</calloutlist>
|
||||
</programlistingco>
|
||||
</example>
|
||||
|
||||
<para>Usually we will have persistence technology specific sub-interfaces
|
||||
to include additional technology specific methods. We will now ship
|
||||
implementations for a variety of Spring Data modules that implement that
|
||||
interface.</para>
|
||||
</section>
|
||||
|
||||
<section id="query-methods">
|
||||
<title>Query methods</title>
|
||||
|
||||
<para>Next to standard CRUD functionality repositories are usually query
|
||||
the underlying datastore. With Spring Data declaring those queries becomes
|
||||
a four-step process (we use the JPA based module as example but that works
|
||||
the same way for other stores):</para>
|
||||
|
||||
<orderedlist>
|
||||
<listitem>
|
||||
<para>Declare an interface extending the technology specific
|
||||
Repository sub-interface and type it to the domain class it shall
|
||||
handle.</para>
|
||||
|
||||
<programlisting language="java">public interface PersonRepository extends JpaRepository<User, Long> { … }</programlisting>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Declare query methods on the interface.</para>
|
||||
|
||||
<programlisting language="java">List<Person> findByLastname(String lastname);</programlisting>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Setup Spring to create proxy instances for those
|
||||
interfaces.</para>
|
||||
|
||||
<programlisting language="xml"><?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans:beans xmlns:beans="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://www.springframework.org/schema/data/jpa
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/data/jpa
|
||||
http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
|
||||
|
||||
<repositories base-package="com.acme.repositories" />
|
||||
|
||||
</beans></programlisting>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Get the repository instance injected and use it.</para>
|
||||
|
||||
<programlisting language="java">public class SomeClient {
|
||||
|
||||
@Autowired private PersonRepoyitory repository;
|
||||
|
||||
public void doSomething() {
|
||||
List<Person> persons = repository.findByLastname("Matthews");
|
||||
}</programlisting>
|
||||
</listitem>
|
||||
</orderedlist>
|
||||
|
||||
<para>At this stage we barely scratched the surface of what's possible
|
||||
with the repositories but the general approach should be clear. Let's go
|
||||
through each of these steps and and figure out details and various options
|
||||
that you have at each stage.</para>
|
||||
|
||||
<section>
|
||||
<title>Defining repository interfaces</title>
|
||||
|
||||
<para>As a very first step you define a domain class specific repository
|
||||
interface to start with. It's got to be typed to the domain class and an
|
||||
ID type so that you get CRUD methods of the
|
||||
<interfacename>Repository</interfacename> interface tailored to
|
||||
it.</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Defining query methods</title>
|
||||
|
||||
<section>
|
||||
<title>Query lookup strategies</title>
|
||||
|
||||
<para>The next thing we have to discuss is the definition of query
|
||||
methods. There's roughly two main ways how the repository proxy is
|
||||
generally able to come up with the store specific query from the
|
||||
method name. The first option is to derive the quer from the method
|
||||
name directly, the second is using some kind of additionally created
|
||||
query. What detailed options are available pretty much depends on the
|
||||
actual store. However there's got to be some algorithm the decision
|
||||
which actual query to is made.</para>
|
||||
|
||||
<para>There's three strategies for the repository infrastructure to
|
||||
resolve the query. The strategy to be used can be configured at the
|
||||
namespace through the <code>query-lookup-strategy</code> attribute.
|
||||
However might be the case that some of the strategies are not
|
||||
supported for the specific datastore. Here are your options:</para>
|
||||
|
||||
<simplesect>
|
||||
<title>CREATE</title>
|
||||
|
||||
<para>This strategy will try to construct a store specific query
|
||||
from the query method's name. The general approach is to remove a
|
||||
given set of well-known prefixes from the method name and parse the
|
||||
rest of the method. Read more about query construction in <xref
|
||||
linkend="query-methods.query-creation" />.</para>
|
||||
</simplesect>
|
||||
|
||||
<simplesect>
|
||||
<title>USE_DECLARED_QUERY</title>
|
||||
|
||||
<para>This strategy tries to find a declared query which will be
|
||||
used for execution first. The query could be defined by an
|
||||
annotation somwhere or declared by other means. Please consult the
|
||||
documentation of the specific store to find out what options are
|
||||
available for that store. If the repository infrastructure does not
|
||||
find a declared query for the method at bootstrap time it will
|
||||
fail.</para>
|
||||
</simplesect>
|
||||
|
||||
<simplesect>
|
||||
<title>CREATE_IF_NOT_FOUND (default)</title>
|
||||
|
||||
<para>This strategy is actually a combination of the both mentioned
|
||||
above. It will try to lookup a declared query first but create a
|
||||
custom method name based query if no declared query was found. This
|
||||
is default lookup strategy and thus will be used if you don't
|
||||
configure anything explicitly. It allows quick query definition by
|
||||
method names but also custom tuning of these queries by introducing
|
||||
declared queries for those who need explicit tuning.</para>
|
||||
</simplesect>
|
||||
</section>
|
||||
|
||||
<section id="query-methods.query-creation">
|
||||
<title>Query creation</title>
|
||||
|
||||
<para>The query builder mechanism built into Spring Data repository
|
||||
infrastructue is useful to build constraining queries over entities of
|
||||
the repository. We will strip the prefixes <code>findBy</code>,
|
||||
<code>find</code>, <code>readBy</code>, <code>read</code>,
|
||||
<code>getBy</code> as well as <code>get</code> from the method and
|
||||
start parsing the rest of it. At a very basic level you can define
|
||||
conditions on entity properties and concatenate them with
|
||||
<code>AND</code> and <code>OR</code>.</para>
|
||||
|
||||
<example>
|
||||
<title>Query creation from method names</title>
|
||||
|
||||
<para><programlisting language="java">public interface PersonRepository extends JpaRepository<User, Long> {
|
||||
|
||||
List<Person> findByEmailAddressAndLastname(EmailAddress emailAddress, String lastname);
|
||||
}</programlisting></para>
|
||||
</example>
|
||||
|
||||
<para>The actual result of parsing that method will of course depend
|
||||
on the persistence store we create the query for. However there are
|
||||
some general things to notice. The expression are usually property
|
||||
traversals combined with operators that can be concatenated. As you
|
||||
can see in the example you can combine property expressions with And
|
||||
and Or. Beyond that you will get support for various operators like
|
||||
Between, LessThan, GreaterThan, Like for the property expressions. As
|
||||
the operators supported can vary from datastore to datastore please
|
||||
consult the according part of the reference documentation.</para>
|
||||
|
||||
<section>
|
||||
<title>Property expressions</title>
|
||||
|
||||
<para>Property expressions can just refer to a direct property of
|
||||
the managed entity (as you just saw in the example above. On query
|
||||
creation time we already make sure that the parsed property is at a
|
||||
property of the managed domain class. However you can also traverse
|
||||
nested properties to define constraints on. Assume
|
||||
<classname>Person</classname>s have <classname>Address</classname>es
|
||||
with <classname>ZipCode</classname>s. In that case a method name
|
||||
of</para>
|
||||
|
||||
<programlisting language="java">List<Person> findByAddressZipCode(ZipCode zipCode);</programlisting>
|
||||
|
||||
<para>will create the property traversal
|
||||
<code>x.address.zipCode</code>. The resolution algorithm starts with
|
||||
interpreting the entire part (<literal>AddressZipCode</literal>) as
|
||||
property and checks the domain class for a property with that name
|
||||
(uncapitalized). If it succeeds it just uses that. If not it starts
|
||||
splitting up the source at the camel case parts from the right side
|
||||
into a head and a tail and tries to find the according property,
|
||||
e.g. <literal>AddressZip</literal> and <literal>Code</literal>. If
|
||||
we find a property with that head we take the tail and continue
|
||||
building the tree down from there. As in our case the first split
|
||||
does not match we move the split point to the left
|
||||
(<literal>Address</literal>, <literal>ZipCode</literal>).</para>
|
||||
|
||||
<para>Now although this should work for most cases, there might be
|
||||
cases where the algorithm could select the wrong property. Suppose
|
||||
our <classname>Person</classname> class has a
|
||||
<code>addressZip</code> property as well. Then our algorithm would
|
||||
match in the first split round already and essentially choose the
|
||||
wrong property and finally fail (as the type of
|
||||
<classname>addressZip</classname> probably has no code property). To
|
||||
resolve this ambiguity you can use <literal>_</literal> inside your
|
||||
method name to manually define traversal points. So our method name
|
||||
would end up like so:</para>
|
||||
|
||||
<programlisting language="java">List<Person> findByAddress_ZipCode(ZipCode zipCode);
|
||||
</programlisting>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section id="special-parameters">
|
||||
<title>Special parameter handling</title>
|
||||
|
||||
<para>To hand parameters to your query you simply define method
|
||||
parameters as already seen in in examples above. Besides that we will
|
||||
recognizes certain specific types to apply pagination and sorting to
|
||||
your queries dynamically.</para>
|
||||
|
||||
<example>
|
||||
<title>Using Pageable and Sort in query methods</title>
|
||||
|
||||
<programlisting>Page<User> findByLastname(String lastname, Pageable pageable);
|
||||
|
||||
List<User> findByLastname(String lastname, Sort sort);
|
||||
|
||||
List<User> findByLastname(String lastname, Pageable pageable);</programlisting>
|
||||
</example>
|
||||
|
||||
<para>The first method allows you to pass a <code>Pageable</code>
|
||||
instance to the query method to dynamically add paging to your
|
||||
statically defined query. <code>Sorting</code> options are handed via
|
||||
the <interfacename>Pageable</interfacename> instance, too. If you only
|
||||
need sorting, simply add a <code>Sort</code> parameter to your method.
|
||||
As you also can see, simply returning a
|
||||
<interfacename>List</interfacename> is possible as well. We will then
|
||||
not retrieve the additional metadata required to build the actual
|
||||
<interfacename>Page</interfacename> instance but rather simply
|
||||
restrict the query to lookup only the given range of entities.</para>
|
||||
|
||||
<note>
|
||||
<para>To find out how many pages you get for a query entirely we
|
||||
have to trigger an additional count query. This will be derived from
|
||||
the query you actually trigger by default.</para>
|
||||
</note>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Creating repository instances</title>
|
||||
|
||||
<para>So now the question is how to create instances and bean
|
||||
definitions for the repository interfaces defined.</para>
|
||||
|
||||
<section>
|
||||
<title>Spring</title>
|
||||
|
||||
<para>The easiest way to do so is by using the Spring namespace that
|
||||
is shipped with each Spring Data module that supports the repository
|
||||
mechanism. Each of those includes a repositories element that allows
|
||||
you to simply define a base packge Spring shall scan for you.</para>
|
||||
|
||||
<programlisting language="java"><?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans:beans xmlns:beans="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://www.springframework.org/schema/data/jpa
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/data/jpa
|
||||
http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
|
||||
|
||||
<repositories base-package="com.acme.repositories" />
|
||||
|
||||
</beans:beans></programlisting>
|
||||
|
||||
<para>In this case we instruct Spring to scan
|
||||
<package>com.acme.repositories</package> and all it's sub packages for
|
||||
interfaces extending the appropriate
|
||||
<interfacename>Repository</interfacename> sub-interface (in this case
|
||||
<interfacename>JpaRepository</interfacename>). For each interface
|
||||
found it will register the presistence technology specific
|
||||
<interfacename>FactoryBean</interfacename> to create the according
|
||||
proxies that handle invocations of the query methods. Each of these
|
||||
beans will be registered under a bean name that is derived from the
|
||||
interface name, so an interface of
|
||||
<interfacename>UserRepository</interfacename> would be registered
|
||||
under <code>userRepository</code>. The <code>base-package</code>
|
||||
attribute allows to use wildcards, so that you can have a pattern of
|
||||
packages parsed. </para>
|
||||
|
||||
<simplesect>
|
||||
<title>Using filters</title>
|
||||
|
||||
<para>By default we will pick up every interface extending the
|
||||
persistence technology specific
|
||||
<interfacename>Repository</interfacename> sub-interface located
|
||||
underneath the configured base package and create a bean instance
|
||||
for it. However, you might want to gain finer grained control over
|
||||
which interfaces bean instances get created for. To do this we
|
||||
support the use of <code><include-filter /></code> and
|
||||
<code><exclude-filter /></code> elements inside
|
||||
<code><repositories /></code>. The semantics are exactly
|
||||
equivalent to the elements in Spring's context namespace. For
|
||||
details see <ulink
|
||||
url="http://static.springsource.org/spring/docs/2.5.x/reference/beans.html#beans-scanning-filters"
|
||||
vendor="">Spring reference documentation</ulink> on these
|
||||
elements.</para>
|
||||
|
||||
<para>E.g. to exclude certain interfaces from instantiation as
|
||||
repository, you could use the following configuration:</para>
|
||||
|
||||
<example>
|
||||
<title>Using exclude-filter element</title>
|
||||
|
||||
<programlisting language="xml"><repositories base-package="com.acme.repositories">
|
||||
<context:exclude-filter type="regex" expression=".*SomeRepository" />
|
||||
</repositories>
|
||||
</programlisting>
|
||||
|
||||
<para>This would exclude all interface ending on
|
||||
<interfacename>SomeRepository</interfacename> from being
|
||||
instantiated.</para>
|
||||
</example>
|
||||
</simplesect>
|
||||
|
||||
<simplesect>
|
||||
<title>Manual configuration</title>
|
||||
|
||||
<para>If you'd rather like to manually define which repository
|
||||
instances to create you can do this with nested <code><repository
|
||||
/></code> elements.</para>
|
||||
|
||||
<programlisting language="java"><repositories base-package="com.acme.repositories">
|
||||
<repository id="userRepository" />
|
||||
</repositories>
|
||||
</programlisting>
|
||||
</simplesect>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Standalone usage</title>
|
||||
|
||||
<para>You can also use the repository infrastructure outside of a
|
||||
Spring container usage. You will still need to have some of the Spring
|
||||
libraries on your classpath but you can generally setup repositories
|
||||
programatically as well. The Spring Data modules providing repository
|
||||
support ship a persistence technology specific RepositoryFactory that
|
||||
can be used as follows:</para>
|
||||
|
||||
<example>
|
||||
<title>Standalone usage of repository factory</title>
|
||||
|
||||
<programlisting>RepositoryFactorySupport factory = … // Instantiate factory here
|
||||
UserRepository repository = factory.getRepository(UserRepository.class</programlisting>
|
||||
</example>
|
||||
</section>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section id="custom-implementations">
|
||||
<title>Custom implementations</title>
|
||||
|
||||
<section id="single-repository-behaviour">
|
||||
<title>Adding behaviour to single repositories</title>
|
||||
|
||||
<para>Often it is necessary to provide a custom implementation for a few
|
||||
repository methods. Spring Data repositories easily allow provide custom
|
||||
repository code and integrate it with generic CRUD abstraction and query
|
||||
method functionality. To enrich a repository with custom functionality
|
||||
you have to define an interface and an implementation for that
|
||||
functionality first and let the repository interface you provided so far
|
||||
extend that custom interface.</para>
|
||||
|
||||
<example>
|
||||
<title>Interface for custom repository functionality</title>
|
||||
|
||||
<programlisting language="java">interface UserRepositoryCustom {
|
||||
|
||||
public void someCustomMethod(User user);
|
||||
}</programlisting>
|
||||
</example>
|
||||
|
||||
<example>
|
||||
<title>Implementation of custom repository functionality</title>
|
||||
|
||||
<para><programlisting language="java">class UserRepositoryImpl implements UserRepositoryCustom {
|
||||
|
||||
public void someCustomMethod(User user) {
|
||||
// Your custom implementation
|
||||
}
|
||||
}</programlisting>Note that the implementation itself does not depend on
|
||||
Spring Data and can be a regular Spring bean. So you can either use
|
||||
standard dependency injection behaviour to inject references to other
|
||||
beans, take part in aspects and so on.</para>
|
||||
</example>
|
||||
|
||||
<example>
|
||||
<title>Changes to the your basic repository interface</title>
|
||||
|
||||
<para><programlisting language="java">public interface UserRepository extends JpaRepository<User, Long>, UserRepositoryCustom {
|
||||
|
||||
// Declare query methods here
|
||||
}</programlisting>Let your standard repository interface extend the custom
|
||||
one. This makes CRUD and custom functionality available to
|
||||
clients.</para>
|
||||
</example>
|
||||
|
||||
<simplesect>
|
||||
<title>Configuration</title>
|
||||
|
||||
<para>If you use namespace configuration the repository infrastructure
|
||||
tries to autodetect custom implementations by looking up classes in
|
||||
the package we found a repository using the naming conventions
|
||||
appending the namespace element's attribute
|
||||
<code>repository-impl-postfix</code> to the classname. This suffix
|
||||
defaults to <code>Impl</code>.</para>
|
||||
|
||||
<example>
|
||||
<title>Configuration example</title>
|
||||
|
||||
<para><programlisting language="xml"><repositories base-package="com.acme.repository">
|
||||
<repository id="userRepository" />
|
||||
</repositories>
|
||||
|
||||
<repositories base-package="com.acme.repository" repository-impl-postfix="FooBar">
|
||||
<repository id="userRepository" />
|
||||
</repositories></programlisting></para>
|
||||
</example>
|
||||
|
||||
<para>The first configuration example will try to lookup a class
|
||||
<classname>com.acme.repository.UserRepositoryImpl</classname> to act
|
||||
as custom repository implementation, where the second example will try
|
||||
to lookup
|
||||
<classname>com.acme.repository.UserRepositoryFooBar</classname>.</para>
|
||||
</simplesect>
|
||||
|
||||
<simplesect>
|
||||
<title>Manual wiring</title>
|
||||
|
||||
<para>The approach above works perfectly well if your custom
|
||||
implementation uses annotation based configuration and autowring
|
||||
entirely as will be trated as any other Spring bean. If your customly
|
||||
implemented bean needs some special wiring you simply declare the bean
|
||||
and name it after the conventions just descibed. We will then pick up
|
||||
the custom bean by name rather than creating an own instance.</para>
|
||||
|
||||
<example>
|
||||
<title>Manual wiring of custom implementations (I)</title>
|
||||
|
||||
<programlisting language="xml"><repositories base-package="com.acme.repository">
|
||||
<repository id="userRepository" />
|
||||
</repositories>
|
||||
|
||||
<beans:bean id="userRepositoryImpl" class="…">
|
||||
<!-- further configuration -->
|
||||
</beans:bean></programlisting>
|
||||
|
||||
<para>This also works if you use automatic repository lookup without
|
||||
defining single <code><repository /></code> elements.</para>
|
||||
</example>
|
||||
|
||||
<para>In case you are not in control of the implementation bean name
|
||||
(e.g. if you wrap a generic repository facade around an existing
|
||||
repository implementation) you can explicitly tell the
|
||||
<code><repository /></code> element which bean to use as custom
|
||||
implementation by using the <code>repository-impl-ref</code>
|
||||
attribute.</para>
|
||||
|
||||
<example>
|
||||
<title>Manual wiring of custom implementations (II)</title>
|
||||
|
||||
<para><programlisting language="xml"><repositories base-package="com.acme.repository">
|
||||
<repository id="userRepository" repository-impl-ref="customRepositoryImplementation" />
|
||||
</repositories>
|
||||
|
||||
<bean id="customRepositoryImplementation" class="…">
|
||||
<!-- further configuration -->
|
||||
</bean></programlisting></para>
|
||||
</example>
|
||||
</simplesect>
|
||||
</section>
|
||||
|
||||
<section id="custom-behaviour-for-all-repositories">
|
||||
<title>Adding custom behaviour to all repositories</title>
|
||||
|
||||
<para>In other cases you might want to add a single method to all of
|
||||
your repository interfaces. So the approach just shown is not feasible.
|
||||
The first step to achieve this is adding and intermediate interface to
|
||||
declare the shared behaviour</para>
|
||||
|
||||
<example>
|
||||
<title>An interface declaring custom shared behaviour</title>
|
||||
|
||||
<para><programlisting language="java">public interface MyRepository<T, ID extends Serializable>
|
||||
extends JpaRepository<T, ID> {
|
||||
|
||||
void sharedCustomMethod(ID id);
|
||||
}</programlisting></para>
|
||||
</example>
|
||||
|
||||
<para>Now your individual repository interfaces will extend this
|
||||
intermediate interface to include the functionality declared. The second
|
||||
step is to create an implementation of this interface that extends the
|
||||
persistence technology specific repository base class which will act as
|
||||
custom base class for the repository proxies then.</para>
|
||||
|
||||
<note>
|
||||
<para>If you're using automatic repository interface detection using
|
||||
the Spring namespace using the interface just as is will cause Spring
|
||||
trying to create an instance of
|
||||
<interfacename>MyRepository</interfacename>. This is of course not
|
||||
desired as it just acts as indermediate between
|
||||
<interfacename>Repository</interfacename> and the actual repository
|
||||
interfaces you want to define for each entity. To exclude an interface
|
||||
extending <interfacename>Repository</interfacename> from being
|
||||
instantiated as repository instance annotate it with
|
||||
<interfacename>@NoRepositoryBean</interfacename>.</para>
|
||||
</note>
|
||||
|
||||
<example>
|
||||
<title>Custom repository base class</title>
|
||||
|
||||
<programlisting language="java">public class MyRepositoryImpl<T, ID extends Serializable>
|
||||
extends SimpleJpaRepository<T, ID> implements MyRepository<T, ID> {
|
||||
|
||||
public void sharedCustomMethod(ID id) {
|
||||
// implementation goes here
|
||||
}
|
||||
}</programlisting>
|
||||
</example>
|
||||
|
||||
<para>The last step to get this implementation used as base class for
|
||||
Spring Data repositores is replacing the standard
|
||||
<classname>RepositoryFactoryBean</classname> with a custom one using a
|
||||
custom <classname>RepositoryFactory</classname> that in turn creates
|
||||
instances of your <classname>MyRepositoryImpl</classname> class.</para>
|
||||
|
||||
<example>
|
||||
<title>Custom repository factory bean</title>
|
||||
|
||||
<programlisting language="java">public class MyRepositoryFactoryBean<T extends JpaRepository<?, ?>
|
||||
extends JpaRepositoryFactoryBean<T> {
|
||||
|
||||
protected RepositoryFactorySupport getRepositoryFactory(…) {
|
||||
return new MyRepositoryFactory(…);
|
||||
}
|
||||
|
||||
private static class MyRepositoryFactory extends JpaRepositoryFactory{
|
||||
|
||||
public MyRepositoryImpl getTargetRepository(…) {
|
||||
return new MyRepositoryImpl(…);
|
||||
}
|
||||
|
||||
public Class<? extends RepositorySupport> getRepositoryClass() {
|
||||
return MyRepositoryImpl.class;
|
||||
}
|
||||
}
|
||||
}</programlisting>
|
||||
</example>
|
||||
|
||||
<para>Finally you can either declare beans of the custom factory
|
||||
directly or use the <code>factory-class</code> attribute of the Spring
|
||||
namespace to tell the repository infrastructure to use your custom
|
||||
factory implementation.</para>
|
||||
|
||||
<example>
|
||||
<title>Using the custom factory with the namespace</title>
|
||||
|
||||
<programlisting language="xml"><repositories base-package="com.acme.repository"
|
||||
factory-class="com.acme.MyRepositoryFactoryBean" /></programlisting>
|
||||
</example>
|
||||
</section>
|
||||
</section>
|
||||
</chapter>
|
||||
Reference in New Issue
Block a user