Spring-Data-JPA嘗鮮:快速搭建CRUD+分頁(yè)后臺(tái)實(shí)例

前言:由于之前沒有接觸過Hibernate框架,但是最近看一些博客深深被它的“效率”所吸引,所以這就來跟大家一起就著一個(gè)簡(jiǎn)單的例子來嘗嘗Spring全家桶里自帶的JPA的鮮

Spring-DATA-JPA 簡(jiǎn)介

JPA(Java Persistence API)是Sun官方提出的Java持久化規(guī)范。它為Java開發(fā)人員提供了一種對(duì)象/關(guān)聯(lián)映射工具來管理Java應(yīng)用中的關(guān)系數(shù)據(jù)。他的出現(xiàn)主要是為了簡(jiǎn)化現(xiàn)有的持久化開發(fā)工作和整合ORM技術(shù),結(jié)束現(xiàn)在Hibernate,TopLink,JDO等ORM框架各自為營(yíng)的局面。值得注意的是,JPA是在充分吸收了現(xiàn)有Hibernate,TopLink,JDO等ORM框架的基礎(chǔ)上發(fā)展而來的,具有易于使用,伸縮性強(qiáng)等優(yōu)點(diǎn)。從目前的開發(fā)社區(qū)的反應(yīng)上看,JPA受到了極大的支持和贊揚(yáng),其中就包括了Spring與EJB3.0的開發(fā)團(tuán)隊(duì)。

注意:JPA是一套規(guī)范,不是一套產(chǎn)品,那么像Hibernate,TopLink,JDO他們是一套產(chǎn)品,如果說這些產(chǎn)品實(shí)現(xiàn)了這個(gè)JPA規(guī)范,那么我們就可以叫他們?yōu)镴PA的實(shí)現(xiàn)產(chǎn)品。

