做者:追夢1819
原文:https://www.cnblogs.com/yanfei1819/p/10910059.html
版權聲明:本文爲博主原創文章,轉載請附上博文連接!
html
前面幾章,咱們介紹了 JDBCTemplate、MyBatis 等 ORM 框架。下面咱們來介紹極簡模式的 Spring Data JPA。
java
咱們先來了解幾個基本概念,捋一下各個概念之間的關係。mysql
一、JPAgit
JPA是Java Persistence API的簡稱,中文名Java持久層API,SUN公司出品。是 JDK 5.0 註解或 XML 描述對象-關係表的映射關係,並將運行期的實體對象持久化到數據庫中。github
它的出現目的有兩個:web
二、JPA 和 hibernate 的關係redis
JPA 是 Hibernate 的一個抽象,就像 JDBC 和 JDBC 驅動的關係;spring
JPA 是一種 ORM 規範,是 Hibernate 功能的一個子集 ;sql
Hibernate 是 JPA 的一個實現;mongodb
三、Spring Data
衆所周知,Spring 是一個你們庭,一個技術的生態圈,其中整合的內容一應俱全。而 Spring Data xxx 系列就是 Spring 這個生態圈對數據持久化層的整合。
Spring Data是Spring用來解決數據訪問的解決方案,它包含了大量關係型數據庫以及NoSQL數據庫的數據持久層訪問解決方案。它包含Spring Data JPA、Spring Data MongoDB、Spring Data Neo4j、Spring Data GernFile、Spring Data Cassandra等。
目前現有的 Spring-data-jpa,Spring-data-template,Spring-data-mongodb,Spring-data-redis 等。固然,也包括最開始用的 mybatis-spring ,MyBatis 與 Spring 的整合。
因而,就出現了 Spring-data-jpa ,Spring 與 JPA 的整合。
具體詳情參看 Spring Data 官網介紹 。
四、關於 "dao"
在 Java web 項目中,咱們常常會看到這個包名或者以它爲後綴名的類名,它所表明的是,數據庫持久化層,是 Data Access Object 的簡寫。不過有時候咱們也會看到 mapper 等。其實這些都是同一個意思,表明數據庫持久化層。
在 JDBCTemplate 中,可能會用 "dao",在 MyBatis 中,可能會用 "mapper"(對應 xxxMapper.xml文件),在 JPA 中,可能會用"repository"等。
其實名稱徹底是自定義,只不過開發者在開發的時候會有一些約定俗成的習慣,僅此而已。
五、Spring Data JPA
正如上所說,Spring Data JPA 是 Spring 與 JPA 的整合。是基於 JPA 對數據庫訪問層的加強支持。旨在簡化數據庫訪問層,減小工做量。
具體詳情參看 Spring Data JPA 官網。
對於開發者來講,使用起來很簡單。下面咱們看看 Spring Data JPA 幾個核心概念。
Repository:最頂層的接口,是一個空的接口,目的是爲了統一全部Repository的類型,且能讓組件掃描的時候自動識別;
CrudRepository :是Repository的子接口,提供CRUD的功能;
@NoRepositoryBean 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:是PagingAndSortingRepository的子接口,增長了一些實用的功能,好比:批量操做等;
@NoRepositoryBean public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> { List<T> findAll(); List<T> findAll(Sort var1); List<T> findAllById(Iterable<ID> var1); <S extends T> List<S> saveAll(Iterable<S> var1); void flush(); <S extends T> S saveAndFlush(S var1); void deleteInBatch(Iterable<T> var1); void deleteAllInBatch(); T getOne(ID var1); <S extends T> List<S> findAll(Example<S> var1); <S extends T> List<S> findAll(Example<S> var1, Sort var2); }
PagingAndSortingRepository:是 CrudRepository 的子接口,添加分頁和排序的功能;
@NoRepositoryBean public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> { Iterable<T> findAll(Sort sort); Page<T> findAll(Pageable pageable); }
Specification:是Spring Data JPA提供的一個查詢規範,要作複雜的查詢,只需圍繞這個規範來設置查詢條件便可;
public interface Specification<T> extends Serializable { long serialVersionUID = 1L; static <T> Specification<T> not(Specification<T> spec) { return Specifications.negated(spec); } static <T> Specification<T> where(Specification<T> spec) { return Specifications.where(spec); } default Specification<T> and(Specification<T> other) { return Specifications.composed(this, other, CompositionType.AND); } default Specification<T> or(Specification<T> other) { return Specifications.composed(this, other, CompositionType.OR); } @Nullable Predicate toPredicate(Root<T> var1, CriteriaQuery<?> var2, CriteriaBuilder var3); }
JpaSpecificationExecutor:用來作負責查詢的接口。
public interface JpaSpecificationExecutor<T> { Optional<T> findOne(@Nullable Specification<T> var1); List<T> findAll(@Nullable Specification<T> var1); Page<T> findAll(@Nullable Specification<T> var1, Pageable var2); List<T> findAll(@Nullable Specification<T> var1, Sort var2); long count(@Nullable Specification<T> var1); }
下面咱們演示幾種 JPA 的使用。
構建SpringBoot項目,首先引入 maven 依賴:
<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>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency>
而後,application.properties 中配置相關的信息:
server.address= server.servlet.context-path= server.port=8082 spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://192.168.1.88:3306/test?serverTimezone=GMT%2B8 spring.datasource.username=root spring.datasource.password=pass123 # 第一次建表用create,後面使用update # 否則每次從新系統工程都會先刪除表再新建 spring.jpa.hibernate.ddl-auto=create # 控制檯打印sql spring.jpa.show-sql=true
下一步,建立實體類 User:
package com.yanfei1819.jpademo.entity; import javax.persistence.*; /** * Created by 追夢1819 on 2019-05-05. */ @Entity @Table(name="user") public class User { @Id // 字段自動生成 @GeneratedValue(strategy= GenerationType.AUTO) private Long id; private int age; private String name; // set/get 省略 }
再者,建立 dao 層:
package com.yanfei1819.jpademo.repository; import com.yanfei1819.jpademo.entity.User; import org.springframework.data.jpa.repository.JpaRepository; /** * Created by 追夢1819 on 2019-05-05. */ public interface UserRepository extends JpaRepository<User,Long> { }
最後,建立controller 層(此處爲了簡化代碼,省略了 service 層):
package com.yanfei1819.jpademo.web.controller; import com.yanfei1819.jpademo.entity.User; import com.yanfei1819.jpademo.repository.UserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.ResponseBody; import java.util.List; /** * Created by 追夢1819 on 2019-05-05. */ @Controller public class UserController { @Autowired private UserRepository userRepository; @ResponseBody @GetMapping("/queryUser") public List<User> queryUser(){ // 經過 jpa 內置的方法進行查詢 return userRepository.findAll(); } }
在上述示例中,咱們使用的是 JPA 的內置方法 findAll()
,另外也有不少別的方法,以下:
寫了幾個查詢方法。
在以上示例的基礎上,咱們添加一個新的方法:
UserController:
@ResponseBody @GetMapping("/queryUser/{name}") public User queryUserByName(@PathVariable String name){ // 經過自定義的方法進行查詢 return userRepository.findByName(name); }
UserRepository:
User findByName(String name);
spring-data-jpa 會根據方法的名字來自動生成 sql 語句,Spring Data JPA 支持經過定義在 Repository 接口中的方法名來定義查詢,方法名是根據實體類的屬性名來肯定的。咱們只須要按照方法定義的規則便可。其中findBy關鍵字能夠用find、read、readBy、query、queryBy、get、getBy替代。
這種方式不只僅限制這種簡單的參數支持,還包括限制返回的查詢的結果、返回流式查詢結果、異步查詢等,能夠參看示例:
// 限制查詢結果 User findTopByOrderByAgeDesc(); Page<User> queryFirst10ByLastname(String lastname, Pageable pageable); // 返回流式查詢結果 Stream<User> readAllByFirstnameNotNull(); // 異步查詢 @Async Future<User> findByFirstname(String firstname);
在上述示例中添加新的方法:
UserController:
@ResponseBody @GetMapping("/queryUser2/{id}") public User queryUserById(@PathVariable Long id){ // 經過 @Query 註解查詢 return userRepository.withIdQuery(id); }
UserRepository:
// @Query("select u from User u where u.id = :id ") @Query(value = "select u from User u where u.id = ?1 ",nativeQuery = true) User withIdQuery(@Param("id") Long id);
使用這種命名查詢來聲明實體查詢是一種有效的方法,適用於少許查詢。上面的 @Query
註解須要注意的是:
一、@Query(value = "select u from User u where u.id = ?1 ",nativeQuery = true)
中的 nativeQuery
屬性若是設置爲 true 時,表示的意思是:能夠執行原生sql語句,所謂原生sql,也就是說這段sql拷貝到數據庫中,而後把參數值給一下就能運行了,原生的 sql 都是真實的字段,真實的表名。若是設置爲 false 時,就不是原生的 sql ,而不是數據庫對應的真正的表名,而是對應的實體名,而且 sql 中的字段名也不是數據庫中真正的字段名,而是實體的字段名;
二、nativeQuery
若是不設置時,即 @Query("select u from User u where u.id = ?1 ")
,默認是 false;
三、 @Query("select u from User u where u.id = ?1 ")
能夠寫成 @Query("select u from User u where u.id = :id ")
。
四、如涉及到刪除和修改在須要加上@Modifying
.也能夠根據須要添加 @Transactional
對事物的支持,查詢超時的設置等。
在以上的示例中,爲了區分 User 實體類,咱們從新建立一個實體類 UserVo:
package com.yanfei1819.jpademo.entity; import javax.persistence.*; /** * Created by 追夢1819 on 2019-05-23. */ @Entity @Table(name = "user") @NamedQuery(name = "findAll",query = "select u from User u") public class UserVo { @Id @GeneratedValue(strategy= GenerationType.AUTO) private Long id; @Column(name = "age") private int age; @Column(name = "name") private String name; // get/set 省略 }
在 UserController 中加一個方法:
@Autowired private EntityManager entityManager; @ResponseBody @GetMapping("/queryUserByNameQuery") public List<UserVo> queryUserByNameQuery(){ // 經過 @Query 註解查詢 return entityManager.createNamedQuery("findAll").getResultList(); }
啓動程序,訪問 http://localhost:8082/queryUserByNameQuery
,可以查詢到全部用戶:
再看看控制檯打印結果:
注意一點,若是是多個查詢,可使用 @NamedQuerys
。
筆者其實並不推薦使用這種自定義方法,由於它對實體類的污染很嚴重。不利於可讀性。
JPA 能夠單獨使用,只不過 SpringBoot 這個大平臺將該技術引入進來。
JPA也是一個大的分支,細枝末節有不少,做者認爲,對於 JPA 的學習,只要關注三個方面:
1) ORM 映射元數據,支持 XML 和 JDK 註解兩種元數據的形式;
2) 核心的 API;
3) 查詢語言:JPQL。
固然,若是感興趣的,能夠好好研究一下 JPA 的官方文檔,深刻研讀一下其源碼。此處因爲篇幅所限,就不一一闡述。後續有時間會開設一個 JPA 專欄,專門講述這一大分支,歡迎你們持續關注。
話分兩頭,另外一方面,正如前面幾章說的,各個 ORM 技術框架各有優缺點,沒有一種框架是絕對完美的。技術、框架的選型,不必追求最新,追求最適合便可。
源碼:個人GitHub