歡迎光臨
每天分享高質量文章

一起來學 SpringBoot 2.x | 第六篇:整合 Spring Data JPA

點擊上方“芋道原始碼”,選擇“置頂公眾號”

技術文章第一時間送達!

原始碼精品專欄

 


摘要: 原文可閱讀 http://www.iocoder.cn/Spring-Boot/battcn/v2-orm-jpa/ 「唐亞峰」歡迎轉載,保留摘要,謝謝!

  • JPA

  • Spring Data JPA

  • 匯入依賴

  • 連接資料庫

  • JPA配置

  • 輸出日誌

  • 資料庫型別

  • 具體編碼

  • 物體類

  • Repository

  • 測試

  • 總結

  • 說點什麼


SpringBoot 是為了簡化 Spring 應用的創建、運行、除錯、部署等一系列問題而誕生的產物,自動裝配的特性讓我們可以更好的關註業務本身而不是外部的XML配置,我們只需遵循規範,引入相關的依賴就可以輕易的搭建出一個 WEB 工程

上一篇介紹了Spring JdbcTemplate的使用,對比原始的JDBC而言,它更加的簡潔。但隨著表的增加,重覆的CRUD工作讓我們苦不堪言,這時候Spring Data Jpa的作用就體現出來了…..

JPA

JPA是Java Persistence API的簡稱,中文名Java持久層API,是官方(Sun)在JDK5.0後提出的Java持久化規範。其目的是為了簡化現有JAVA EEJAVA SE應用開發工作,以及整合現有的ORM技術實現規範統一

JPA的總體思想和現有HibernateTopLinkJDO等ORM框架大體一致。總的來說,JPA包括以下3方面的技術:

  • ORM映射元資料: 支持XML和註解兩種元資料的形式,元資料描述物件和表之間的映射關係,框架據此將物體物件持久化到資料庫表中;

  • API: 操作物體物件來執行CRUD操作,框架在後臺替代我們完成所有的事情,開發者從繁瑣的JDBC和SQL代碼中解脫出來。

  • 查詢語言: 通過面向物件而非面向資料庫的查詢語言查詢資料,避免程式的SQL陳述句緊密耦合。

JPA只是一種規範,它需要第三方自行實現其功能,在眾多框架中Hibernate是最為強大的一個。從功能上來說,JPA就是Hibernate功能的一個子集。Hibernate 從3.2開始,就開始兼容JPA。同時Hibernate3.2獲得了Sun TCK的JPA(Java Persistence API) 兼容認證。

Spring Data JPA

常見的ORM框架中Hibernate的JPA最為完整,因此Spring Data JPA 是採用基於JPA規範的Hibernate框架基礎下提供了Repository層的實現。Spring Data Repository極大地簡化了實現各種持久層的資料庫訪問而寫的樣板代碼量,同時CrudRepository提供了豐富的CRUD功能去管理物體類。

優點

  • 豐富的API,簡單操作無需編寫額外的代碼

  • 豐富的SQL日誌輸出

缺點

  • 學習成本較大,需要學習HQL

  • 配置複雜,雖然SpringBoot簡化的大量的配置,關係映射多表查詢配置依舊不容易

  • 性能較差,對比JdbcTemplateMybatis等ORM框架,它的性能無異於是最差的

匯入依賴

在 pom.xml 中添加 spring-boot-starter-data-jpa 的依賴


<dependency>
    <groupId>org.springframework.bootgroupId>


    <artifactId>spring-boot-starter-data-jpaartifactId>
dependency>

<dependency>
    <groupId>mysqlgroupId>
    <artifactId>mysql-connector-javaartifactId>
dependency>

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-webartifactId>
dependency>

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-testartifactId>
    <scope>testscope>
dependency>

連接資料庫

application.properties中添加如下配置。值得註意的是,SpringBoot預設會自動配置DataSource,它將優先採用HikariCP連接池,如果沒有該依賴的情況則選取tomcat-jdbc,如果前兩者都不可用最後選取Commons DBCP2通過spring.datasource.type屬性可以指定其它種類的連接池

spring.datasource.url=jdbc:mysql://localhost:3306/chapter5?useUnicode=true&characterEncoding;=UTF-8&zeroDateTimeBehavior;=convertToNull&allowMultiQueries;=true&useSSL;=false
spring.datasource.password=root
spring.datasource.username=root
#spring.datasource.type
# JPA配置
spring.jpa.hibernate.ddl-auto=update
# 輸出日誌
spring.jpa.show-sql=true
# 資料庫型別
spring.jpa.database=mysql

ddl-auto 幾種屬性

  • create: 每次運行程式時,都會重新創建表,故而資料會丟失

  • create-drop: 每次運行程式時會先創建表結構,然後待程式結束時清空表

  • upadte: 每次運行程式,沒有表時會創建表,如果物件發生改變會更新表結構,原有資料不會清空,只會更新(推薦使用)

  • validate: 運行程式會校驗資料與資料庫的欄位型別是否相同,欄位不同會報錯

具體編碼

由於上面我們採用的是spring.jpa.hibernate.ddl-auto=update方式,因此這裡可以跳過手動建表的操作

物體類

