This guide walks you through the process of building an application that uses Spring Data JPA to store and retrieve data in a relational database.
You'll build an application that stores Person
POJOs in a memory-based database.
- About 15 minutes
- A favorite text editor or IDE
- JDK 6 or later
- Gradle 1.8+ or Maven 3.0+
- You can also import the code from this guide as well as view the web page directly into Spring Tool Suite (STS) and work your way through it from there.
Like all Spring's Getting Started guides, you can start from scratch and complete each step, or you can bypass basic setup steps that are already familiar to you. Either way, you end up with working code.
To start from scratch, move on to Set up the project.
To skip the basics, do the following:
- [Download][zip] and unzip the source repository for this guide, or clone it using [Git][u-git]:
git clone https://github.com/spring-guides/gs-accessing-data-jpa.git
- cd into
gs-accessing-data-jpa/initial
. - Jump ahead to Define a simple entity.
When you're finished, you can check your results against the code in gs-accessing-data-jpa/complete
.
[zip]: https://github.com/spring-guides/gs-accessing-data-jpa/archive/master.zip
[u-git]: /understanding/Git
First you set up a basic build script. You can use any build system you like when building apps with Spring, but the code you need to work with Gradle and Maven is included here. If you're not familiar with either, refer to Building Java Projects with Gradle or Building Java Projects with Maven.
In a project directory of your choosing, create the following subdirectory structure; for example, with mkdir -p src/main/java/hello
on *nix systems:
└── src
└── main
└── java
└── hello
Below is the initial Gradle build file. But you can also use Maven. The pom.xml file is included right here. If you are using Spring Tool Suite (STS), you can import the guide directly.
build.gradle
buildscript {
repositories {
maven { url "http://repo.spring.io/libs-snapshot" }
mavenLocal()
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
jar {
baseName = 'gs-accessing-data-jpa'
version = '0.1.0'
}
repositories {
mavenCentral()
maven { url "http://repo.spring.io/libs-snapshot" }
maven { url "https://repository.jboss.org/nexus/content/repositories/releases" }
}
dependencies {
compile("org.springframework.boot:spring-boot-starter-web:0.5.0.M4")
compile("org.springframework:spring-orm:4.0.0.M3")
compile("org.springframework.data:spring-data-jpa:1.3.2.RELEASE")
compile("org.hibernate:hibernate-entitymanager:4.2.1.Final")
compile("com.h2database:h2:1.3.172")
testCompile("junit:junit:4.11")
}
task wrapper(type: Wrapper) {
gradleVersion = '1.8'
}
In this example, you store Customer objects, annotated as a JPA entity.
src/main/java/hello/Customer.java
package hello;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class Customer {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private long id;
private String firstName;
private String lastName;
private Customer() {}
public Customer(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
@Override
public String toString() {
return String.format(
"Customer[id=%d, firstName='%s', lastName='%s']",
id, firstName, lastName);
}
}
Here you have a Customer
class with three attributes, the id
, the firstName
, and the lastName
. You also have two constructors. The default constructor only exists for the sake of JPA. You won't use it directly, so it is designated as private
. The other constructor is the one you'll use to create instances of Customer
to be saved to the database.
Note: In this guide, the typical getters and setters have been left out for brevity.
The Customer
class is annotated with @Entity
, indicating that it is a JPA entity. For lack of a @Table
annotation, it is assumed that this entity will be mapped to a table named Customer
.
The Customer
's id
property is annotated with @Id
so that JPA will recognize it as the object's ID. The id
property is also annotated with @GeneratedValue
to indicate that the ID should be generated automatically.
The other two properties, firstName
and lastName
are left unannotated. It is assumed that they'll be mapped to columns that share the same name as the properties themselves.
The convenient toString()
method will print out the customer's properties.
Spring Data JPA focuses on using JPA to store data in a relational database. Its most compelling feature is the ability to create repository implementations automatically, at runtime, from a repository interface.
To see how this works, create a repository interface that works with Customer
entities:
src/main/java/hello/CustomerRepository.java
package hello;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
public interface CustomerRepository extends JpaRepository<Customer, Long> {
List<Customer> findByLastName(String lastName);
}
CustomerRepository
extends the JpaRepository
interface. The type of entity and ID that it works with,Customer
and Long
, are specified in the generic parameters on JpaRepository
. By extending JpaRepository
, CustomerRepository
inherits several methods for working with Customer
persistence, including methods for saving, deleting, and finding Customer
entities.
Spring Data JPA also allows you to define other query methods by simply declaring their method signature. In the case of CustomerRepository
, this is shown with a findByLastName()
method.
In a typical Java application, you'd expect to write a class that implements CustomerRepository
. But that's what makes Spring Data JPA so powerful: You don't have to write an implementation of the repository interface. Spring Data JPA creates an implementation on the fly when you run the application.
Let's wire this up and see what it looks like!
Here you create an Application class with all the components.
src/main/java/hello/Application.java
package hello;
import static org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType.H2;
import java.util.List;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
@Configuration
@EnableJpaRepositories
public class Application {
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder().setType(H2).build();
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource, JpaVendorAdapter jpaVendorAdapter) {
LocalContainerEntityManagerFactoryBean lef = new LocalContainerEntityManagerFactoryBean();
lef.setDataSource(dataSource);
lef.setJpaVendorAdapter(jpaVendorAdapter);
lef.setPackagesToScan("hello");
return lef;
}
@Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
hibernateJpaVendorAdapter.setShowSql(false);
hibernateJpaVendorAdapter.setGenerateDdl(true);
hibernateJpaVendorAdapter.setDatabase(Database.H2);
return hibernateJpaVendorAdapter;
}
@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
public static void main(String[] args) {
AbstractApplicationContext context = new AnnotationConfigApplicationContext(Application.class);
CustomerRepository repository = context.getBean(CustomerRepository.class);
// save a couple of customers
repository.save(new Customer("Jack", "Bauer"));
repository.save(new Customer("Chloe", "O'Brian"));
repository.save(new Customer("Kim", "Bauer"));
repository.save(new Customer("David", "Palmer"));
repository.save(new Customer("Michelle", "Dessler"));
// fetch all customers
List<Customer> customers = repository.findAll();
System.out.println("Customers found with findAll():");
System.out.println("-------------------------------");
for (Customer customer : customers) {
System.out.println(customer);
}
System.out.println();
// fetch an individual customer by ID
Customer customer = repository.findOne(1L);
System.out.println("Customer found with findOne(1L):");
System.out.println("--------------------------------");
System.out.println(customer);
System.out.println();
// fetch customers by last name
List<Customer> bauers = repository.findByLastName("Bauer");
System.out.println("Customer found with findByLastName('Bauer'):");
System.out.println("--------------------------------------------");
for (Customer bauer : bauers) {
System.out.println(bauer);
}
context.close();
}
}
In the configuration, you need to add the @EnableJpaRepositories
annotation. This annotation tells Spring Data JPA that it should seek out any interface that extends org.springframework.data.repository.Repository
and automatically generate an implementation. By extending JpaRepository
, your CustomerRepository
interface transitively extends Repository
. Therefore, Spring Data JPA will find it and create an implementation for you.
Most of the content in Application
sets up several beans to support Spring Data JPA and the sample:
- The
dataSource()
method defines aDataSource
bean, as an embedded database to which the objects are persisted. - The
entityManagerFactory()
method defines aLocalContainerEntityManagerFactoryBean
that is ultimately used to createLocalContainerEntityManagerFactory
a bean that implements theEntityManagerFactory
interface. It is the bean through which JPA operations will be performed. Note that this factory bean'spackagesToScan
property is set to look for entities in the package named "hello". This makes it possible to work with JPA without defining a "persistence.xml" file. - The
jpaVendorAdapter()
method defines a Hibernate-based JPA vendor adapter bean for use by theEntityManagerFactory
bean. - The
transactionManager()
method defines aJpaTransactionManager
bean for transactional persistence.
Finally, Application
includes a main()
method that puts the CustomerRepository
through a few tests. First, it fetches the CustomerRepository
from the Spring application context. Then it saves a handful of Customer
objects, demonstrating the save()
method and setting up some data to work with. Next, it calls findAll()
to fetch all Customer
objects from the database. Then it calls findOne()
to fetch a single Customer
by its ID. Finally, it calls findByLastName()
to find all customers whose last name is "Bauer".
Now that your Application
class is ready, you simply instruct the build system to create a single, executable jar containing everything. This makes it easy to ship, version, and deploy the service as an application throughout the development lifecycle, across different environments, and so forth.
Below are the Gradle steps, but if you are using Maven, you can find the updated pom.xml right here and build it by typing mvn clean package
.
Update your Gradle build.gradle
file's buildscript
section, so that it looks like this:
buildscript {
repositories {
maven { url "http://repo.spring.io/libs-snapshot" }
mavenLocal()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:0.5.0.M4")
}
}
Further down inside build.gradle
, add the following to the list of applied plugins:
apply plugin: 'spring-boot'
You can see the final version of build.gradle
[right here]((https://github.com/spring-guides/gs-accessing-data-jpa/blob/master/complete/build.gradle).
The Spring Boot gradle plugin collects all the jars on the classpath and builds a single "über-jar", which makes it more convenient to execute and transport your service.
It also searches for the public static void main()
method to flag as a runnable class.
Now run the following command to produce a single executable JAR file containing all necessary dependency classes and resources:
$ ./gradlew build
If you are using Gradle, you can run the JAR by typing:
$ java -jar build/libs/gs-accessing-data-jpa-0.1.0.jar
If you are using Maven, you can run the JAR by typing:
$ java -jar target/gs-accessing-data-jpa-0.1.0.jar
Note: The procedure above will create a runnable JAR. You can also opt to build a classic WAR file instead.
If you are using Gradle, you can run your service at the command line this way:
$ ./gradlew clean build && java -jar build/libs/gs-accessing-data-jpa-0.1.0.jar
Note: If you are using Maven, you can run your service by typing
mvn clean package && java -jar target/gs-accessing-data-jpa-0.1.0.jar
.
You should see something like this:
Customers found with findAll():
-------------------------------
Customer[id=1, firstName='Jack', lastName='Bauer']
Customer[id=2, firstName='Chloe', lastName='O'Brian']
Customer[id=3, firstName='Kim', lastName='Bauer']
Customer[id=4, firstName='David', lastName='Palmer']
Customer[id=5, firstName='Michelle', lastName='Dessler']
Customer found with findOne(1L):
--------------------------------
Customer[id=1, firstName='Jack', lastName='Bauer']
Customer found with findByLastName('Bauer'):
--------------------------------------------
Customer[id=1, firstName='Jack', lastName='Bauer']
Customer[id=3, firstName='Kim', lastName='Bauer']
Congratulations! You've written a simple application that uses Spring Data JPA to save objects to a database and to fetch them -- all without writing a concrete repository implementation.