Spring Data JPA 是 Spring 基于 ORM 框架、JPA 規(guī)范的基礎(chǔ)上封裝的一套JPA應(yīng)用框架,可使開發(fā)者用極簡(jiǎn)的代碼即可實(shí)現(xiàn)對(duì)數(shù)據(jù)的訪問和操作。它提供了包括增刪改查等在內(nèi)的常用功能,且易于擴(kuò)展!學(xué)習(xí)并使用 Spring Data JPA 可以極大提高開發(fā)效率!(spring data jpa讓我們解脫了DAO層的操作,基本上所有CRUD都可以依賴于它來實(shí)現(xiàn)

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

Hibernate 和 MyBatis 簡(jiǎn)單對(duì)比

由于JPA底層干活的仍然是Hibernate框架,而我們之前學(xué)習(xí)的只有MyBatis相關(guān)的東西,所以在嘗鮮之前還是有必要簡(jiǎn)單了解一下兩者的區(qū)別:

Hibernate的優(yōu)勢(shì):

  • Hibernate的DAO層開發(fā)比MyBatis簡(jiǎn)單,Mybatis需要維護(hù)SQL和結(jié)果映射。
  • Hibernate對(duì)對(duì)象的維護(hù)和緩存要比MyBatis好,對(duì)增刪改查的對(duì)象的維護(hù)要方便。
  • Hibernate數(shù)據(jù)庫(kù)移植性很好,MyBatis的數(shù)據(jù)庫(kù)移植性不好,不同的數(shù)據(jù)庫(kù)需要寫不同SQL。
  • Hibernate有更好的二級(jí)緩存機(jī)制,可以使用第三方緩存。MyBatis本身提供的緩存機(jī)制不佳。

MyBatis的優(yōu)勢(shì):

  • MyBatis可以進(jìn)行更為細(xì)致的SQL優(yōu)化,可以減少查詢字段。
  • MyBatis容易掌握,而Hibernate門檻較高。

簡(jiǎn)單總結(jié):

  • MyBatis:小巧、方便、高效、簡(jiǎn)單、直接、半自動(dòng)化
  • Hibernate:強(qiáng)大、方便、高效、復(fù)雜、間接、全自動(dòng)化

引用自:【持久化框架】Mybatis與Hibernate的詳細(xì)對(duì)比——高亮

CRUD + 分頁(yè)后臺(tái)實(shí)例

下面我們來快速搭建一個(gè)使用Spring-DATA-JPA的CRUD+分頁(yè)后臺(tái)實(shí)例,并且我們會(huì)直接使用到RESTful API(不熟悉的同學(xué)戳這里

第一步:新建SpringBoot項(xiàng)目

打開IDEA新建一個(gè)SpringBoot項(xiàng)目,不熟悉SpringBoot的同學(xué)請(qǐng)右轉(zhuǎn):【傳送門】,然后在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>

然后把a(bǔ)pplication.properties弄成這個(gè)樣子:

#數(shù)據(jù)庫(kù)
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
#不加下面這句則默認(rèn)創(chuàng)建MyISAM引擎的數(shù)據(jù)庫(kù)
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
#自己重寫的配置類,默認(rèn)使用utf8編碼
spring.jpa.properties.hibernate.dialect=com.wmyskxz.demo.config.MySQLConfig

spring.jpa.properties.hibernate.hbm2ddl.auto是hibernate的配置屬性,其主要作用是:自動(dòng)創(chuàng)建、更新、驗(yàn)證數(shù)據(jù)庫(kù)表結(jié)構(gòu)。該參數(shù)的幾種配置如下:

  • create:每次加載hibernate時(shí)都會(huì)刪除上一次的生成的表,然后根據(jù)你的model類再重新來生成新表,哪怕兩次沒有任何改變也要這樣執(zhí)行,這就是導(dǎo)致數(shù)據(jù)庫(kù)表數(shù)據(jù)丟失的一個(gè)重要原因。
  • create-drop:每次加載hibernate時(shí)根據(jù)model類生成表,但是sessionFactory一關(guān)閉,表就自動(dòng)刪除。
  • update:最常用的屬性,第一次加載hibernate時(shí)根據(jù)model類會(huì)自動(dòng)建立起表的結(jié)構(gòu)(前提是先建立好數(shù)據(jù)庫(kù)),以后加載hibernate時(shí)根據(jù)model類自動(dòng)更新表結(jié)構(gòu),即使表結(jié)構(gòu)改變了但表中的行仍然存在不會(huì)刪除以前的行。要注意的是當(dāng)部署到服務(wù)器后,表結(jié)構(gòu)是不會(huì)被馬上建立起來的,是要等應(yīng)用第一次運(yùn)行起來后才會(huì)。
  • validate:每次加載hibernate時(shí),驗(yàn)證創(chuàng)建數(shù)據(jù)庫(kù)表結(jié)構(gòu),只會(huì)和數(shù)據(jù)庫(kù)中的表進(jìn)行比較,不會(huì)創(chuàng)建新表,但是會(huì)插入新值。

然后新建一個(gè)【config】包,創(chuàng)建一個(gè)【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";
    }
}

第二步:創(chuàng)建好需要的數(shù)據(jù)庫(kù)

打開SQL服務(wù),建表語句也很簡(jiǎn)單啦:

create database testdb;

第三步:創(chuàng)建實(shí)體類

實(shí)體類映射的實(shí)際上是數(shù)據(jù)庫(kù)表的結(jié)構(gòu),在適當(dāng)?shù)陌夸浵拢ɡ纭緀ntity】)下創(chuàng)建好實(shí)體類:

package com.wmyskxz.demo.entity;

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

@Entity // 表明這是個(gè)實(shí)體類
public class User {

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

    // getter and setter
}

第四步:DAO層

新建一個(gè)【repository】包,然后新建一個(gè)【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需要傳入兩個(gè)參數(shù),一個(gè)是實(shí)體類User一個(gè)是主鍵的類型Long,而凡是繼承了JpaRepository類的就會(huì)自動(dòng)實(shí)現(xiàn)很多內(nèi)置的方法,包括增刪改查,以及使用默認(rèn)支持的Pageable對(duì)象來進(jìn)行分頁(yè),默認(rèn)的方法大致如下:

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】包,新建一個(gè)【UserController】類,編寫簡(jiǎn)單的增刪改查代碼:

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 // 表明這是一個(gè)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為負(fù)數(shù)則修改為0,防止在首頁(yè)點(diǎn)擊上一頁(yè)發(fā)生錯(cuò)誤
        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自動(dòng)引入了繼承了JpaRepository的UserRepository接口,我們使用它默認(rèn)的方法已經(jīng)足夠完成我們的基礎(chǔ)功能了,值得一提的是我們的getAllUsers(...)方法,它往findAll()方法里傳入了一個(gè)Pageable對(duì)象,這是Spring Data庫(kù)中定義的一個(gè)接口,是所有分頁(yè)相關(guān)信息的一個(gè)抽象,通過該接口,我們可以得到和分頁(yè)相關(guān)的所有信息(例如pageNumber、pageSize等),這樣Jpa就能夠通過Pageable參數(shù)來得到一個(gè)帶分頁(yè)信息的Sql語句。

