Spring Boot 2.x基礎教程:使用Spring Data JPA訪問MySQL

在數據訪問這章的第一篇文章《Spring中使用JdbcTemplate訪問數據庫》 中,咱們已經介紹瞭如何使用Spring Boot中最基本的jdbc模塊來實現關係型數據庫的數據讀寫操做。那麼結合Web開發一章的內容,咱們就能夠利用JDBC模塊與Web模塊的功能,綜合着使用來完成一個適用於不少簡單應用場景的後端應用了。java

然而當咱們有必定的開發經驗以後,不難發現,在實際開發過程當中,對數據庫的操做大多能夠歸結爲:「增刪改查」。就最爲廣泛的單表操做而言,除了表和字段不一樣外,語句幾乎都是相似的,開發人員須要寫大量相似而枯燥的語句來完成業務邏輯。mysql

爲了解決這些大量枯燥的數據操做語句,誕生了很是多的優秀框架,好比:Hibernate。經過整合Hibernate,咱們可以以操做Java實體的方式來完成對數據的操做,經過框架的幫助,對Java實體的變動最終將自動地映射到數據庫表中。git

在Hibernate的幫助下,Java實體映射到數據庫表數據完成以後,再進一步解決抽象各個Java實體基本的「增刪改查」操做,咱們一般會以泛型的方式封裝一個模板Dao來進行抽象簡化,可是這樣依然不是很方便,咱們須要針對每一個實體編寫一個繼承自泛型模板Dao的接口,再編寫該接口的實現。雖然一些基礎的數據訪問已經能夠獲得很好的複用,可是在代碼結構上針對每一個實體都會有一堆Dao的接口和實現。github

因爲模板Dao的實現,使得這些具體實體的Dao層已經變的很是「薄」,有一些具體實體的Dao實現可能徹底就是對模板Dao的簡單代理,而且每每這樣的實現類可能會出如今不少實體上。Spring Data JPA的出現正可讓這樣一個已經很「薄」的數據訪問層變成只是一層接口的編寫方式。好比,下面的例子:spring

public interface UserRepository extends JpaRepository<User, Long> {

    User findByName(String name);

    @Query("from User u where u.name=:name")
    User findUser(@Param("name") String name);

}

咱們只須要經過編寫一個繼承自JpaRepository的接口就能完成數據訪問,下面以一個具體實例來體驗Spring Data JPA給咱們帶來的強大功能。sql

使用步驟

因爲Spring Data JPA依賴於Hibernate。若是您對Hibernate有必定了解,下面內容能夠絕不費力的看懂並上手使用它。若是您仍是Hibernate新手,您能夠先按以下方式入門,再建議回頭學習一下Hibernate以幫助這部分的理解和進一步使用。數據庫

工程配置

pom.xml中添加相關依賴,加入如下內容:後端

<dependency
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

application.xml中配置:數據庫鏈接信息(如使用嵌入式數據庫則不須要)、自動建立表結構的設置,例如使用mysql的狀況以下:api

spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

spring.jpa.properties.hibernate.hbm2ddl.auto=create-drop

spring.jpa.properties.hibernate.hbm2ddl.auto是hibernate的配置屬性,其主要做用是:自動建立、更新、驗證數據庫表結構。該參數的幾種配置以下:bash

  • create:每次加載hibernate時都會刪除上一次的生成的表,而後根據你的model類再從新來生成新表,哪怕兩次沒有任何改變也要這樣執行,這就是致使數據庫表數據丟失的一個重要緣由。
  • create-drop:每次加載hibernate時根據model類生成表,可是sessionFactory一關閉,表就自動刪除。
  • update:最經常使用的屬性,第一次加載hibernate時根據model類會自動創建起表的結構(前提是先創建好數據庫),之後加載hibernate時根據model類自動更新表結構,即便表結構改變了但表中的行仍然存在不會刪除之前的行。要注意的是當部署到服務器後,表結構是不會被立刻創建起來的,是要等應用第一次運行起來後纔會。
  • validate:每次加載hibernate時,驗證建立數據庫表結構,只會和數據庫中的表進行比較,不會建立新表,可是會插入新值。

至此已經完成基礎配置,若是您有在Spring下整合使用過它的話,相信你已經感覺到Spring Boot的便利之處:JPA的傳統配置在persistence.xml文件中,可是這裏咱們不須要。固然,最好在構建項目時候按照以前提過的最佳實踐的工程結構來組織,這樣以確保各類配置都能被框架掃描到。

建立實體

建立一個User實體,包含id(主鍵)、name(姓名)、age(年齡)屬性,經過ORM框架其會被映射到數據庫表中,因爲配置了hibernate.hbm2ddl.auto,在應用啓動的時候框架會自動去數據庫中建立對應的表。

@Entity
@Data
@NoArgsConstructor
public class User {

    @Id
    @GeneratedValue
    private Long id;

    private String name;
    private Integer age;

    public User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
}
  • @Entity註解標識了User類是一個持久化的實體
  • @Data@NoArgsConstructor是Lombok中的註解。用來自動生成各參數的Set、Get函數以及不帶參數的構造函數。若是您對Lombok還不瞭解,能夠看看這篇文章:Java開發神器Lombok的使用與原理
  • @Id@GeneratedValue用來標識User對應對應數據庫表中的主鍵

注意:除了這些註解以外,還有不少用來精細化配置映射關係的註解,這裏不作具體介紹。後續會出專門一篇來介紹經常使用註解。讀者也能夠自行閱讀Hibernate的文檔來學習這些註解的詳細使用方法。

建立數據訪問接口

下面針對User實體建立對應的Repository接口實現對該實體的數據訪問,以下代碼:

