Spring Data JPA: Difference between revisions

From NovaOrdis Knowledge Base
Jump to navigation Jump to search
 
(34 intermediate revisions by the same user not shown)
Line 4: Line 4:
* Spring Data Core JavaDoc https://docs.spring.io/spring-data/commons/docs/current/api/
* Spring Data Core JavaDoc https://docs.spring.io/spring-data/commons/docs/current/api/
* Spring Data JPA JavaDoc https://docs.spring.io/spring-data/jpa/docs/current/api/
* Spring Data JPA JavaDoc https://docs.spring.io/spring-data/jpa/docs/current/api/
* Spring Data JPA Reference Documentation https://docs.spring.io/spring-data/jpa/docs/2.1.3.RELEASE/reference/html/


=Internal=
=Internal=
Line 9: Line 10:
* [[Spring Data#Projects|Spring Data]]
* [[Spring Data#Projects|Spring Data]]
* [[JPA]]
* [[JPA]]
* [[QueryDSL]]


=Overview=
=Overview=


Spring Data JPA is a [[Spring Data]] project that assists with implementing JPA-based repositories, which persist data in relational databases. The approach involves writing the repository interface, including custom finder methods, and Spring will provide the implementation automatically.
Spring Data JPA is a [[Spring Data]] project that assists with implementing JPA-based repositories, which persist data in relational databases. The approach requires only writing the repository interface, by extending [https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/repository/CrudRepository.html org.springframework.data.repository.CrudRepository] or [https://docs.spring.io/spring-data/jpa/docs/current/api/org/springframework/data/jpa/repository/JpaRepository.html org.springframework.data.jpa.repository.JpaRepository] and optionally including custom finder methods. Once this is done, Spring will provide the implementation automatically.


=Spring Persistence Concepts=
=Spring Persistence Concepts=
Line 77: Line 79:
==<span id='Spring_Data_JPA_Repository'></span>Declare the JPA Repositories==
==<span id='Spring_Data_JPA_Repository'></span>Declare the JPA Repositories==


Entities are built, managed and exposed to the application by JPA repositories. JPA repositories are explicitly declared by the application. A Spring Data JPA repository is the embodiment of the Spring [[Spring_Persistence_Concepts#Repository|repository concept]] concept. Similarly to a [[JdbcTemplate#JdbcTemplate-Based_Repository|JdbcTemplate-based repository]], a JPA repository conceals low-level data access details from the application while exposing Entities to the application. With [[JdbcTemplate#JdbcTemplate-Based_Repository|JdbcTemplate-based repositories]] the developers need to explicitly declare and implement the methods that should be exposed by the repository, such as <tt>findOne(...)</tt>, <tt>findAll()</tt> and <tt>save(...)</tt>. With Spring Data JPA repositories, it is sufficient to extend the [[#CrudRepository|CrudRepository]] interface, which already comes with essential methods. Note that similarly to the [[JdbcTemplate#JdbcTemplate-Based_Repository|JdbcTemplate-based repository]], it is a good idea to define the repository as an [[Spring Persistence Concepts#Interface_in_Application_Domain|interface in the application domain]]:
Entities are built, managed and exposed to the application by JPA repositories. JPA repositories are explicitly declared by the application. A Spring Data JPA repository is the embodiment of the Spring [[Spring_Persistence_Concepts#Repository|repository concept]] concept. Similarly to a [[JdbcTemplate#JdbcTemplate-Based_Repository|JdbcTemplate-based repository]], a JPA repository conceals low-level data access details from the application while exposing Entities to the application. With [[JdbcTemplate#JdbcTemplate-Based_Repository|JdbcTemplate-based repositories]] the developers need to explicitly declare and implement the methods that should be exposed by the repository, such as <tt>findOne(...)</tt>, <tt>findAll()</tt> and <tt>save(...)</tt>. With Spring Data JPA repositories, it is sufficient to extend the [[#CrudRepository|CrudRepository]] or [[#JpaRepository|JpaRepository]] interface, which already comes with essential methods. Note that similarly to the [[JdbcTemplate#JdbcTemplate-Based_Repository|JdbcTemplate-based repository]], it is a good idea to define the repository as an [[Spring Persistence Concepts#Interface_in_Application_Domain|interface in the application domain]]:


<syntaxhighlight lang='java'>
<syntaxhighlight lang='java'>
public interface IngredientRepository extends CrudRepository<Ingredient, String> {
public interface IngredientRepository extends CrudRepository<Ingredient, String> {
}
</syntaxhighlight>
or
<syntaxhighlight lang='java'>
public interface IngredientRepository extends JpaRepository<Ingredient, String> {
}
}
</syntaxhighlight>
</syntaxhighlight>
Line 112: Line 121:
}
}
</syntaxhighlight>
</syntaxhighlight>
===JpaRepository===
{{External|[https://docs.spring.io/spring-data/jpa/docs/current/api/org/springframework/data/jpa/repository/JpaRepository.html org.springframework.data.jpa.repository.JpaRepository JavaDoc]}}
{{External|[https://github.com/ovidiuf/playground/tree/master/spring/jpa/03-spring-boot-jpa-repository Playground JpaRepository Example]}}
<syntaxhighlight lang='java'>
package org.springframework.data.jpa.repository;
@NoRepositoryBean
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
List<T> findAll();
List<T> findAll(Sort sort);
List<T> findAllById(Iterable<ID> ids);
<S extends T> List<S> saveAll(Iterable<S> entities);
void flush();
<S extends T> S saveAndFlush(S entity);
void deleteInBatch(Iterable<T> entities);
void deleteAllInBatch();
T getOne(ID id);
@Override
<S extends T> List<S> findAll(Example<S> example);
@Override
<S extends T> List<S> findAll(Example<S> example, Sort sort);
}
</syntaxhighlight>
===Difference between CrudRepository and JpaRepository===
<font color=darkgray>TODO</font>


===Customizing JPA Repositories===
===Customizing JPA Repositories===


Additional methods can be added to the [[#Declare_the_JPA_Repositories|Spring Data JPA repository interface]]. Spring Data examines any method in the repository interface, parses the method name and attempts to understand the method's purpose in the context of the persisted entity. In essence, Spring Data understands a meta-domain-specific language (DSL) where persistence details are expressed in repository method signatures. In order to be interpretable, repository methods must be composed of a verb, an optional subject, the word "By", and a predicate. If the subject is not specified, it is implied to be the entity associated with the repository.
{{External|[https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repository-query-keywords Repository Query Keywords - Reference Documentation]}}
{{External|[https://github.com/ovidiuf/playground/tree/master/spring/jpa/03-spring-boot-jpa-repository Playground JpaRepository Custom Query Method Example]}}


To fetch one or more entities, use "get", "find" or "read" as verb. To count, use "count".  
Additional methods can be added to the [[#Declare_the_JPA_Repositories|Spring Data JPA repository interface]]. Spring Data examines any method in the repository interface, parses the method name and attempts to understand the method's purpose in the context of the persisted entity. In essence, Spring Data understands a meta-domain-specific language (DSL) where persistence details are expressed in repository method signatures.  


The subject, if it specified and it is not the type the repository is parameterized with, will be ignored.
In order to be interpretable, repository methods must be composed of a verb, an optional subject, the word "By", and a predicate. If the subject is not specified, it is implied to be the entity associated with the repository. To fetch one or more entities, use "get", "find" or "read" as verb. To count, use "count".  The subject, if it specified and it is not the type the repository is parameterized with, will be ignored.


The predicate signature may include any of these operators:
The predicate signature may include any of these operators:
Line 127: Line 170:
* IsLessThanEqual, LessThanEqual
* IsLessThanEqual, LessThanEqual
* IsBetween, Between
* IsBetween, Between
<syntaxhighlight lang='java'>
List<Item> getItemByCreatedBetween(Date from, Date to);
</syntaxhighlight>
* IsNull, Null
* IsNull, Null
* IsNotNull, NotNull
* IsNotNull, NotNull
Line 146: Line 192:
"OrderBy" can be placed at the end of the method name to sort the results by a specified column.
"OrderBy" can be placed at the end of the method name to sort the results by a specified column.


Complex queries can be added with the @Qyery annotation:
Also see: {{Internal|QueryDSL|QueryDSL}}
 
Complex queries can be added with the @Qyery annotation, but this approach has the disadvantage that it hardcodes support for a specific database: the query runs the risk of working with the database it was designed with, but not with others:


<syntaxhighlight lang='java'>
<syntaxhighlight lang='java'>
Line 154: Line 202:


Paging example: [[SIA]] page 122.
Paging example: [[SIA]] page 122.
====Queries that Do Not Match Anything====
Queries that do not match anything conventionally return empty lists or nulls. They do not throw exceptions.


=Database Access Configuration=
=Database Access Configuration=
<font color=darkgray>TODO
* https://docs.spring.io/spring-boot/docs/current/reference/html/howto-data-access.html#howto-configure-jpa-properties
* https://docs.spring.io/spring-boot/docs/current/reference/html/howto-database-initialization.html
</font>


Concrete Examples:
Concrete Examples:
Line 164: Line 221:
  spring.jpa.generate-ddl=true
  spring.jpa.generate-ddl=true
  spring.jpa.show-sql=true
  spring.jpa.show-sql=true
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQL9Dialect
spring.jpa.database=
  spring.jpa.hibernate.ddl-auto=none
  spring.jpa.hibernate.ddl-auto=none
spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults=false
spring.jpa.generate-ddl=true|false
Is a vendor-independent configuration that switches the DDL generation on and off.
spring.jpa.hibernate.ddl-auto=none|validate|update|create|create-drop
Is a Hibernate feature (the Spring property is passed to Hibernate as "hibernate.hbm2ddl.auto") that controls the DDL creation in a more fine-grained way. Spring Boot chooses a default value based on whether it thinks your database is embedded. It defaults to create-drop if no schema manager has been detected or none in all other cases.
Embedded databases: 'hsqldb', 'h2', and 'derby'.
Verified: even in the presence of a schema manager like Flyway, setting spring.jpa.hibernate.ddl-auto to "create-drop" is honored.
spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults=false
makes the following to away:
<syntaxhighlight lang='java'>
2018-12-04 14:20:33.266  INFO 64943 --- [          main] o.h.e.j.e.i.LobCreatorBuilderImpl        : HHH000424: Disabling contextual LOB creation as createClob() method threw error : java.lang.reflect.InvocationTargetException
...
Caused by: java.sql.SQLFeatureNotSupportedException: Method org.postgresql.jdbc.PgConnection.createClob() is not yet implemented.
at org.postgresql.Driver.notImplemented(Driver.java:688) ~[postgresql-42.2.5.jar!/:42.2.5]
at org.postgresql.jdbc.PgConnection.createClob(PgConnection.java:1269) ~[postgresql-42.2.5.jar!/:42.2.5]
... 52 common frames omitted
</syntaxhighlight>


=Database Initialization=
=Database Initialization=

Latest revision as of 19:36, 14 January 2019

External

Internal

Overview

Spring Data JPA is a Spring Data project that assists with implementing JPA-based repositories, which persist data in relational databases. The approach requires only writing the repository interface, by extending org.springframework.data.repository.CrudRepository or org.springframework.data.jpa.repository.JpaRepository and optionally including custom finder methods. Once this is done, Spring will provide the implementation automatically.

Spring Persistence Concepts

Spring Persistence Concepts

Spring Boot Support

Playground Examples

Working examples for H2, and both H2 and PostgreSQL is available here:

Playground Spring Data H2 JPA
Playground Spring Data JPA H2 and PostgreSQL

Spring Boot Starter Dependencies

To add support for Spring Data JPA to a Spring Boot project, add the following starter dependency:

dependencies {
    implementation('org.springframework.boot:spring-boot-starter-data-jpa')
}

This starter dependency also transitively includes Hibernate as the JPA implementation.

Add Specific Database Support

Spring Data JPA needs a database to work with. The database drivers should be bundled with the application and should be available on its classpath. Spring Boot does that if its build system is configured with the right dependencies, as shown below:

Configure Database Access

Adding Persistence to an Application with Spring Data JPA

Annotate Domain Objects with @Entity

Annotate your domain objects that require persistence with @Entity. JPA requires each entity class to expose a no-argument constructor, which can be coded manually, or it can be generated with Lombok's @NoArgConstructor:

@NoArgConstructor(access=AccessLevel.PRIVATE, force=true)

From a project layout perspective, we conventionally maintain the classes in a "model" package.

Define Entity's ID

Designate the entity's primary key field with @Id and optionally with @GeneratedValue.

Entity Member Variable Conventions

  • Camel-case member variables map by default on '_' separated column names. "createdAt" maps to a "created_at" column.

Define Relationships between Entities

@ManyToMany, etc.

Declare the JPA Repositories

Entities are built, managed and exposed to the application by JPA repositories. JPA repositories are explicitly declared by the application. A Spring Data JPA repository is the embodiment of the Spring repository concept concept. Similarly to a JdbcTemplate-based repository, a JPA repository conceals low-level data access details from the application while exposing Entities to the application. With JdbcTemplate-based repositories the developers need to explicitly declare and implement the methods that should be exposed by the repository, such as findOne(...), findAll() and save(...). With Spring Data JPA repositories, it is sufficient to extend the CrudRepository or JpaRepository interface, which already comes with essential methods. Note that similarly to the JdbcTemplate-based repository, it is a good idea to define the repository as an interface in the application domain:

public interface IngredientRepository extends CrudRepository<Ingredient, String> {
}

or

public interface IngredientRepository extends JpaRepository<Ingredient, String> {
}

Unlike JdbcTemplate-based repositories, the developer does NOT need to provide an implementation of the repository interface. Spring Data JPA automatically generates an implementation. All that is needed to be done is to inject the repositories into controllers and other components that need them.

If the default methods are not sufficient, JPA repositories can be customized.

From a project layout perspective, we conventionally maintain the repository interfaces in a "repository" package.

CrudRepository

org.springframework.data.repository.CrudRepository JavaDoc

CrudRepository declares about a dozen methods for CRUD (create, read, update, delete) operations. It is type-parameterized, with the first parameter being the entity type of the repository, and the second parameter being the type of the entity ID property.

public interface CrudRepository<T, ID> extends Repository<T, ID> {

  <S extends T> S save(S entity);
  <S extends T> Iterable<S> saveAll(Iterable<S> entities);
  Optional<T> findById(ID id);
  boolean existsById(ID id);
  Iterable<T> findAll();
  Iterable<T> findAllById(Iterable<ID> ids);
  long count();
  void deleteById(ID id);
  void delete(T entity);
  void deleteAll(Iterable<? extends T> entities);
  void deleteAll();
}

JpaRepository

org.springframework.data.jpa.repository.JpaRepository JavaDoc
Playground JpaRepository Example
package org.springframework.data.jpa.repository;

@NoRepositoryBean
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {

 List<T> findAll();
 List<T> findAll(Sort sort);
 List<T> findAllById(Iterable<ID> ids);
 <S extends T> List<S> saveAll(Iterable<S> entities);
 void flush();
 <S extends T> S saveAndFlush(S entity);
 void deleteInBatch(Iterable<T> entities);
 void deleteAllInBatch();
 T getOne(ID id);

 @Override
 <S extends T> List<S> findAll(Example<S> example);

 @Override
 <S extends T> List<S> findAll(Example<S> example, Sort sort);
}

Difference between CrudRepository and JpaRepository

TODO

Customizing JPA Repositories

Repository Query Keywords - Reference Documentation
Playground JpaRepository Custom Query Method Example

Additional methods can be added to the Spring Data JPA repository interface. Spring Data examines any method in the repository interface, parses the method name and attempts to understand the method's purpose in the context of the persisted entity. In essence, Spring Data understands a meta-domain-specific language (DSL) where persistence details are expressed in repository method signatures.

In order to be interpretable, repository methods must be composed of a verb, an optional subject, the word "By", and a predicate. If the subject is not specified, it is implied to be the entity associated with the repository. To fetch one or more entities, use "get", "find" or "read" as verb. To count, use "count". The subject, if it specified and it is not the type the repository is parameterized with, will be ignored.

The predicate signature may include any of these operators:

  • IsAfter, After, IsGreaterThan, GreaterThan
  • IsGreaterThanEqual, GreaterThanEqual
  • IsBefore, Before, IsLessThan, LessThan
  • IsLessThanEqual, LessThanEqual
  • IsBetween, Between
List<Item> getItemByCreatedBetween(Date from, Date to);
  • IsNull, Null
  • IsNotNull, NotNull
  • IsIn, In
  • IsNotIn, NotIn
  • IsStartingWith, StartingWith, StartsWith
  • IsEndingWith, EndingWith, EndsWith
  • IsContaining, Containing, Contains
  • IsLike, Like
  • IsNotLike, NotLike
  • IsTrue, True
  • IsFalse, False
  • Is, Equals
  • IsNot, Not
  • IgnoringCase, IgnoresCase

As alternatives for "IgnoringCase" and "IgnoresCase", "AllIgnoringCase" or "AllIgnoresCase" can be placed in the method name to ignore case for all String comparisons.

"OrderBy" can be placed at the end of the method name to sort the results by a specified column.

Also see:

QueryDSL

Complex queries can be added with the @Qyery annotation, but this approach has the disadvantage that it hardcodes support for a specific database: the query runs the risk of working with the database it was designed with, but not with others:

@Query("Something s where o.name='blah'")
List<Something> getSomeSomethings();

Paging example: SIA page 122.

Queries that Do Not Match Anything

Queries that do not match anything conventionally return empty lists or nulls. They do not throw exceptions.

Database Access Configuration

TODO

Concrete Examples:

Also see:

Spring Boot DataSource Autoconfiguration
spring.jpa.generate-ddl=true
spring.jpa.show-sql=true
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQL9Dialect
spring.jpa.database=
spring.jpa.hibernate.ddl-auto=none
spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults=false


spring.jpa.generate-ddl=true|false

Is a vendor-independent configuration that switches the DDL generation on and off.

spring.jpa.hibernate.ddl-auto=none|validate|update|create|create-drop

Is a Hibernate feature (the Spring property is passed to Hibernate as "hibernate.hbm2ddl.auto") that controls the DDL creation in a more fine-grained way. Spring Boot chooses a default value based on whether it thinks your database is embedded. It defaults to create-drop if no schema manager has been detected or none in all other cases.

Embedded databases: 'hsqldb', 'h2', and 'derby'.

Verified: even in the presence of a schema manager like Flyway, setting spring.jpa.hibernate.ddl-auto to "create-drop" is honored.

spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults=false

makes the following to away:

2018-12-04 14:20:33.266  INFO 64943 --- [           main] o.h.e.j.e.i.LobCreatorBuilderImpl        : HHH000424: Disabling contextual LOB creation as createClob() method threw error : java.lang.reflect.InvocationTargetException
...
Caused by: java.sql.SQLFeatureNotSupportedException: Method org.postgresql.jdbc.PgConnection.createClob() is not yet implemented.
	at org.postgresql.Driver.notImplemented(Driver.java:688) ~[postgresql-42.2.5.jar!/:42.2.5]
	at org.postgresql.jdbc.PgConnection.createClob(PgConnection.java:1269) ~[postgresql-42.2.5.jar!/:42.2.5]
	... 52 common frames omitted

Database Initialization

Spring Persistence Concepts - Database Initialization

Note that for initialization to work, the database access has to be configured as described above in Database Access Configuration.

Generic JPA Database Initialization

JPA can generate DDL in the database at startup. The feature is controlled by the configuration property spring.jpa.generate-ddl (true or false), which switches the feature on and off and it is vendor independent. The configuration property is conventionally placed in application.properties or application.yml. This was tested and proven to work with H2.

Database Initialization Using Hibernate

https://docs.spring.io/spring-boot/docs/current/reference/html/howto-database-initialization.html#howto-initialize-a-database-using-hibernate

Controlled by spring.jpa.hibernate.ddl-auto. Can be "none", "validate", "update", "create" and "create-drop".

If Hibernate creates the schema from scratch and finds a file named import.sql in the root of the classpath, it will execute it against the database.

The configuration property is conventionally placed in application.properties or application.yml.

Configuration

spring.jpa.show-sql=true

TODO

  • How to tell that a JPA repository should use a specific database. How is that configured?
  • @EnableJpaRepositories(basePackages = "com.example.dev.myproject.driver.repo")