當(dāng)然上面我們是通過自己創(chuàng)建了一個(gè)Pageable對(duì)象,Spring也支持直接獲取Pageable對(duì)象,可以把上面的getAllUsers(...)方法改寫成下面這樣:

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

默認(rèn)從第0頁(yè)開始,也可以自己傳入一個(gè)page參數(shù),跟上面的是一樣的。

第六步:運(yùn)行項(xiàng)目

上面我們就快速搭建起來了一個(gè)基于Spring Boot和JPA的REST風(fēng)格的后臺(tái)增刪改查實(shí)例,我們把項(xiàng)目跑起來,可以看到數(shù)據(jù)庫(kù)自動(dòng)創(chuàng)建了一些表:

JPA幫我們創(chuàng)建的user表的創(chuàng)建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測(cè)試工具測(cè)試

完全符合我們的要求,然后我們使用一些REST的測(cè)試工具,來測(cè)試上面的功能是否都能正確運(yùn)行,比如我這里使用的【Restlet Client】,在Chrome商店就可以下載到。

/all地址測(cè)試:

首先先來測(cè)試一下http://localhost:8080/all地址,由于現(xiàn)在數(shù)據(jù)庫(kù)還是空的,所以可以看到返回如下:

{
    "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
}

添加用戶測(cè)試:

然后我們使用http://localhost:8080/add?username=wmyskxz&password=123地址,添加幾個(gè)類似的用戶信息:

可以看到返回正確的Saved信息:

/getOne地址測(cè)試:

我們就直接使用http://localhost:8080/getOne?id=1來獲取剛才添加的用戶,可以看到返回正確的數(shù)據(jù):

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

修改用戶測(cè)試:

然后我們使用http://localhost:8080/update?id=1&username=wmyskxz&password=123456來模擬進(jìn)行用戶密碼的修改:

可以看到正確的更新信息Updated,再次查詢用戶,也能看到正確的數(shù)據(jù):

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

分頁(yè)測(cè)試:

我們使用添加功能為數(shù)據(jù)庫(kù)添加5條以上的數(shù)據(jù),然后進(jìn)行一次查詢/all,可以看到能夠按照id倒敘排列后返回5條數(shù)據(jù):

數(shù)據(jù)庫(kù)的情況

返回的JSON數(shù)據(jù)如下:

{
    "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
}

刪除用戶測(cè)試:

使用地址http://localhost:8080/delete?id=1來刪除ID為1的用戶:

能正確看到Deleted信息,并查看數(shù)據(jù)能夠看到數(shù)據(jù)已經(jīng)被刪除了。


以上,我們就快速搭建好了一個(gè)CRUD+分頁(yè)的后臺(tái)實(shí)例,還用了比較流行的RESTful風(fēng)格,粗略的感受了一下JPA的方便,還是挺爽的..沒有復(fù)雜的Mapper文件,不用自動(dòng)生成實(shí)體,甚至不用管SQL,只需要專注在邏輯上就行了,其實(shí)簡(jiǎn)單使用的話以上的東西也能應(yīng)付一些常見的場(chǎng)景了,后期再深入了解了解吧!

參考資料:
springboot(五):spring data jpa的使用——純潔的微笑
springboot(十五):springboot+jpa+thymeleaf增刪改查示例——純潔的微笑
Spring Boot中使用Spring-data-jpa讓數(shù)據(jù)訪問更簡(jiǎn)單、更優(yōu)雅——程序猿DD

歡迎轉(zhuǎn)載,轉(zhuǎn)載請(qǐng)注明出處!
簡(jiǎn)書ID:@我沒有三顆心臟
github:wmyskxz
歡迎關(guān)注公眾微信號(hào):wmyskxz
分享自己的學(xué)習(xí) & 學(xué)習(xí)資料 & 生活
想要交流的朋友也可以加qq群:3382693

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容