public interface UserRepository extends JpaRepository<User, Long> {

    User findByName(String name);

    User findByNameAndAge(String name, Integer age);

    @Query("from User u where u.name=:name")
    User findUser(@Param("name") String name);

}

在Spring Data JPA中,只須要編寫相似上面這樣的接口就可實現數據訪問。再也不像咱們以往編寫了接口時候還須要本身編寫接口實現類,直接減小了咱們的文件清單。

下面對上面的UserRepository作一些解釋,該接口繼承自JpaRepository,經過查看JpaRepository接口的API文檔,能夠看到該接口自己已經實現了建立(save)、更新(save)、刪除(delete)、查詢(findAll、findOne)等基本操做的函數,所以對於這些基礎操做的數據訪問就不須要開發者再本身定義。

在咱們實際開發中,JpaRepository接口定義的接口每每還不夠或者性能不夠優化,咱們須要進一步實現更復雜一些的查詢或操做。因爲本文重點在Spring Boot中整合spring-data-jpa,在這裏先拋磚引玉簡單介紹一下spring-data-jpa中讓咱們興奮的功能,後續再單獨開篇講一下spring-data-jpa中的常見使用。

在上例中,咱們能夠看到下面兩個函數:

  • User findByName(String name)
  • User findByNameAndAge(String name, Integer age)

它們分別實現了按name查詢User實體和按name和age查詢User實體,能夠看到咱們這裏沒有任何類SQL語句就完成了兩個條件查詢方法。這就是Spring-data-jpa的一大特性:經過解析方法名建立查詢

除了經過解析方法名來建立查詢外,它也提供經過使用@Query 註解來建立查詢,您只須要編寫JPQL語句,並經過相似「:name」來映射@Param指定的參數,就像例子中的第三個findUser函數同樣。

Spring Data JPA的能力遠不止本文提到的這些,因爲本文主要以整合介紹爲主,對於Spring Data JPA的使用只是介紹了常見的使用方式。諸如@Modifying操做、分頁排序、原生SQL支持以及與Spring MVC的結合使用等等內容就不在本文中詳細展開,這裏先挖個坑,後續再補文章填坑,如您對這些感興趣能夠關注我博客或簡書,一樣歡迎你們留言交流想法。

單元測試

在完成了上面的數據訪問接口以後,按照慣例就是編寫對應的單元測試來驗證編寫的內容是否正確。這裏就很少作介紹,主要經過數據操做和查詢來反覆驗證操做的正確性。

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

    @Autowired
    private UserRepository userRepository;

    @Test
    public void test() throws Exception {

        // 建立10條記錄
        userRepository.save(new User("AAA", 10));
        userRepository.save(new User("BBB", 20));
        userRepository.save(new User("CCC", 30));
        userRepository.save(new User("DDD", 40));
        userRepository.save(new User("EEE", 50));
        userRepository.save(new User("FFF", 60));
        userRepository.save(new User("GGG", 70));
        userRepository.save(new User("HHH", 80));
        userRepository.save(new User("III", 90));
        userRepository.save(new User("JJJ", 100));

        // 測試findAll, 查詢全部記錄
        Assert.assertEquals(10, userRepository.findAll().size());

        // 測試findByName, 查詢姓名爲FFF的User
        Assert.assertEquals(60, userRepository.findByName("FFF").getAge().longValue());

        // 測試findUser, 查詢姓名爲FFF的User
        Assert.assertEquals(60, userRepository.findUser("FFF").getAge().longValue());

        // 測試findByNameAndAge, 查詢姓名爲FFF而且年齡爲60的User
        Assert.assertEquals("FFF", userRepository.findByNameAndAge("FFF", 60).getName());

        // 測試刪除姓名爲AAA的User
        userRepository.delete(userRepository.findByName("AAA"));

        // 測試findAll, 查詢全部記錄, 驗證上面的刪除是否成功
        Assert.assertEquals(9, userRepository.findAll().size());

    }
}

拓展閱讀:關於Spring Data

Spring Data JPA在Spring家族中其實是一個二級項目,它隸屬於Spring Data這個頂級項目。讀者能夠看一下關於這個項目的介紹,它除了涵蓋對關係型數據庫的抽象以外,其實還有不少對其餘數據存儲中間件的實現,好比咱們經常使用的Redis、MongoDB、Elasticsearch等。

若是再找幾個項目看一下它們的簡單示例,你會發現:不論你是要訪問什麼數據存儲產品,它們的編碼方式幾乎都是同樣的!這就是Spring Data這個項目充滿魅力的地方!經過對數據訪問操做的抽象來屏蔽細節,用不一樣子項目的方式去實現細節。讓開發者只須要學會使用Spring Data,就能方便快捷的學會對各類數據存儲的操做。因此,對於Spring Data,我是強烈推薦Java開發者們能夠學、甚至讀一下源碼的重要框架。雖然,目前來講不少大型互聯網公司並不會選擇它(性能考量居多,能真正用好它的人很少)做爲主要的開發框架,可是其背後的抽象思想是很是值得咱們學習的。而且,在作一些非高併發項目的時候,這簡直就是一個快捷開發神器,它能夠幫助咱們少寫很是多的代碼!

更多本系列免費教程連載「點擊進入彙總目錄」

代碼示例

本文的相關例子能夠查看下面倉庫中的chapter3-4目錄:

若是您以爲本文不錯,歡迎Star支持,您的關注是我堅持的動力!

歡迎關注個人公衆號:程序猿DD,得到獨家整理的學習資源和平常乾貨推送。
若是您對個人專題內容感興趣,也能夠關注個人博客: didispace.com
相關文章
相關標籤/搜索