You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1025 lines
39 KiB
1025 lines
39 KiB
<?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="jpa.repositories"> |
|
<title>JPA Repositories</title> |
|
|
|
<abstract> |
|
<para>This chapter includes details of the JPA repository |
|
implementation.</para> |
|
</abstract> |
|
|
|
<section id="jpa.introduction"> |
|
<title>Introduction</title> |
|
|
|
<section id="jpa.namespace"> |
|
<title>Spring namespace</title> |
|
|
|
<para>The JPA module of Spring Data contains a custom namespace that |
|
allows defining repository beans. It also contains certain features and |
|
element attributes that are special to JPA. Generally the JPA |
|
repositories can be set up using the <code>repositories</code> |
|
element:</para> |
|
|
|
<example> |
|
<title>Setting up JPA repositories using the namespace</title> |
|
|
|
<programlisting language="xml"><?xml version="1.0" encoding="UTF-8"?> |
|
<beans xmlns="http://www.springframework.org/schema/beans" |
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|
xmlns:jpa="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"> |
|
|
|
<jpa:repositories base-package="com.acme.repositories" /> |
|
|
|
</beans></programlisting> |
|
</example> |
|
|
|
<para>Using this element looks up Spring Data repositories as described |
|
in <xref linkend="repositories.create-instances"/>. Beyond that it |
|
activates persistence exception translation for all beans annotated with |
|
<interfacename>@Repository</interfacename> to let exceptions being |
|
thrown by the JPA presistence providers be converted into Spring's |
|
<classname>DataAccessException</classname> hierarchy.</para> |
|
|
|
<simplesect id="jpa.namespace-attributes"> |
|
<title>Custom namespace attributes</title> |
|
|
|
<para>Beyond the default attributes of the <code>repositories</code> |
|
element the JPA namespace offers additional attributes to gain more |
|
detailled control over the setup of the repositories:</para> |
|
|
|
<table> |
|
<title>Custom JPA-specific attributes of the repositories |
|
element</title> |
|
|
|
<tgroup cols="2"> |
|
<tbody> |
|
<row> |
|
<entry><code>entity-manager-factory-ref</code></entry> |
|
|
|
<entry>Explicitly wire the |
|
<interfacename>EntityManagerFactory</interfacename> to be used |
|
with the repositories being detected by the |
|
<code>repositories</code> element. Usually used if multiple |
|
<interfacename>EntityManagerFactory</interfacename> beans are |
|
used within the application. If not configured we will |
|
automatically lookup the single |
|
<interfacename>EntityManagerFactory</interfacename> configured |
|
in the |
|
<interfacename>ApplicationContext</interfacename>.</entry> |
|
</row> |
|
|
|
<row> |
|
<entry><code>transaction-manager-ref</code></entry> |
|
|
|
<entry>Explicitly wire the |
|
<interfacename>PlatformTransactionManager</interfacename> to |
|
be used with the repositories being detected by the |
|
<code>repositories</code> element. Usually only necessary if |
|
multiple transaction managers and/or |
|
<interfacename>EntityManagerFactory</interfacename> beans have |
|
been configured. Default to a single defined |
|
<interfacename>PlatformTransactionManager</interfacename> |
|
inside the current |
|
<interfacename>ApplicationContext</interfacename>.</entry> |
|
</row> |
|
</tbody> |
|
</tgroup> |
|
</table> |
|
</simplesect> |
|
</section> |
|
</section> |
|
|
|
<section id="jpa.query-methods"> |
|
<title>Query methods</title> |
|
|
|
<section id="jpa.sample-app.finders.strategies"> |
|
<title>Query lookup strategies</title> |
|
|
|
<para>The JPA module supports defining a query manually as String or |
|
have it being derived from the method name.</para> |
|
|
|
<simplesect> |
|
<title>Declared queries</title> |
|
|
|
<para>Although getting a query derived from the method name is quite |
|
convenient, one might face the situation in which either the method |
|
name parser does not support the keyword one wants to use or the |
|
method name would get unnecessarily ugly. So you can either use JPA |
|
named queries through a naming convention (see <xref |
|
linkend="jpa.query-methods.named-queries"/> for more information) or |
|
rather annotate your query method with |
|
<interfacename>@Query</interfacename> (see <xref |
|
linkend="jpa.query-methods.at-query"/> for details).</para> |
|
</simplesect> |
|
</section> |
|
|
|
<section id="jpa.query-methods.query-creation"> |
|
<title>Query creation</title> |
|
|
|
<para>Generally the query creation mechanism for JPA works as described |
|
in <xref linkend="repositories.query-methods"/>. Here's a short example |
|
of what a JPA query method translates into:<example> |
|
<title>Query creation from method names</title> |
|
|
|
<para><programlisting language="java">public interface UserRepository extends Repository<User, Long> { |
|
|
|
List<User> findByEmailAddressAndLastname(String emailAddress, String lastname); |
|
}</programlisting>We will create a query using the JPA criteria API from this |
|
but essentially this translates into the following query:</para> |
|
|
|
<programlisting>select u from User u where u.emailAddress = ?1 and u.lastname = ?2</programlisting> |
|
|
|
<para>Spring Data JPA will do a property check and traverse nested |
|
properties as described in <xref |
|
linkend="repositories.query-methods.property-expressions"/>. Here's |
|
an overview of the keywords supported for JPA and what a method |
|
containing that keyword essentially translates to.</para> |
|
</example></para> |
|
|
|
<para><table> |
|
<title>Supported keywords inside method names</title> |
|
|
|
<tgroup cols="3"> |
|
<colspec colwidth="1*"/> |
|
|
|
<colspec colwidth="2*"/> |
|
|
|
<colspec colwidth="3*"/> |
|
|
|
<thead> |
|
<row> |
|
<entry>Keyword</entry> |
|
|
|
<entry>Sample</entry> |
|
|
|
<entry>JPQL snippet</entry> |
|
</row> |
|
</thead> |
|
|
|
<tbody> |
|
<row> |
|
<entry><code>And</code></entry> |
|
|
|
<entry><code>findByLastnameAndFirstname</code></entry> |
|
|
|
<entry><code>… where x.lastname = ?1 and x.firstname = |
|
?2</code></entry> |
|
</row> |
|
|
|
<row> |
|
<entry><code>Or</code></entry> |
|
|
|
<entry><code>findByLastnameOrFirstname</code></entry> |
|
|
|
<entry><code>… where x.lastname = ?1 or x.firstname = |
|
?2</code></entry> |
|
</row> |
|
|
|
<row> |
|
<entry><code>Between</code></entry> |
|
|
|
<entry><code>findByStartDateBetween</code></entry> |
|
|
|
<entry><code>… where x.startDate between 1? and |
|
?2</code></entry> |
|
</row> |
|
|
|
<row> |
|
<entry><code>LessThan</code></entry> |
|
|
|
<entry><code>findByAgeLessThan</code></entry> |
|
|
|
<entry><code>… where x.age < ?1</code></entry> |
|
</row> |
|
|
|
<row> |
|
<entry><code>GreaterThan</code></entry> |
|
|
|
<entry><code>findByAgeGreaterThan</code></entry> |
|
|
|
<entry><code>… where x.age > ?1</code></entry> |
|
</row> |
|
|
|
<row> |
|
<entry><code>After</code></entry> |
|
|
|
<entry><code>findByStartDateAfter</code></entry> |
|
|
|
<entry><code>… where x.startDate > ?1</code></entry> |
|
</row> |
|
|
|
<row> |
|
<entry><code>Before</code></entry> |
|
|
|
<entry><code>findByStartDateBefore</code></entry> |
|
|
|
<entry><code>… where x.startDate < ?1</code></entry> |
|
</row> |
|
|
|
<row> |
|
<entry><code>IsNull</code></entry> |
|
|
|
<entry><code>findByAgeIsNull</code></entry> |
|
|
|
<entry><code>… where x.age is null</code></entry> |
|
</row> |
|
|
|
<row> |
|
<entry><code>IsNotNull,NotNull</code></entry> |
|
|
|
<entry><code>findByAge(Is)NotNull</code></entry> |
|
|
|
<entry><code>… where x.age not null</code></entry> |
|
</row> |
|
|
|
<row> |
|
<entry><code>Like</code></entry> |
|
|
|
<entry><code>findByFirstnameLike</code></entry> |
|
|
|
<entry><code>… where x.firstname like ?1</code></entry> |
|
</row> |
|
|
|
<row> |
|
<entry><code>NotLike</code></entry> |
|
|
|
<entry><code>findByFirstnameNotLike</code></entry> |
|
|
|
<entry><code>… where x.firstname not like ?1</code></entry> |
|
</row> |
|
|
|
<row> |
|
<entry><code>StartingWith</code></entry> |
|
|
|
<entry><code>findByFirstnameStartingWith</code></entry> |
|
|
|
<entry><code>… where x.firstname like ?1</code> (parameter |
|
bound with prepended <code>%</code>)</entry> |
|
</row> |
|
|
|
<row> |
|
<entry><code>ngWith</code></entry> |
|
|
|
<entry><code>findByFirstnameEndingWith</code></entry> |
|
|
|
<entry><code>… where x.firstname like ?1</code> (parameter |
|
bound with appended <code>%</code>)</entry> |
|
</row> |
|
|
|
<row> |
|
<entry><code>Containing</code></entry> |
|
|
|
<entry><code>findByFirstnameContaining</code></entry> |
|
|
|
<entry><code>… where x.firstname like ?1</code> (parameter |
|
bound wrapped in <code>%</code>)</entry> |
|
</row> |
|
|
|
<row> |
|
<entry><code>OrderBy</code></entry> |
|
|
|
<entry><code>findByAgeOrderByLastnameDesc</code></entry> |
|
|
|
<entry><code>… where x.age = ?1 order by x.lastname |
|
desc</code></entry> |
|
</row> |
|
|
|
<row> |
|
<entry><code>Not</code></entry> |
|
|
|
<entry><code>findByLastnameNot</code></entry> |
|
|
|
<entry><code>… where x.lastname <> ?1</code></entry> |
|
</row> |
|
|
|
<row> |
|
<entry><code>In</code></entry> |
|
|
|
<entry><code>findByAgeIn(Collection<Age> |
|
ages)</code></entry> |
|
|
|
<entry><code>… where x.age in ?1</code></entry> |
|
</row> |
|
|
|
<row> |
|
<entry><code>NotIn</code></entry> |
|
|
|
<entry><code>findByAgeNotIn(Collection<Age> |
|
age)</code></entry> |
|
|
|
<entry><code>… where x.age not in ?1</code></entry> |
|
</row> |
|
|
|
<row> |
|
<entry><code>True</code></entry> |
|
|
|
<entry><code>findByActiveTrue()</code></entry> |
|
|
|
<entry><code>… where x.active = true</code></entry> |
|
</row> |
|
|
|
<row> |
|
<entry><code>False</code></entry> |
|
|
|
<entry><code>findByActiveFalse()</code></entry> |
|
|
|
<entry><code>… where x.active = false</code></entry> |
|
</row> |
|
</tbody> |
|
</tgroup> |
|
</table><note> |
|
<para><code>In</code> and <code>NotIn</code> also take any subclass |
|
of <interfacename>Collection</interfacename> as parameter as well as |
|
arrays or varargs. For other syntactical versions of the very same |
|
logical operator check <xref |
|
linkend="repository-query-keywords"/>.</para> |
|
</note></para> |
|
</section> |
|
|
|
<section id="jpa.query-methods.named-queries"> |
|
<title>Using JPA NamedQueries</title> |
|
|
|
<note> |
|
<para>The examples use simple <code><named-query /></code> |
|
element and <code>@NamedQuery</code> annotation. The queries for these |
|
configuration elements have to be defined in JPA query language. Of |
|
course you can use <code><named-native-query /></code> or |
|
<code>@NamedNativeQuery</code> too. These elements allow you to define |
|
the query in native SQL by losing the database platform |
|
independence.</para> |
|
</note> |
|
|
|
<simplesect> |
|
<title>XML named query definition</title> |
|
|
|
<para>To use XML configuration simply add the necessary |
|
<code><named-query /></code> element to the |
|
<filename>orm.xml</filename> JPA configuration file located in |
|
<filename>META-INF</filename> folder of your classpath. Automatic |
|
invocation of named queries is enabled by using some defined naming |
|
convention. For more details see below.</para> |
|
|
|
<example> |
|
<title>XML named query configuration</title> |
|
|
|
<programlisting language="xml"><named-query name="User.findByLastname"> |
|
<query>select u from User u where u.lastname = ?1</query> |
|
</named-query></programlisting> |
|
</example> |
|
|
|
<para>As you can see the query has a special name which will be used |
|
to resolve it at runtime.</para> |
|
</simplesect> |
|
|
|
<simplesect> |
|
<title>Annotation configuration</title> |
|
|
|
<para>Annotation configuration has the advantage of not needing |
|
another configuration file to be edited, probably lowering maintenance |
|
costs. You pay for that benefit by the need to recompile your domain |
|
class for every new query declaration.</para> |
|
|
|
<example> |
|
<title>Annotation based named query configuration</title> |
|
|
|
<programlisting language="java">@Entity |
|
@NamedQuery(name = "User.findByEmailAddress", |
|
query = "select u from User u where u.emailAddress = ?1") |
|
public class User { |
|
|
|
}</programlisting> |
|
</example> |
|
</simplesect> |
|
|
|
<simplesect> |
|
<title>Declaring interfaces</title> |
|
|
|
<para>To allow execution of these named queries all you need to do is |
|
to specify the <interfacename>UserRepository</interfacename> as |
|
follows:</para> |
|
|
|
<example> |
|
<title>Query method declaration in UserRepository</title> |
|
|
|
<programlisting language="java">public interface UserRepository extends JpaRepository<User, Long> { |
|
|
|
List<User> findByLastname(String lastname); |
|
|
|
User findByEmailAddress(String emailAddress); |
|
}</programlisting> |
|
</example> |
|
|
|
<para>Spring Data will try to resolve a call to these methods to a |
|
named query, starting with the simple name of the configured domain |
|
class, followed by the method name separated by a dot. So the example |
|
here would use the named queries defined above instead of trying to |
|
create a query from the method name.</para> |
|
</simplesect> |
|
</section> |
|
|
|
<section id="jpa.query-methods.at-query"> |
|
<title>Using @Query</title> |
|
|
|
<para>Using named queries to declare queries for entities is a valid |
|
approach and works fine for a small number of queries. As the queries |
|
themselves are tied to the Java method that executes them you actually |
|
can bind them directly using the Spring Data JPA <code>@Query</code> |
|
annotation rather than annotating them to the domain class. This will |
|
free the domain class from persistence specific information and |
|
co-locate the query to the repository interface.</para> |
|
|
|
<para>Queries annotated to the query method will take precedence over |
|
queries defined using <code>@NamedQuery</code> or named queries declared |
|
in <filename>orm.xml</filename>.</para> |
|
|
|
<example> |
|
<title>Declare query at the query method using @Query</title> |
|
|
|
<programlisting language="java">public interface UserRepository extends JpaRepository<User, Long> { |
|
|
|
@Query("select u from User u where u.emailAddress = ?1") |
|
User findByEmailAddress(String emailAddress); |
|
}</programlisting> |
|
</example> |
|
|
|
<simplesect> |
|
<title>Native queries</title> |
|
|
|
<para>The <interfacename>@Query</interfacename> annotation allows to |
|
execute native queries by setting the <code>nativeQuery</code> flag to |
|
true. Note, that we currently don't support execution of pagination or |
|
dynamic sorting for native queries as we'd have to manipulate the |
|
actual query declared and we cannot do this reliably for native |
|
SQL.</para> |
|
</simplesect> |
|
</section> |
|
|
|
<section id="jpa.named-parameters"> |
|
<title>Using named parameters</title> |
|
|
|
<para>By default Spring Data JPA will use position based parameter |
|
binding as described in all the samples above. This makes query methods |
|
a little error prone to refactoring regarding the parameter position. To |
|
solve this issue you can use <code>@Param</code> annotation to give a |
|
method parameter a concrete name and bind the name in the query:</para> |
|
|
|
<example> |
|
<title>Using named parameters</title> |
|
|
|
<programlisting language="java">public interface UserRepository extends JpaRepository<User, Long> { |
|
|
|
@Query("select u from User u where u.firstname = :firstname or u.lastname = :lastname") |
|
User findByLastnameOrFirstname(@Param("lastname") String lastname, |
|
@Param("firstname") String firstname); |
|
}</programlisting> |
|
|
|
<para>Note that the method parameters are switched according to the |
|
occurrence in the query defined.</para> |
|
</example> |
|
</section> |
|
|
|
<section id="jpa.modifying-queries"> |
|
<title>Modifying queries</title> |
|
|
|
<para>All the sections above describe how to declare queries to access a |
|
given entity or collection of entities. Of course you can add custom |
|
modifying behaviour by using facilities described in <xref |
|
linkend="repositories.custom-implementations"/>. As this approach is |
|
feasible for comprehensive custom functionality, you can achieve the |
|
execution of modifying queries that actually only need parameter binding |
|
by annotating the query method with <code>@Modifying</code>:</para> |
|
|
|
<example> |
|
<title>Declaring manipulating queries</title> |
|
|
|
<programlisting language="java">@Modifying |
|
@Query("update User u set u.firstname = ?1 where u.lastname = ?2") |
|
int setFixedFirstnameFor(String firstname, String lastname);</programlisting> |
|
</example> |
|
|
|
<para>This will trigger the query annotated to the method as updating |
|
query instead of a selecting one. As the |
|
<interfacename>EntityManager</interfacename> might contain outdated |
|
entities after the execution of the modifying query, we automatically |
|
clear it (see JavaDoc of |
|
<interfacename>EntityManager</interfacename>.<methodname>clear()</methodname> |
|
for details). This will effectively drop all non-flushed changes still |
|
pending in the <interfacename>EntityManager</interfacename>. If you |
|
don't wish the <interfacename>EntityManager</interfacename> to be |
|
cleared automatically you can set |
|
<interfacename>@Modifying</interfacename> annotation's |
|
<code>clearAutomatically</code> attribute to |
|
<literal>false</literal>;</para> |
|
</section> |
|
|
|
<section id="jpa.query-hints"> |
|
<title>Applying query hints</title> |
|
|
|
<para>To apply JPA <interfacename>QueryHint</interfacename>s to the |
|
queries declared in your repository interface you can use the |
|
<interfacename>QueryHints</interfacename> annotation. It takes an array |
|
of JPA <interfacename>QueryHint</interfacename> annotations plus a |
|
boolean flag to potentially disable the hints applied to the addtional |
|
count query triggered when applying pagination.</para> |
|
|
|
<example> |
|
<title>Using QueryHints with a repository method</title> |
|
|
|
<programlisting language="java">public interface UserRepository extends Repository<User, Long> { |
|
|
|
@QueryHints(value = { @QueryHint(name = "name", value = "value")}, |
|
forCounting = false) |
|
Page<User> findByLastname(String lastname, Pageable pageable); |
|
}</programlisting> |
|
|
|
<para>The just shown declaration would apply the configured |
|
<interfacename>QueryHint</interfacename> for that actually query but |
|
omit applying it to the count query triggered to calculate the total |
|
number of pages.</para> |
|
</example> |
|
</section> |
|
</section> |
|
|
|
<section id="specifications"> |
|
<title>Specifications</title> |
|
|
|
<para>JPA 2 introduces a criteria API that can be used to build queries |
|
programmatically. Writing a <code>criteria</code> you actually define the |
|
where-clause of a query for a domain class. Taking another step back these |
|
criteria can be regarded as predicate over the entity that is described by |
|
the JPA criteria API constraints.</para> |
|
|
|
<para>Spring Data JPA takes the concept of a specification from Eric |
|
Evans' book "Domain Driven Design", following the same semantics and |
|
providing an API to define such |
|
<interfacename>Specification</interfacename>s using the JPA criteria API. |
|
To support specifications you can extend your repository interface with |
|
the <interfacename>JpaSpecificationExecutor</interfacename> |
|
interface:</para> |
|
|
|
<programlisting language="java">public interface CustomerRepository extends CrudRepository<Customer, Long>, JpaSpecificationExecutor { |
|
… |
|
}</programlisting> |
|
|
|
<para>The additional interface carries methods that allow you to execute |
|
<interfacename>Specification</interfacename>s in a variety of ways.</para> |
|
|
|
<para>For example, the <code>readAll</code> method will return all |
|
entities that match the specification: </para> |
|
|
|
<programlisting language="java">List<T> readAll(Specification<T> spec);</programlisting> |
|
|
|
<para>The <interfacename>Specification</interfacename> interface is as |
|
follows:</para> |
|
|
|
<programlisting language="java">public interface Specification<T> { |
|
Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, |
|
CriteriaBuilder builder); |
|
}</programlisting> |
|
|
|
<para>Okay, so what is the typical use case? |
|
<interfacename>Specification</interfacename>s can easily be used to build |
|
an extensible set of predicates on top of an entity that then can be |
|
combined and used with <interfacename>JpaRepository</interfacename> |
|
without the need to declare a query (method) for every needed combination. |
|
Here's an example:</para> |
|
|
|
<example> |
|
<title>Specifications for a Customer</title> |
|
|
|
<programlisting language="java">public class CustomerSpecs { |
|
|
|
public static Specification<Customer> isLongTermCustomer() { |
|
return new Specification<Customer>() { |
|
Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, |
|
CriteriaBuilder builder) { |
|
|
|
LocalDate date = new LocalDate().minusYears(2); |
|
return builder.lessThan(root.get(Customer_.createdAt), date); |
|
} |
|
}; |
|
} |
|
|
|
public static Specification<Customer> hasSalesOfMoreThan(MontaryAmount value) { |
|
return new Specification<Customer>() { |
|
Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, |
|
CriteriaBuilder builder) { |
|
|
|
// build query here |
|
} |
|
}; |
|
} |
|
}</programlisting> |
|
</example> |
|
|
|
<para>Admittedly the amount of boilerplate leaves room for improvement |
|
(that will hopefully be reduced by Java 8 closures) but the client side |
|
becomes much nicer as you will see below. Besides that we have expressed |
|
some criteria on a business requirement abstraction level and created |
|
executable <interfacename>Specification</interfacename>s. So a client |
|
might use a <interfacename>Specification</interfacename> as |
|
follows:</para> |
|
|
|
<example> |
|
<title>Using a simple Specification</title> |
|
|
|
<programlisting language="java">List<Customer> customers = customerRepository.findAll(isLongTermCustomer());</programlisting> |
|
</example> |
|
|
|
<para>Okay, why not simply create a query for this kind of data access? |
|
You're right. Using a single <interfacename>Specification</interfacename> |
|
does not gain a lot of benefit over a plain query declaration. The power |
|
of <interfacename>Specification</interfacename>s really shines when you |
|
combine them to create new <interfacename>Specification</interfacename> |
|
objects. You can achieve this through the |
|
<classname>Specifications</classname> helper class we provide to build |
|
expressions like this:</para> |
|
|
|
<example> |
|
<title>Combined Specifications</title> |
|
|
|
<para><programlisting language="java">MonetaryAmount amount = new MonetaryAmount(200.0, Currencies.DOLLAR); |
|
List<Customer> customers = customerRepository.readAll( |
|
where(isLongTermCustomer()).or(hasSalesOfMoreThan(amount)));</programlisting>As |
|
you can see, <classname>Specifications</classname> offers some glue-code |
|
methods to chain and combine |
|
<interfacename>Specification</interfacename>s. Thus extending your data |
|
access layer is just a matter of creating new |
|
<interfacename>Specification</interfacename> implementations and |
|
combining them with ones already existing.</para> |
|
</example> |
|
</section> |
|
|
|
<section id="transactions"> |
|
<title>Transactionality</title> |
|
|
|
<para>CRUD methods on repository instances are transactional by default. |
|
For reading operations the transaction configuration <code>readOnly</code> |
|
flag is set to true, all others are configured with a plain |
|
<classname>@Transactional</classname> so that default transaction |
|
configuration applies. For details see JavaDoc of |
|
<classname>Repository</classname>. If you need to tweak transaction |
|
configuration for one of the methods declared in |
|
<interfacename>Repository</interfacename> simply redeclare the method in |
|
your repository interface as follows:</para> |
|
|
|
<example> |
|
<title>Custom transaction configuration for CRUD</title> |
|
|
|
<programlisting language="java">public interface UserRepository extends JpaRepository<User, Long> { |
|
|
|
@Override |
|
@Transactional(timeout = 10) |
|
public List<User> findAll(); |
|
|
|
// Further query method declarations |
|
}</programlisting> |
|
|
|
<para>This will cause the <methodname>findAll()</methodname> method to |
|
be executed with a timeout of 10 seconds and without the |
|
<code>readOnly</code> flag.</para> |
|
</example> |
|
|
|
<para>Another possibility to alter transactional behaviour is using a |
|
facade or service implementation that typically covers more than one |
|
repository. Its purpose is to define transactional boundaries for non-CRUD |
|
operations:</para> |
|
|
|
<example> |
|
<title>Using a facade to define transactions for multiple repository |
|
calls</title> |
|
|
|
<programlisting language="java">@Service |
|
class UserManagementImpl implements UserManagement { |
|
|
|
private final UserRepository userRepository; |
|
private final RoleRepository roleRepository; |
|
|
|
@Autowired |
|
public UserManagementImpl(UserRepository userRepository, |
|
RoleRepository roleRepository) { |
|
this.userRepository = userRepository; |
|
this.roleRepository = roleRepository; |
|
} |
|
|
|
@Transactional |
|
public void addRoleToAllUsers(String roleName) { |
|
|
|
Role role = roleRepository.findByName(roleName); |
|
|
|
for (User user : userRepository.readAll()) { |
|
user.addRole(role); |
|
userRepository.save(user); |
|
} |
|
}</programlisting> |
|
|
|
<para>This will cause call to |
|
<methodname>addRoleToAllUsers(…)</methodname> to run inside a |
|
transaction (participating in an existing one or create a new one if |
|
none already running). The transaction configuration at the repositories |
|
will be neglected then as the outer transaction configuration determines |
|
the actual one used. Note that you will have to activate |
|
<code><tx:annotation-driven /></code> explicitly to get annotation |
|
based configuration at facades working. The example above assumes you |
|
are using component scanning.</para> |
|
</example> |
|
|
|
<section id="transactional-query-methods"> |
|
<title>Transactional query methods</title> |
|
|
|
<para>To allow your query methods to be transactional simply use |
|
<interfacename>@Transactional</interfacename> at the repository |
|
interface you define.</para> |
|
|
|
<example> |
|
<title>Using @Transactional at query methods</title> |
|
|
|
<programlisting language="java">@Transactional(readOnly = true) |
|
public interface UserRepository extends JpaRepository<User, Long> { |
|
|
|
List<User> findByLastname(String lastname); |
|
|
|
@Modifying |
|
@Transactional |
|
@Query("delete from User u where u.active = false") |
|
void deleteInactiveUsers(); |
|
}</programlisting> |
|
|
|
<para>Typically you will want the <code>readOnly</code> flag set to |
|
true as most of the query methods will only read data. In contrast to |
|
that <methodname>deleteInactiveUsers()</methodname> makes use of the |
|
<interfacename>@Modifying</interfacename> annotation and overrides the |
|
transaction configuration. Thus the method will be executed with |
|
<code>readOnly</code> flag set to false.</para> |
|
</example> |
|
|
|
<note> |
|
<para>It's definitely reasonable to use transactions for read only |
|
queries and we can mark them as such by setting the |
|
<code>readOnly</code> flag. This will not, however, act as check that |
|
you do not trigger a manipulating query (although some databases |
|
reject <literal>INSERT</literal> and <literal>UPDATE</literal> |
|
statements inside a read only transaction). The <code>readOnly</code> |
|
flag instead is propagated as hint to the underlying JDBC driver for |
|
performance optimizations. Furthermore, Spring will perform some |
|
optimizations on the underlying JPA provider. E.g. when used with |
|
Hibernate the flush mode is set to <code>NEVER</code> when you |
|
configure a transaction as <code>readOnly</code> which causes |
|
Hibernate to skip dirty checks (a noticeable improvement on large |
|
object trees).</para> |
|
</note> |
|
</section> |
|
</section> |
|
|
|
<section id="locking"> |
|
<title>Locking</title> |
|
|
|
<para>To specify the lock mode to be used the |
|
<interfacename>@Lock</interfacename> annotation can be used on query |
|
methods:</para> |
|
|
|
<example> |
|
<title>Defining lock metadata on query methods</title> |
|
|
|
<programlisting language="java">interface UserRepository extends Repository<User, Long> { |
|
|
|
// Plain query method |
|
@Lock(LockModeType.READ) |
|
List<User> findByLastname(String lastname); |
|
}</programlisting> |
|
</example> |
|
|
|
<para>This method declaration will cause the query being triggered to be |
|
equipped with the <interfacename>LockModeType</interfacename> |
|
<code>READ</code>. You can also define locking for CRUD methods by |
|
redeclaring them in your repository interface and adding the |
|
<interfacename>@Lock</interfacename> annotation:</para> |
|
|
|
<example> |
|
<title>Defining lock metadata on CRUD methods</title> |
|
|
|
<programlisting language="java">interface UserRepository extends Repository<User, Long> { |
|
|
|
// Redeclaration of a CRUD method |
|
@Lock(LockModeType.READ); |
|
List<User> findAll(); |
|
}</programlisting> |
|
</example> |
|
</section> |
|
|
|
<section id="jpa.auditing"> |
|
<title>Auditing</title> |
|
|
|
<para>Most applications will require some form of auditability to track |
|
when an entity was created or modified and by whom. Spring Data JPA |
|
provides facilities to add this audit information to an entity |
|
transparently by AOP means. To take part in this functionality your domain |
|
classes must implement a more advanced interface:</para> |
|
|
|
<example> |
|
<title><interfacename>Auditable</interfacename> interface</title> |
|
|
|
<programlisting language="java">public interface Auditable<U, ID extends Serializable> |
|
extends Persistable<ID> { |
|
|
|
U getCreatedBy(); |
|
|
|
void setCreatedBy(U createdBy); |
|
|
|
DateTime getCreatedDate(); |
|
|
|
void setCreated(Date creationDate); |
|
|
|
U getLastModifiedBy(); |
|
|
|
void setLastModifiedBy(U lastModifiedBy); |
|
|
|
DateTime getLastModifiedDate(); |
|
|
|
void setLastModified(Date lastModifiedDate); |
|
}</programlisting> |
|
</example> |
|
|
|
<para>As you can see the modifying entity itself only has to be an entity. |
|
Mostly this will be some sort of User entity, so we chose U as parameter |
|
type.</para> |
|
|
|
<note> |
|
<para>To minimize boilerplate code Spring Data JPA offers |
|
<classname>AbstractPersistable</classname> and |
|
<classname>AbstractAuditable</classname> base classes that implement and |
|
pre-configure entities. Thus you can decide to only implement the |
|
interface or enjoy more sophisticated support by extending the base |
|
class.</para> |
|
</note> |
|
|
|
<simplesect> |
|
<title>General auditing configuration</title> |
|
|
|
<para>Spring Data JPA ships with an entity listener that can be used to |
|
trigger capturing auditing information. So first you have to register |
|
the <classname>AuditingEntityListener</classname> inside your |
|
<filename>orm.xml</filename> to be used for all entities in your |
|
persistence contexts:</para> |
|
|
|
<example> |
|
<title>Auditing configuration orm.xml</title> |
|
|
|
<programlisting language="xml"><persistence-unit-metadata> |
|
<persistence-unit-defaults> |
|
<entity-listeners> |
|
<entity-listener class="….data.jpa.domain.support.AuditingEntityListener" /> |
|
</entity-listeners> |
|
</persistence-unit-defaults> |
|
</persistence-unit-metadata></programlisting> |
|
</example> |
|
|
|
<para>Now activating auditing functionality is just a matter of adding |
|
the Spring Data JPA <literal>auditing</literal> namespace element to |
|
your configuration:</para> |
|
|
|
<example> |
|
<title>Activating auditing in the Spring configuration</title> |
|
|
|
<programlisting language="xml"><jpa:auditing auditor-aware-ref="yourAuditorAwareBean" /></programlisting> |
|
</example> |
|
|
|
<para>As you can see you have to provide a bean that implements the |
|
<interfacename>AuditorAware</interfacename> interface which looks as |
|
follows:</para> |
|
|
|
<example> |
|
<title><interfacename>AuditorAware</interfacename> interface</title> |
|
|
|
<programlisting language="java">public interface AuditorAware<T, ID extends Serializable> { |
|
|
|
T getCurrentAuditor(); |
|
}</programlisting> |
|
</example> |
|
|
|
<para>Usually you will have some kind of authentication component in |
|
your application that tracks the user currently working with the system. |
|
This component should be <interfacename>AuditorAware</interfacename> and |
|
thus allow seamless tracking of the auditor.</para> |
|
</simplesect> |
|
</section> |
|
|
|
<section id="jpa.misc"> |
|
<title>Miscellaneous</title> |
|
|
|
<section id="jpa.misc.merging-persistence-units"> |
|
<title>Merging persistence units</title> |
|
|
|
<para>Spring supports having multiple persistence units out of the box. |
|
Sometimes, however, you might want to modularize your application but |
|
still make sure that all these modules run inside a single persistence |
|
unit at runtime. To do so Spring Data JPA offers a |
|
<code>PersistenceUnitManager</code> implementation that automatically |
|
merges persistence units based on their name.</para> |
|
|
|
<example> |
|
<title>Using MergingPersistenceUnitmanager</title> |
|
|
|
<para><programlisting language="xml"><bean class="….LocalContainerEntityManagerFactoryBean"> |
|
<property name="persistenceUnitManager"> |
|
<bean class="….MergingPersistenceUnitManager" /> |
|
</property |
|
</bean></programlisting></para> |
|
</example> |
|
</section> |
|
|
|
<section id="jpa.misc.entity-scanning"> |
|
<title>Classpath scanning for @Entity classes and JPA mapping |
|
files</title> |
|
|
|
<para>A plain JPA setup requires all annotation mapped entity classes |
|
listed in <filename>orm.xml</filename>. Same applies to XML mapping |
|
files. Spring Data JPA provides a |
|
<classname>ClasspathScanningPersistenceUnitPostProcessor</classname> |
|
that gets a base package configured and optionally takes a mapping |
|
filename pattern. It will then scan the given package for classes |
|
annotated with <interfacename>@Entity</interfacename> or |
|
<interfacename>@MappedSuperclass</interfacename> and also loads the |
|
configuration files matching the filename pattern and hands them to the |
|
JPA configuration. The PostProcessor has to be configured like |
|
this</para> |
|
|
|
<example> |
|
<title>Using ClasspathScanningPersistenceUnitPostProcessor</title> |
|
|
|
<para><programlisting language="xml"><bean class="….LocalContainerEntityManagerFactoryBean"> |
|
<property name="persistenceUnitPostProcessors"> |
|
<list> |
|
<bean class="org.springframework.data.jpa.support.ClasspathScanningPersistenceUnitPostProcessor"> |
|
<constructor-arg value="com.acme.domain" /> |
|
<property name="mappingFileNamePattern" value="**/*Mapping.xml" /> |
|
</bean> |
|
</list> |
|
</property> |
|
</bean></programlisting></para> |
|
</example> |
|
|
|
<note> |
|
<para>As of Spring 3.1 a package to scan can be configured on the |
|
<classname>LocalContainerEntityManagerFactoryBean</classname> directly |
|
to enable classpath scanning for entity classes. See the <ulink |
|
url="http://static.springsource.org/spring/docs/3.1.x/javadoc-api/org/springframework/orm/jpa/LocalContainerEntityManagerFactoryBean.html#setPackagesToScan(java.lang.String...)">JavaDoc</ulink> |
|
for details.</para> |
|
</note> |
|
</section> |
|
|
|
<section id="jpd.misc.cdi-integration"> |
|
<title>CDI integration</title> |
|
|
|
<para>Instances of the repository interfaces are usually created by a |
|
container, which Spring is the most natural choice when working with |
|
Spring Data. There's sophisticated support to easily set up Spring to |
|
create bean instances documented in <xref |
|
linkend="repositories.create-instances"/>. As of version 1.1.0 Spring |
|
Data JPA ships with a custom CDI extension that allows using the |
|
repository abstraction in CDI environments. The extension is part of the |
|
JAR so all you need to do to activate it is dropping the Spring Data JPA |
|
JAR into your classpath.</para> |
|
|
|
<para>You can now set up the infrastructure by implementing a CDI |
|
<interfacename>Producer</interfacename> for the |
|
<classname>EntityManagerFactory</classname>:</para> |
|
|
|
<programlisting language="java">class EntityManagerFactoryProducer { |
|
|
|
@Produces |
|
@ApplicationScoped |
|
public EntityManagerFactory createEntityManagerFactory() { |
|
return Persistence.createEntityManagerFactory("my-presistence-unit"); |
|
} |
|
|
|
public void close(@Disposes EntityManagerFactory entityManagerFactory) { |
|
entityManagerFactory.close(); |
|
} |
|
}</programlisting> |
|
|
|
<para>The Spring Data JPA CDI extension will pick up all |
|
<interfacename>EntityManager</interfacename>s availables as CDI beans |
|
and create a proxy for a Spring Data repository whenever an bean of a |
|
repository type is requested by the container. Thus obtaining an |
|
instance of a Spring Data repository is a matter of declaring an |
|
<interfacename>@Inject</interfacename>ed property:</para> |
|
|
|
<programlisting language="java">class RepositoryClient { |
|
|
|
@Inject |
|
PersonRepository repository; |
|
|
|
public void businessMethod() { |
|
|
|
List<Person> people = repository.findAll(); |
|
} |
|
}</programlisting> |
|
</section> |
|
</section> |
|
</chapter>
|
|
|