天天看點

Spring-Data-JPA嘗鮮:快速搭建CRUD+分頁背景執行個體

前言:由于之前沒有接觸過Hibernate架構,但是最近看一些部落格深深被它的“效率”所吸引,是以這就來跟大家一起就着一個簡單的例子來嘗嘗Spring全家桶裡自帶的JPA的鮮

Spring-DATA-JPA 簡介

JPA(Java Persistence API)是Sun官方提出的Java持久化規範。它為Java開發人員提供了一種對象/關聯映射工具來管理Java應用中的關系資料。他的出現主要是為了簡化現有的持久化開發工作和整合ORM技術,結束現在Hibernate,TopLink,JDO等ORM架構各自為營的局面。值得注意的是,JPA是在充分吸收了現有Hibernate,TopLink,JDO等ORM架構的基礎上發展而來的,具有易于使用,伸縮性強等優點。從目前的開發社群的反應上看,JPA受到了極大的支援和贊揚,其中就包括了Spring與EJB3.0的開發團隊。

注意:JPA是一套規範,不是一套産品,那麼像Hibernate,TopLink,JDO他們是一套産品,如果說這些産品實作了這個JPA規範,那麼我們就可以叫他們為JPA的實作産品。

Spring Data JPA 是 Spring 基于 ORM 架構、JPA 規範的基礎上封裝的一套JPA應用架構,可使開發者用極簡的代碼即可實作對資料的通路和操作。它提供了包括增删改查等在内的常用功能,且易于擴充!學習并使用 Spring Data JPA 可以極大提高開發效率!(spring data jpa讓我們解脫了DAO層的操作,基本上所有CRUD都可以依賴于它來實作)

摘自:springboot(五):spring data jpa的使用——純潔的微笑

Hibernate 和 MyBatis 簡單對比

由于JPA底層幹活的仍然是Hibernate架構,而我們之前學習的隻有MyBatis相關的東西,是以在嘗鮮之前還是有必要簡單了解一下兩者的差別:

Hibernate的優勢:

  • Hibernate的DAO層開發比MyBatis簡單,Mybatis需要維護SQL和結果映射。
  • Hibernate對對象的維護和緩存要比MyBatis好,對增删改查的對象的維護要友善。
  • Hibernate資料庫移植性很好,MyBatis的資料庫移植性不好,不同的資料庫需要寫不同SQL。
  • Hibernate有更好的二級緩存機制,可以使用第三方緩存。MyBatis本身提供的緩存機制不佳。

MyBatis的優勢:

  • MyBatis可以進行更為細緻的SQL優化,可以減少查詢字段。
  • MyBatis容易掌握,而Hibernate門檻較高。

簡單總結:

  • MyBatis:小巧、友善、高效、簡單、直接、半自動化
  • Hibernate:強大、友善、高效、複雜、間接、全自動化
引用自:【持久化架構】Mybatis與Hibernate的詳細對比——高亮

CRUD + 分頁背景執行個體

下面我們來快速搭建一個使用Spring-DATA-JPA的CRUD+分頁背景執行個體,并且我們會直接使用到RESTful API(不熟悉的同學戳這裡)

第一步:建立SpringBoot項目

打開IDEA建立一個SpringBoot項目,不熟悉SpringBoot的同學請右轉:【傳送門】,然後在pom.xml中添加以下依賴:

<!-- mysql-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.21</version>
</dependency>

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

然後把application.properties弄成這個樣子:

#資料庫
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/testdb?useUnicode=true&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.properties.hibernate.hbm2ddl.auto=update
#顯示SQL語句
spring.jpa.show-sql=true
#不加下面這句則預設建立MyISAM引擎的資料庫
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
#自己重寫的配置類,預設使用utf8編碼
spring.jpa.properties.hibernate.dialect=com.wmyskxz.demo.config.MySQLConfig
           

spring.jpa.properties.hibernate.hbm2ddl.auto

是hibernate的配置屬性,其主要作用是:自動建立、更新、驗證資料庫表結構。該參數的幾種配置如下:

  • create:每次加載hibernate時都會删除上一次的生成的表,然後根據你的model類再重新來生成新表,哪怕兩次沒有任何改變也要這樣執行,這就是導緻資料庫表資料丢失的一個重要原因。
  • create-drop:每次加載hibernate時根據model類生成表,但是sessionFactory一關閉,表就自動删除。
  • update:最常用的屬性,第一次加載hibernate時根據model類會自動建立起表的結構(前提是先建立好資料庫),以後加載hibernate時根據model類自動更新表結構,即使表結構改變了但表中的行仍然存在不會删除以前的行。要注意的是當部署到伺服器後,表結構是不會被馬上建立起來的,是要等應用第一次運作起來後才會。
  • validate:每次加載hibernate時,驗證建立資料庫表結構,隻會和資料庫中的表進行比較,不會建立新表,但是會插入新值。