JPA規範註解坐落在javax.persistence包下,@Id註解一定不要取用錯了,否則會報錯@GeneratedValue(strategy = GenerationType.IDENTITY)自增策略,不需要映射的欄位可以通過@Transient註解排除掉

常見的幾種自增策略

  • TABLE: 使用一個特定的資料庫表格來儲存主鍵

  • SEQUENCE: 根據底層資料庫的序列來生成主鍵,條件是資料庫支持序列。這個值要與generator一起使用,generator 指定生成主鍵使用的生成器(可能是orcale中自己編寫的序列)。

  • IDENTITY: 主鍵由資料庫自動生成(主要是支持自動增長的資料庫,如mysql)

  • AUTO: 主鍵由程式控制,也是GenerationType的預設值。

package com.battcn.entity;

import javax.persistence.GenerationType;
import javax.persistence.Id;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import java.io.Serializable;

/**
 * @author Levin
 * @since 2018/5/7 0007
 */

@Entity(name = "t_user")
public class User implements Serializable {

    private static final long serialVersionUID = 8655851615465363473L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String username;
    private String password;
    /**
     * TODO 忽略該欄位的映射
     */

    @Transient
    private String  email;

    // TODO  省略get set
}

Repository

創建UserRepository資料訪問層接口,需要繼承JpaRepository第一個泛型引數是物體物件的名稱,第二個是主鍵型別。只需要這樣簡單的配置,該UserRepository就擁常用的CRUD功能,JpaRepository本身就包含了常用功能,剩下的查詢我們按照規範寫接口即可,JPA支持@Query註解寫HQL,也支持findAllByUsername這種根據欄位名命名的方式(強烈推薦IntelliJ IDEA對JPA支持非常NICE)

package com.battcn.repository;

import com.battcn.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.List;

/**
 * t_user 操作
 *
 * @author Levin
 * @since 2018/5/7 0007
 */

@Repository
public interface UserRepository extends JpaRepository<UserLong{

    /**
     * 根據用戶名查詢用戶信息
     *
     * @param username 用戶名
     * @return 查詢結果
     */

    List findAllByUsername(String username);
}

測試

完成資料訪問層接口後,最後編寫一個junit測試類來檢驗代碼的正確性。

下麵的幾個操作中,只有findAllByUsername是我們自己編寫的代碼,其它的都是繼承自JpaRepository接口中的方法,更關鍵的是分頁及排序是如此的簡單實體化一個Pageable即可…

package com.battcn;

import com.battcn.entity.User;
import com.battcn.repository.UserRepository;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.List;

/**
 * @author Levin
 */

@RunWith(SpringRunner.class)
@SpringBootTest
public class Chapter5ApplicationTests {

    private static final Logger log = LoggerFactory.getLogger(Chapter5ApplicationTests.class);

    @Autowired
    private UserRepository userRepository;

    @Test
    public void test1() throws Exception {
        final User user = userRepository.save(new User("u1""p1"));
        log.info("[添加成功] - [{}]", user);
        final List u1 = userRepository.findAllByUsername("u1");
        log.info("[條件查詢] - [{}]", u1);
        Pageable pageable = PageRequest.of(010, Sort.by(Sort.Order.desc("username")));
        final Page users = userRepository.findAll(pageable);
        log.info("[分頁+排序+查詢所有] - [{}]", users.getContent());
        userRepository.findById(users.getContent().get(0).getId()).ifPresent(user1 -> log.info("[主鍵查詢] - [{}]", user1));
        final User edit = userRepository.save(new User(user.getId(), "修改後的ui""修改後的p1"));
        log.info("[修改成功] - [{}]", edit);
        userRepository.deleteById(user.getId());
        log.info("[刪除主鍵為 {} 成功] - [{}]", user.getId());
    }
}

總結

更多內容請參考官方文件

目前很多大佬都寫過關於 SpringBoot 的教程了,如有雷同,請多多包涵,本教程基於最新的 spring-boot-starter-parent:2.0.1.RELEASE編寫,包括新版本的特性都會一起介紹…

說點什麼

全文代碼:https://github.com/battcn/spring-boot2-learning/tree/master/chapter5

666. 彩蛋




如果你對 Dubbo 感興趣,歡迎加入我的知識星球一起交流。

知識星球

目前在知識星球(https://t.zsxq.com/2VbiaEu)更新瞭如下 Dubbo 原始碼解析如下:

01. 除錯環境搭建
02. 專案結構一覽
03. 配置 Configuration
04. 核心流程一覽

05. 拓展機制 SPI

06. 執行緒池

07. 服務暴露 Export

08. 服務取用 Refer

09. 註冊中心 Registry

10. 動態編譯 Compile

11. 動態代理 Proxy

12. 服務呼叫 Invoke

13. 呼叫特性 

14. 過濾器 Filter

15. NIO 服務器

16. P2P 服務器

17. HTTP 服務器

18. 序列化 Serialization

19. 集群容錯 Cluster

20. 優雅停機

21. 日誌適配

22. 狀態檢查

23. 監控中心 Monitor

24. 管理中心 Admin

25. 運維命令 QOS

26. 鏈路追蹤 Tracing


一共 60 篇++

赞(0)

分享創造快樂