Easy Data with Spring-Data JPA
Miya W. Longwe, Tech Lead, Staples Inc.January 07, 2014
Java Meetup Group, Cambridge, MA , USA
Agenda Java DB Access Ordeal
Enter Spring-Data
Spring-Data JPA Features
Code Demo
Q and A
Java DB AccessThe Developer’s Holy Pilgrim!
Application Domain Domain driven design has become a
ubiquitous approach to tackle complex problem domains and build a rich object model.
Implementing a data access layer of an application has been cumbersome for quite a while.
Too much boilerplate code has to be written.
Code to execute simple queries as well as perform pagination, auditing, etc
Java DB Access – AccessorInterface
public interface CustomerService {Customer findById(Long id);Customer save(Customer customer);List<Customer> findAll();List<Customer> findAll(int page, int pageSize);...}
Java DB Access –The Boilerplate Code
/** * Plain JPA implementation of {@link CustomerService}. * * @author Miya W Longwe */@Repository@Transactional(readOnly = true)public class CustomerServiceImpl implements CustomerService {
@PersistenceContextprivate EntityManager em;
@Overridepublic Customer findById(Long id) {
return em.find(Customer.class, id);}
@Overridepublic List<Customer> findAll() {
return em.createQuery("select c from Customer c", Customer.class).getResultList();
}……
}
Java DB Access – The Story● JPA handles mechanics of ORM● The catch:– You are responsible for accessor boilerplate code● Using direct JDBC?– More boilerplate code (think DAO layer)● What about Spring support?– Makes things better (JdbcTemplate)– Spring-Data eases the pain further
Spring-Data to the Rescue!
Spring-Data Uses the Repository abstraction for
data access
Automation of data access boilerplate code
Reduces level of efforts for accessor code
Support for multiple data stores including– JPA– Key-Value, column, document, graph data stores(Redis, Mongo, Neo4j, HBase)– Hadoop / HDFS – Others
Spring-Data JPA Workflow Define an Entity Class
Define a Repository interface with data accessor methods
Then see you gator!
Define Your Entity
/** * An entity class which contains the information of a single person. * @author Miya W. Longwe */@Entity@Table(name = "persons")public class Person { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Column(name = "creation_time", nullable = false) private Date creationTime; @Column(name = "first_name", nullable = false) private String firstName;
@Column(name = "last_name", nullable = false) private String lastName; @Column(name = "modification_time", nullable = false) private Date modificationTime; ...}
Define The Repository Interface
You provide a Java interface
– Attach an entity type along with key type
– CRUD/data accessor method signatures
Spring-Data can automatically derive proper JPQL
In simpler cases, no additional code required
Queries are derived from method signatures
Repository Interface
* Specifies methods used to obtain and modify person related information * which is stored in the database. * @author Miya W. Longwe */public interface PersonRepository extends JpaRepository<Person, Long> {}
No More Boilerplate Code It goes away (sort of) *
Spring-Data framework derives and attaches JPQL (or specified query) at load-time
Spring-Data Features
Provided Repositories Spring-Data JPA provides two
repositories
CrudRepository– Long list of standard CRUD operations provided– findOne(), findAll(), save(), delete(), exists(), etc
PagingAndSortingRepository
– Derived from CrudRepository
– Provides paginated repository access methods
Configure Spring Framework Specify your repository locations for
scanning
Spring will create proxy instances for repositories
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http:/www.springframework.org/schema/beans" xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd"><mvc:resources mapping="/static/**" location="/static/"/>
<mvc:default-servlet-handler/>
<!-- Configures Spring Data JPA and sets the base package of my DAOs. --> <jpa:repositories base-package="com.meetup.easydata.spring.datajpa.repository"/></beans>
Spring-Data Query Generation Derived from method signatures
Parses method names for attributes and keywords
Uses method parameters as query params
public interface UserRepository extends CrudRepository<User, Long> {List<Customer> findByEmailAddressAndLastname(String emailAddress, String lastname);}
Select c from Customer where c.emailAddress = ?1 and c.lastName = ?2
Spring-DataMethod Name-to-JPQL Mapping
Method Name Generated JPQL
findByXxxAndYyy(aaa, bbb)
... where t.xxx = ?1 and t.yyy = ?2
findByXxxOrYyy(aaa, bbb)
... where t.xxx = ?1 or t.yyy = ?2
findByXxxStartingWith(aaa)
('%' appended to param value)
findByXxxNot(aaa) ... where t.xxx <> ?1
findByXxxIn(Collection<E>aaa)
...where t.xxx in ?1
---and many more!
Spring-DataFurther Property Parsing Features
-Traversal can reach into nested properties -Will do best effort using camelCase-You can delineate properties using “_”
}@Entitypublic class User <Long> {private ZiCode zicode;--}
}public interface UserRepository extends CrudRepository<User, Long>{...User findByAddress_ZipCode(ZipCode zipCode);
@Query – Use Your Own Query
• You don't like the derived query or want to do
something fancier?● Use @Query notation to provide your own● Support both JPQL or native SQL● Still provides automatic proxy implementationpublic interface UserRepository extends
CrudRepository<User, Long>{...@Query("select u from User u where u.firstname = ?1")List<User> findByFirstname(String firstname);@Query(value="SELECT FROM USERS WHERE EMAIL_ADDRESS = ?1" nativeQuery=true)User findByEmailAddress(String email);
...
@Query – Named Params
• Spring-Data JPA will use position for parameter binding• You can also use named params instead
interface UserRepository extends CrudRepository<User, Long>{...@Query("select u from User u where u.firstname = :name or u.lastname = :name")List<User> findByFirstnameOrLastname(@Param("name") String name);...}
Result Pagination
• Seamlessly provides support for result set pagination via Pageable Interface
• Define repository method with Pageable• Call method with PageRequest class (or define your
own)
public interface ProductRepository extends CrudRepository<User, Long>{...Page<Product> findAll(Pageable pageable);...}
class ProductService {Pageable pageable = new PageRequest(1, 20);Page<Product> page = repository.findByDescriptionContaining(pageable);}
Custom Repositories
• When Spring-Data JPA derived queries are not• enough or you need additional logic• Provide your own repository implementation• A bean that lives in Spring Context
interface UserRepositoryCustom {List<User> myCustomBatchOperation();}
class UserRepositoryImpl implements UserRepositoryCustom {@PersistenceContextprivate EntityManager em;public List<User> myCustomBatchOperation() {CriteriaQuery<User> criteriaQuery = em.getCriteriaBuilder().createQuery(User.class);return em.createQuery(criteriaQuery).getResultList();}}
Transaction Support
• Repository classes are transactional by default• Reads are made readOnly• Other methods are @Transactional by default• Ability to override by providing your own@Transactional demarcation
public interface UserRepository extends CrudRepository<User, Long>{...@Transactional(timeout=10)@Modifying@Query("update User u set u.firstname = ?1 where u.lastname = ?2")User fixFirstNameByLastName(String firstname, String lastname);...}
Code Demo