然後建立一個【config】包,建立一個【MySQLConfig】類(上面的

spring.jpa.properties.hibernate.dialect

屬性就要配置這裡的類全路徑):

package com.wmyskxz.demo.config;

import org.hibernate.dialect.MySQL5InnoDBDialect;

public class MySQLConfig extends MySQL5InnoDBDialect {
    @Override
    public String getTableTypeString() {
        return "ENGINE=InnoDB DEFAULT CHARSET=utf8";
    }
}
           

第二步:建立好需要的資料庫

打開SQL服務,建表語句也很簡單啦:

create database testdb;
           

第三步:建立實體類

實體類映射的實際上是資料庫表的結構,在适當的包目錄下(例如【entity】)下建立好實體類:

package com.wmyskxz.demo.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity // 表明這是個實體類
public class User {

    @Id // 表明這個屬性是主鍵
    @GeneratedValue // 自增長
    private long id;
    @Column(nullable = false, unique = true)    // 不允許為空,屬性唯一
    private String username;
    @Column(nullable = false)   // 不允許為空
    private String password;

    // getter and setter
}
           

第四步:DAO層

建立一個【repository】包,然後建立一個【UserRepository】接口,并繼承JpaRepository類:

package com.wmyskxz.demo.repository;

import com.wmyskxz.demo.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<User, Long> {
}
           

繼承JpaRepository需要傳入兩個參數,一個是實體類User一個是主鍵的類型Long,而凡是繼承了JpaRepository類的就會自動實作很多内置的方法,包括增删改查,以及使用預設支援的Pageable對象來進行分頁,預設的方法大緻如下:

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);
}
           

第五步:Controller層

建立【controller】包,建立一個【UserController】類,編寫簡單的增删改查代碼:

package com.wmyskxz.demo.controoler;

import com.wmyskxz.demo.entity.User;
import com.wmyskxz.demo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.web.bind.annotation.*;

import java.util.Optional;

@RestController // 表明這是一個Controller并傳回JSON格式
public class UserController {
    @Autowired
    private UserRepository userRepository;

    @GetMapping("/getOne")
    public Optional<User> getOneUserById(@RequestParam long id) {
        return userRepository.findById(id);
    }

    @GetMapping("/all")
    public Iterable<User> getAllUsers(@RequestParam(value = "page", defaultValue = "0") int page,
                                      @RequestParam(value = "size", defaultValue = "5") int size) {
        page = page < 0 ? 0 : page;// 如果page為負數則修改為0,防止在首頁點選上一頁發生錯誤
        Sort sort = new Sort(Sort.Direction.DESC, "id");// 按id倒叙排列
        return userRepository.findAll(new PageRequest(page, size, sort));
    }

    @PostMapping("/add")
    public String addUser(@RequestParam String username,
                          @RequestParam String password) {
        User user = new User();
        user.setUsername(username);
        user.setPassword(password);
        userRepository.save(user);// 注意這裡是save
        return "Saved";
    }

    @DeleteMapping("/delete")
    public String deleteUserById(@RequestParam long id) {
        userRepository.deleteById(id);
        return "Deleted";
    }

    @PutMapping("/update")
    public String updateUser(User user) {
//        User user = new User();
//        user.setId(id);
//        user.setUsername(username);
//        user.setPassword(password);
        userRepository.save(user);
        return "Updated";
    }
}
           

上面就直接使用

@Autowired

自動引入了繼承了JpaRepository的UserRepository接口,我們使用它預設的方法已經足夠完成我們的基礎功能了,值得一提的是我們的

getAllUsers(...)

方法,它往

findAll()

方法裡傳入了一個Pageable對象,這是Spring Data庫中定義的一個接口,是所有分頁相關資訊的一個抽象,通過該接口,我們可以得到和分頁相關的所有資訊(例如

pageNumber

pageSize

等),這樣Jpa就能夠通過Pageable參數來得到一個帶分頁資訊的Sql語句。

當然上面我們是通過自己建立了一個Pageable對象,Spring也支援直接擷取Pageable對象,可以把上面的

getAllUsers(...)

方法改寫成下面這樣:

@GetMapping("/all")
public Iterable<User> getAllUsers(@PageableDefault(value = 5, sort = {"id"}, direction = Sort.Direction.DESC) 
                                              Pageable pageable) {
    return userRepository.findAll(pageable);
}
           

