在上篇文章中對Spring MVC經常使用的一些註解作了簡要的說明,在這篇文章中主要對Spring Data JPA 作一個簡要的說明,並附有一個簡單的例子,能夠體會到Spring Data JPA 的強大之處。html
JPA(Java Persistence API)是Sun官方提出的Java持久化規範。它爲Java開發人員提供了一種對象/關聯映射工具來管理Java應用中的關係數據。他的出現主要是爲了簡化現有的持久化開發工做和整合ORM技術,結束如今Hibernate,TopLink,JDO等ORM框架各自爲營的局面。值得注意的是,JPA是在充分吸取了現有Hibernate,TopLink,JDO等ORM框架的基礎上發展而來的,具備易於使用,伸縮性強等優勢。
JPA定義了在對數據庫中的對象處理查詢和事務運行時的EntityManager的API。JPA定義一個對象級查詢語言,JPQL,若是學習過Hibernate的話你能夠把它看作Hibernate中的Hql語句,以容許從所述數據庫中的對象的查詢。
下面是JPA經常使用的一些解決方案:java
EclipseLink (Eclipse) Hibernate (RedHat) Open JPA (Apache) DataNucleus Ebean (SourceForge) TopLink Essentials (Glassfish) TopLink (Oracle) Kodo (Oracle)
你能夠把它作作事JPA的超集 相似於 C與C++的關係,Spring Data JPA能夠極大的簡化JPA的寫法,能夠在幾乎不用寫實現的狀況下,實現對數據的訪問和操做。除了CRUD外,還包括如分頁、排序等一些經常使用的功能。web
也就是說Spring Data JPA 在JPA的基礎上提供了更高層次的抽象,幫助咱們實現了許多經常使用的對數據庫的操做,從而提升開發效率。spring
在瞭解Spring Data JPA以前咱們首先看一下它爲咱們提供了那些便利。sql
面對簡單的查詢時能夠經過解析方法名建立查詢或使用使用 @Query 建立查詢語句數據庫
好比如今咱們有個方法叫作 User findByName(String name),咱們能夠很清楚的明白它的意思,但有沒有辦法讓ORM框架根據方法名幫助咱們推斷出Sql來呢?在Spring Data JPA中這是能夠的,咱們只要將咱們的接口繼承 org.springframework.data.repository.Repository <T, ID extends java.io.Serializable>,或者使用@RepositoryDefinition 註解進行修飾。json
public interface UserRepository extends Repository<User, Long> { User findByName(String name) } @RepositoryDefinition(domainClass = User.class, idClass = Long.class) public interface UserRepository{ User findByName(String name) }
public interface UserRepository extends Repository<User, Long> { //對於更新操做須要添加@Modifying @Query("from User u where u.name=:name") User findUser(@Param("name") String name); }
固然還不止這些Spring Data JPA 還未咱們提供了幾個經常使用的Repository:數組
經過繼承它們能夠得到更強大的功能,固然它們之所可以運行是由於有了默認的實現類:app
@Repository @Transactional(readOnly = true) public class SimpleJpaRepository<T, ID extends Serializable> implements JpaRepository<T, ID>, JpaSpecificationExecutor<T> { }
在查詢的時候,一般須要同時根據多個屬性進行查詢,Spring Data JPA 爲此提供了一些表達條件查詢的關鍵字,大體以下:框架
固然面對比較複雜的查詢時,它們就顯得無力了,若是在面對比較複雜的查詢時,想要有本身的實現方法,只須要經過一個XXXImpl結尾的實現類便可使用咱們本身的實現方法(好比UserRepository接口的同一個包下面創建一個普通類UserRepositoryImpl來表示該類的實現類)。
瞭解更多:使用 Spring Data JPA 簡化 JPA 開發 - IBM
面對複雜的查詢時可使用本地查詢或JPQL或使用JPA的動態接口(JpaSpecificationExecutor)
在面對負責的條件查詢時,須要有條件判斷這個時候方法名推斷就顯得不夠強大了,好比當這裏有個簡單的例子:「咱們要根據用戶的年齡,地址,性別 查詢用戶信息時」, 在考慮可能存在條件爲空的狀況下,在使用方法名推斷時咱們須要提供8個相應的方法,固然若是使用流的話有一個findAll方法便可,可是這無疑增大了數據庫與發射生成對象壓力因此並不推薦這種方式,那麼在這種狀況下若是可使用判斷進行條件拼接的話,即可以將查詢的方法縮減到一個 這即是以上三種方式的優勢(通常狀況下不推薦 本地查詢)。
DO:
@Entity public class User extends AbstractPersistable<Long> { private String name; private Integer age; private String AddressCode; @Enumerated(EnumType.ORDINAL) private Sex sex; /**省略get/set**/ }
DAO:
public interface UserCustomRepository { Page<User> search(Integer age, String AddressCode, User.Sex sex, Pageable pageRequest); } public interface UserRepository extends CrudRepository<User, Long>, UserCustomRepository { } public class UserRepositoryImpl implements UserCustomRepository { @PersistenceContext private EntityManager em; @Override public Page<User> search(Integer age, String AddressCode, User.Sex sex, Pageable pageRequest) { String querySql = "select t "; String countSql = "select count(t) "; StringBuffer sqlBuffer = new StringBuffer("from User t where 1=1"); if (null != age) { sqlBuffer.append(" and t.age = :age"); } if (null != AddressCode) { sqlBuffer.append(" and t.AddressCode = :address"); } if (null != sex) { sqlBuffer.append(" and t.sex = :sex"); } querySql += sqlBuffer.toString(); countSql += sqlBuffer.toString(); Query dataQuery = em.createQuery(querySql); Query countQuery = em.createQuery(countSql); if (null != age) { dataQuery.setParameter("age", age); countQuery.setParameter("age", age); } if (null != AddressCode) { dataQuery.setParameter("address", AddressCode); countQuery.setParameter("address", AddressCode); } if (null != sex) { dataQuery.setParameter("sex", sex); countQuery.setParameter("sex", sex); } // 本地查詢方式 // Query query = em.createNativeQuery(sql); // query.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.aliasToBean(User.class)); Page<User> page = (pageRequest == null ? new PageImpl(dataQuery.getResultList()) : this.readPage(dataQuery, countQuery, pageRequest)); return page; } private Page<User> readPage(Query dataQuery, Query countQuery, Pageable pageable) { dataQuery.setFirstResult(pageable.getOffset()); dataQuery.setMaxResults(pageable.getPageSize()); long totalSize = (long) countQuery.getSingleResult(); List<User> content = totalSize > (long) pageable.getOffset() ? dataQuery.getResultList() : Collections.emptyList(); return new PageImpl(content, pageable, totalSize); } }
Test:
@RunWith(SpringRunner.class) @SpringBootTest @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class SpringBootJpaApplicationTests { @Autowired private UserRepository userRepository; @Test public void contextLoads() { List<User> userList = new ArrayList<>(); for (int i = 0; i < 10; i++) { String name = "name" + i; Integer age = 19 + (i + 1) % 5; String addressCode = "address"; User.Sex sex = i % 2 == 0 ? User.Sex.MAN : User.Sex.WOMAN; User user = new User(name, age, addressCode, sex); System.out.println(JSON.toJSON(user)); userList.add(user); } userRepository.save(userList); } @Test public void search() { int page = 0; int size = 2; Pageable pageable = new PageRequest(page, size); Page<User> pageUser = userRepository.search(null, null, null, pageable); pageUser = userRepository.search(19, null, User.Sex.WOMAN, pageable); System.out.println(JSON.toJSON(pageUser)); } }
只作了簡單的修改
DO:
public interface UserRepository extends CrudRepository<User, Long>, UserCustomRepository, JpaSpecificationExecutor<User> { }
Test:
@RunWith(SpringRunner.class) @SpringBootTest @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class SpringBootJpaApplicationTests { @Autowired private UserRepository userRepository; @Test public void contextLoads() { List<User> userList = new ArrayList<>(); for (int i = 0; i < 10; i++) { String name = "name" + i; Integer age = 19 + (i + 1) % 5; String addressCode = "address"; User.Sex sex = i % 2 == 0 ? User.Sex.MAN : User.Sex.WOMAN; User user = new User(name, age, addressCode, sex); System.out.println(JSON.toJSON(user)); userList.add(user); } userRepository.save(userList); } @Test public void search() { int page = 0; int size = 2; Pageable pageable = new PageRequest(page, size); Page<User> pageUser = userRepository.search(null, null, null, pageable); // System.out.println(JSON.toJSON(pageUser)); pageUser = userRepository.search(19, null, User.Sex.WOMAN, pageable); System.out.println(JSON.toJSON(pageUser)); // pageUser = userRepository.search(null, null, null, pageable); // System.out.println(JSON.toJSON(pageUser)); // pageUser = userRepository.search(null, null, null, pageable); // System.out.println(JSON.toJSON(pageUser)); } @Test public void findAll() { int page = 0; int size = 2; Pageable pageable = new PageRequest(page, size); Page<User> pageUser = search(19, null, User.Sex.WOMAN, pageable); System.out.println(JSON.toJSON(pageUser)); } public Page<User> search(final Integer age, final String AddressCode, final User.Sex sex, Pageable pageRequest) { /* 第一種查詢方式 Specification<User> specification = (Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) -> { //建立查詢條件 List<Predicate> predicates = new ArrayList<>(); if (age != null) { predicates.add(cb.equal(root.<Integer>get("age"), age)); } if (AddressCode != null) { predicates.add(cb.equal(root.get("AddressCode").as(String.class), AddressCode)); } if (sex != null) { predicates.add(cb.equal(root.get("sex").as(User.Sex.class), sex)); } return cb.and(predicates.toArray(new Predicate[predicates.size()])); }; */ //第二種 Specification<User> specification = (Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) -> { //建立查詢條件 List<Predicate> predicates = new ArrayList<>(); if (age != null) { predicates.add(cb.equal(root.<Integer>get("age"), age)); } if (AddressCode != null) { predicates.add(cb.equal(root.get("AddressCode").as(String.class), AddressCode)); } if (sex != null) { predicates.add(cb.equal(root.get("sex").as(User.Sex.class), sex)); } Predicate[] assetTradingArray = new Predicate[predicates.size()]; predicates.toArray(assetTradingArray); query.where(assetTradingArray);//這種方式使用JPA的API設置了查詢條件,因此不須要再返回查詢條件Predicate給Spring Data Jpa,故最後return null;便可。 return null; }; return userRepository.findAll(specification, pageRequest); } }
瞭解更多:純乾貨,Spring-data-jpa詳解,全方位介紹。
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.2.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!--tool--> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.23</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
spring: jpa: show-sql: true properties: hibernate: hbm2ddl: auto: update
@SpringBootApplication public class SpringBootJpaApplication { public static void main(String[] args) { SpringApplication.run(SpringBootJpaApplication.class, args); } }
public interface UserRepository extends CrudRepository<User, Long>, UserCustomRepository, JpaSpecificationExecutor<User> { }
教程:
博客介紹:
視頻:
Spring Data JPA 的解析方法名建立查詢很是強大,只是聲明持久層的接口,相關的查詢它就已經幫你去作了,讓我想起一個問題框架的終點在哪?是人工智能嗎?
全面闡釋和精彩總結JPA
Spring Data 系列(二) Spring+JPA入門(集成Hibernate)
Spring Data概念【從零開始學Spring Boot】