Spring Data
Spring Data JPA
Spring Data JPA provides a more cleaner approach of using JPA for DAO layer. It removes boilerplate code and developers can concentrate on actual logic.
Our DataModel
First create a maven project and add below dependencies:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.mumz.test.springdata.jpa</groupId> <artifactId>SpringDataJPA</artifactId> <version>0.0.1-SNAPSHOT</version> <properties> <spring.version>3.2.4.RELEASE</spring.version> <spring.data.version>1.3.4.RELEASE</spring.data.version> <hibernate.version>4.2.5.FINAL</hibernate.version> <jpa.version>1.0.1.FINAL</jpa.version> <querydsl.version>3.2.3</querydsl.version> <junit.version>4.8.2</junit.version> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-jpa</artifactId> <version>${spring.data.version}</version> </dependency> <dependency> <groupId>org.hibernate.javax.persistence</groupId> <artifactId>hibernate-jpa-2.0-api</artifactId> <version>${jpa.version}</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> <version>${hibernate.version}</version> </dependency> <dependency> <groupId>com.mysema.querydsl</groupId> <artifactId>querydsl-jpa</artifactId> <version>${querydsl.version}</version> </dependency> <dependency> <groupId>javax.inject</groupId> <artifactId>javax.inject</artifactId> <version>1</version> </dependency> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.1.3</version> </dependency> <dependency> <groupId>commons-pool</groupId> <artifactId>commons-pool</artifactId> <version>1.6</version> </dependency> </dependencies> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>2.3.2</version> <executions> <execution> <id>default-testCompile</id> <phase>test-compile</phase> <goals> <goal>testCompile</goal> </goals> <configuration> <source>1.6</source> <target>1.6</target> </configuration> </execution> <execution> <id>default-compile</id> <phase>compile</phase> <goals> <goal>compile</goal> </goals> <configuration> <source>1.6</source> <target>1.6</target> </configuration> </execution> </executions> <configuration> <source>1.6</source> <target>1.6</target> </configuration> </plugin> </plugins> </build> </project>
Given our datamodel and maven dependcies ready, below is the Book
Entity class. Book is a simple POJO with JPA annotations.
package com.mumz.test.springdata.jpa.entity; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; /** * The Class Book. * * @author prabhat.jha */ @Entity(name = "BOOK") public class Book implements Serializable{ /** * */ private static final long serialVersionUID = -5163349208616552005L; /** The id. */ private Long id = null; /** The name. */ private String name = null; /** The author. */ private String author = null; /** * Gets the id. * * @return the id */ @Id @Column(name = "ID") @GeneratedValue(strategy = GenerationType.AUTO) public Long getId() { return id; } /** * Sets the id. * * @param id * the new id */ public void setId(Long id) { this.id = id; } /** * Gets the name. * * @return the name */ @Column(name = "NAME") public String getName() { return name; } /** * Sets the name. * * @param name * the new name */ public void setName(String name) { this.name = name; } /** * Gets the author. * * @return the author */ @Column(name = "AUTHOR") public String getAuthor() { return author; } /** * Sets the author. * * @param author * the new author */ public void setAuthor(String author) { this.author = author; } /** * (non-Javadoc) * * @see java.lang.Object#hashCode() */ @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((author == null) ? 0 : author.hashCode()); result = prime * result + ((id == null) ? 0 : id.hashCode()); result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } /** * (non-Javadoc) * * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (!(obj instanceof Book)) { return false; } Book other = (Book) obj; if (author == null) { if (other.author != null) { return false; } } else if (!author.equals(other.author)) { return false; } if (id == null) { if (other.id != null) { return false; } } else if (!id.equals(other.id)) { return false; } if (name == null) { if (other.name != null) { return false; } } else if (!name.equals(other.name)) { return false; } return true; } /** * (non-Javadoc) * * @see java.lang.Object#toString() */ @Override public String toString() { return "Book [id=" + id + ", name=" + name + ", author=" + author + "]"; } }
The major difference from plain old JPA and "Spring Data JPA" is usage of repositories. A Repository provides you with the generic framework for dealing with persistence layer boilerplate code like below:
public void save(MyEntity myEntity){ if(myEntity.getId() != null) { entityManager.merge(myEntity); } else { entityManager.persist(myEntity); } }
Spring Data JPARepository provides you with enough generic services to take care of most of the scenarios. Given this let's write our Repository called as IBookRepository
which is an interface and extends JpaRepository
. JpaRepository
is parameterized interface so we need to provide the Entity class and class of the identifier(Primary Key). In this case our Entity class is Book
and identifier is Long
.
package com.mumz.test.springdata.jpa.repository; import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import com.mumz.test.springdata.jpa.entity.Book; /** * The Interface IBookRepository. * * @author prabhat.jha */ public interface IBookRepository extends JpaRepository<Book, Long> { /** * Find book by id. * * @param bookId * the book id * @return the book */ public Book findBookById(Long bookId); /** * Find all books. * * @return the list */ public List<Book> findAll(); }
Next we will write our Service interface IBookService
and it's implementation class BookServiceImpl
. IBookService
is a plain interface and self-explanatory.
package com.mumz.test.springdata.jpa.service; import java.util.List; import com.mumz.test.springdata.jpa.entity.Book; /** * The Interface IBookService. * @author prabhat.jha */ public interface IBookService { /** * Save. * * @param book the book * @return the book */ public Book save(Book book); /** * Find book by id. * * @param bookId the book id * @return the book */ public Book findBookById(Long bookId); /** * Find all books. * * @return the list */ public List<Book> findAllBooks(); }
Next is our service implementation class BookServiceImpl
. If you take a closer look you will see we don't have any EntityManager instead we have our IBookRepository
. Same IBookRepository
would be used to save and retrieve records.
package com.mumz.test.springdata.jpa.service; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.mumz.test.springdata.jpa.entity.Book; import com.mumz.test.springdata.jpa.repository.IBookRepository; /** * The Class BookServiceImpl. * * @author prabhat.jha */ @Service(value = "bookService") @Transactional public class BookServiceImpl implements IBookService { /** The book repository. */ @Autowired private IBookRepository bookRepository = null; /** * (non-Javadoc) * * @see com.mumz.test.springdata.jpa.service.IBookService#save(com.mumz.test.springdata.jpa.entity.Book) */ @Override public Book save(Book book) { return bookRepository.save(book); } /** * (non-Javadoc) * * @see com.mumz.test.springdata.jpa.service.IBookService#findBookById(java.lang.Long) */ @Override public Book findBookById(Long bookId) { return bookRepository.findBookById(bookId); } /** * (non-Javadoc) * * @see com.mumz.test.springdata.jpa.service.IBookService#findAllBooks() */ @Override public List<Book> findAllBooks() { return bookRepository.findAll(); } /** * Gets the book repository. * * @return the book repository */ public IBookRepository getBookRepository() { return bookRepository; } /** * Sets the book repository. * * @param bookRepository * the new book repository */ public void setBookRepository(IBookRepository bookRepository) { this.bookRepository = bookRepository; } }
And finally our applicationContext.xml
. Since we are using annotation driven configuration it is important to tell Spring where to search for our repositories and so that it can search and keep it ready for DI.
<?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:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" 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-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd"> <tx:annotation-driven /> <jpa:repositories base-package="com.mumz.test.springdata.jpa" /> <context:component-scan base-package="com.mumz.test.springdata.jpa" /> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName"> <value>oracle.jdbc.driver.OracleDriver</value> </property> <property name="url"> <value>jdbc:oracle:thin:@localhost:1521:MYDB</value> </property> <property name="username"> <value>MYUSER</value> </property> <property name="password"> <value>MYPASSWORD</value> </property> </bean> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> <property name="database"> <value>ORACLE</value> </property> <property name="showSql" value="true" /> <property name="databasePlatform" value="org.hibernate.dialect.Oracle10gDialect" /> </bean> </property> </bean> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory" /> </bean> </beans>
We also need persistence.xml
since we are dealing with JPA.
<?xml version="1.0" encoding="UTF-8"?> <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> <persistence-unit name="springdata" > </persistence-unit> </persistence>
Finally our test class BookServiceTest
/** * */ package com.mumz.test.springdata.jpa; import static org.junit.Assert.assertNotNull; import java.util.List; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestExecutionListeners; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; import org.springframework.test.context.transaction.TransactionConfiguration; import org.springframework.test.context.transaction.TransactionalTestExecutionListener; import org.springframework.transaction.annotation.Transactional; import com.mumz.test.springdata.jpa.entity.Book; import com.mumz.test.springdata.jpa.repository.IBookRepository; import com.mumz.test.springdata.jpa.service.IBookService; /** * The Class BookServiceTest. * * @author prabhat.jha */ @RunWith(SpringJUnit4ClassRunner.class) @TestExecutionListeners({ TransactionalTestExecutionListener.class, DependencyInjectionTestExecutionListener.class }) @ContextConfiguration(locations = { "/META-INF/applicationContext.xml" }) @Transactional @TransactionConfiguration(defaultRollback = false) public class BookServiceTest { /** The last created book id. */ private Long lastCreatedBookId = null; /** The book service. */ @Autowired @Qualifier(value = "bookService") private IBookService bookService; /** The book repository. */ @Autowired private IBookRepository bookRepository; /** * Sets the up. * * @throws Exception the exception */ @Before public void setUp() throws Exception { } /** * Tear down. * * @throws Exception the exception */ @After public void tearDown() throws Exception { } /** * Test create book. */ @Test public void testCreateBook() { Book book = new Book(); book.setName("Spring Data JPA - Reference Documentation"); book.setAuthor("Oliver Gierke"); book = this.getBookRepository().save(book); assertNotNull("Test failed create book returned null id",book.getId()); lastCreatedBookId = book.getId(); } /** * Test find book by id. */ @Test public void testFindBookById() { Book book = this.getBookRepository().findBookById(lastCreatedBookId); assertNotNull(book); } /** * Test find all books. */ @Test public void testFindAllBooks() { List<Book> books = this.getBookRepository().findAll(); assertNotNull("Test failed findAllBooks returned null", books); if (books != null) { for (Book book : books) { System.out.println(book); } } } /** * Gets the book service. * * @return the book service */ public IBookService getBookService() { return bookService; } /** * Sets the book service. * * @param bookService the new book service */ public void setBookService(IBookService bookService) { this.bookService = bookService; } /** * Gets the book repository. * * @return the book repository */ public IBookRepository getBookRepository() { return bookRepository; } /** * Sets the book repository. * * @param bookRepository the new book repository */ public void setBookRepository(IBookRepository bookRepository) { this.bookRepository = bookRepository; } }
Hope this helps!
N.B: You can download the code from Spring Data JPA