預設從第0頁開始,也可以自己傳入一個page參數,跟上面的是一樣的。

第六步:運作項目

上面我們就快速搭建起來了一個基于Spring Boot和JPA的REST風格的背景增删改查執行個體,我們把項目跑起來,可以看到資料庫自動建立了一些表:

JPA幫我們建立的user表的建立SQL如下:

CREATE TABLE `user` (
  `id` bigint(20) NOT NULL,
  `password` varchar(255) NOT NULL,
  `username` varchar(255) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `UK_sb8bbouer5wak8vyiiy4pf2bx` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
           

使用REST測試工具測試

完全符合我們的要求,然後我們使用一些REST的測試工具,來測試上面的功能是否都能正确運作,比如我這裡使用的【Restlet Client】,在Chrome商店就可以下載下傳到。

/all

位址測試:

首先先來測試一下

http://localhost:8080/all

位址,由于現在資料庫還是空的,是以可以看到傳回如下:

{
    "content": [
    ],
    "pageable": {
        "sort": {
            "sorted": true,
            "unsorted": false,
            "empty": false
        },
        "offset": 0,
        "pageNumber": 0,
        "pageSize": 5,
        "unpaged": false,
        "paged": true
    },
    "totalElements": 0,
    "last": true,
    "totalPages": 0,
    "number": 0,
    "size": 5,
    "sort": {
        "sorted": true,
        "unsorted": false,
        "empty": false
    },
    "numberOfElements": 0,
    "first": true,
    "empty": true
}
           

添加使用者測試:

然後我們使用

http://localhost:8080/add?username=wmyskxz&password=123

位址,添加幾個類似的使用者資訊:

可以看到傳回正确的

Saved

資訊:

/getOne

我們就直接使用

http://localhost:8080/getOne?id=1

來擷取剛才添加的使用者,可以看到傳回正确的資料:

{
    "id": 1,
    "username": "wmyskxz",
    "password": "123"
}
           

修改使用者測試:

http://localhost:8080/update?id=1&username=wmyskxz&password=123456

來模拟進行使用者密碼的修改:

可以看到正确的更新資訊

Updated

,再次查詢使用者,也能看到正确的資料:

{
    "id": 1,
    "username": "wmyskxz",
    "password": "123456"
}
           

分頁測試:

我們使用添加功能為資料庫添加5條以上的資料,然後進行一次查詢

/all

,可以看到能夠按照

id

倒叙排列後傳回5條資料:

傳回的JSON資料如下:

{
    "content": [
        {
            "id": 10,
            "username": "wmyskxz8",
            "password": "123"
        },
        {
            "id": 9,
            "username": "wmyskxz7",
            "password": "123"
        },
        {
            "id": 8,
            "username": "wmyskxz6",
            "password": "123"
        },
        {
            "id": 7,
            "username": "wmyskxz5",
            "password": "123"
        },
        {
            "id": 6,
            "username": "wmyskxz4",
            "password": "123"
        }
    ],
    "pageable": {
        "sort": {
            "sorted": true,
            "unsorted": false,
            "empty": false
        },
        "offset": 0,
        "pageNumber": 0,
        "pageSize": 5,
        "unpaged": false,
        "paged": true
    },
    "totalElements": 9,
    "last": false,
    "totalPages": 2,
    "number": 0,
    "size": 5,
    "sort": {
        "sorted": true,
        "unsorted": false,
        "empty": false
    },
    "numberOfElements": 5,
    "first": true,
    "empty": false
}
           

删除使用者測試:

使用位址

http://localhost:8080/delete?id=1

來删除ID為1的使用者:

能正确看到

Deleted

資訊,并檢視資料能夠看到資料已經被删除了。

以上,我們就快速搭建好了一個CRUD+分頁的背景執行個體,還用了比較流行的RESTful風格,粗略的感受了一下JPA的友善,還是挺爽的..沒有複雜的Mapper檔案,不用自動生成實體,甚至不用管SQL,隻需要專注在邏輯上就行了,其實簡單使用的話以上的東西也能應付一些常見的場景了,後期再深入了解了解吧!

參考資料:

springboot(五):spring data jpa的使用——純潔的微笑

springboot(十五):springboot+jpa+thymeleaf增删改查示例——純潔的微笑

Spring Boot中使用Spring-data-jpa讓資料通路更簡單、更優雅——程式猿DD

歡迎轉載,轉載請注明出處!

簡書ID:@我沒有三顆心髒

github:wmyskxz

歡迎關注公衆微信号:wmyskxz

分享自己的學習 & 學習資料 & 生活

想要交流的朋友也可以加qq群:3382693

繼續閱讀