SpringData JPA - 1: 基本使用

1.建立項目

這裏使用 IDEA 來進行工程的建立,使用了 Gradle 對整個項目進行管理,具體的過程以下:java

1.1 建立新項目

點擊 Create New Project -> Spring Initializr , 以後選擇默認的 Initalizr Service URL,而後填寫項目的信息,以下所示:mysql

image-20190714112518289

image-20190714110737655

這裏採用了 Gradle 來進行項目的管理,而後就是對 SpringBoot 進行選擇,以下:web

image-20190714111115989

依賴組件 含義
Spring Boot DevTools 熱部署插件,能夠在項目運行時自動替換 class,生產環境禁用
Lombok 簡化 Java 代碼
Spring Web Starter Spring Web 開發的起步依賴
Spring Data JPA 持久層 ORM 框架
MySQL Driver MySQL 驅動

作完這一步以後就是選擇項目的存儲位置。所有完成以後項目的結構以下所示:spring

../blog/
├── blog.iml
├── build.gradle
├── .gradle
└── src
    ├── main
    │   ├── java
    │   └── resources
    └── test
        └── java
複製代碼

1.2 對項目進行一些小小的改進

這裏主要是對 Gradle 的配置作出小小的改動,在 repositories 節點下增長新的倉庫配置,具體以下:sql

plugins {
    id 'org.springframework.boot' version '2.1.6.RELEASE'
    id 'java'
}

apply plugin: 'io.spring.dependency-management'

group = 'hk.mars'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

configurations {
    developmentOnly
    runtimeClasspath {
        extendsFrom developmentOnly
    }
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    maven { url "https://maven.aliyun.com/repository/spring-plugin" }
    maven { url "https://maven.aliyun.com/repository/spring" }
    maven { url "https://repo.spring.io/libs-release" }
    maven { url "https://repo.spring.io/milestone" }
    mavenLocal()
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    compileOnly 'org.projectlombok:lombok'
    developmentOnly 'org.springframework.boot:spring-boot-devtools'
    runtimeOnly 'mysql:mysql-connector-java'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
複製代碼

這裏須要注意的地方是 Gradle 在初始化的時候速度略慢,甚至可能會失敗,主要是 Gradle 在初始化時會下載一個 .zip 文件,受到國內網絡大環境的影響,該網站響應較慢,不過對此網絡上有不少解決的辦法。shell

同時國內訪問 Maven 中央倉庫也受到網絡大環境的影響比較慢,這裏設置了阿里雲提供的 Maven 倉庫,能夠提升訪問速度(位置靠上的倉庫優先訪問)。數據庫

2. 數據庫的配置

SpringData JPA 能夠在項目啓動的時候會自動的數據庫中的表,所以沒必要提早初始化數據庫,只需在配置文件中配置數據源便可,固然這種自動生成表的方法是不容許在生產環境中使用的,具體的配置以下:json

application.propertiesapi

spring.datasource.url=jdbc:mysql://x.x.x.x:3306/test?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
spring.datasource.username=root
spring.datasource.password=xxxxxx
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

spring.jpa.properties.hibernate.hbm2ddl.auto=create
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
複製代碼

這裏須要進一步說明的是 spring.jpa.properties.hibernate.hbm2ddl.auto 的含義:網絡

前文提到過 SpringBoot JPA 在項目啓動的時候會自動的在數據庫中生成相應的表,該項配置就是設置自動生成的策略,一個有四個選項,分別以下:

生成策略 含義
create 每次加載 hibernate 時都會從新生成表
create-drop 每次加載 hibernate 時都會從新生成表,在 sessionFactory 關閉時刪除表結構
update 第一次加載 hibernate 時都會從新生成表,以後每次加載 hibernate 會更新表結構
validate 驗證模型與數據庫表結構是否匹配
none 若是不配置該項,默認不對數據庫作任何改動

3.code

3.1 BaseEntity.java: 模型超類

在設計數據庫的時候,不一樣的表可能具備相同的字段,用來存儲一些通用的信息,例如每一張表中都會有 id 字段做爲主鍵,同時存在 create_timeupdate_time 用來存儲表中每一行數據的通用信息,這時候就須要定義一個超類來表示這些信息。 這一功能可使用 @MappedSuperclass 註解實現,具體實現以下的 BaseEntity.java

package hk.mars.user;

import lombok.Data;
import org.hibernate.annotations.GenericGenerator;

import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import java.sql.Timestamp;

/** * @author qingke.hk@gmmail.com */
@MappedSuperclass
@Data
public abstract class BaseEntity {
    @Id
    @GeneratedValue(generator = "id-uuid")
    @GenericGenerator(name = "id-uuid", strategy = "uuid")
    private String id;

    @Column(name = "create_time")
    private Timestamp createTime;

    @Column(name = "update_time")
    private Timestamp updateTime;
}
複製代碼

@Data 註解是 Lombok 提供的註解,能夠簡化 Java 代碼。

@Id 註解通常是用在屬性上,表示該屬性對應的數據庫表字段爲主鍵類型,同時可使用 @GeneratedValue@GenericGenerator 指定主鍵生成策略,在上文使用了內置的 uuid 的生成策略,但 hibernate 還提供了替他的策略,同時還支持自定義的主鍵生成策略。

@MappedSuperclass 註解是 SpringData JPA 提供的註解,主要是用於用於類上,表示該類是全部的 Entity 的父類,固然你要在你的 Entity 中顯示的繼承,具體的用法能夠繼續向下看。

  • todo: 寫一篇 hibernate 的主鍵生成策略相關的博客
  • todo: 寫一篇 Lombok 的博客

3.2 UserEntity.java: 用戶模型

首先定義用戶角色的枚舉:

package hk.mars.user;

import lombok.Getter;

/** * @author qingke.hk@gmmail.com */
public enum UserRole {
    /** 管理員角色 */
    ADMIN("admin"),

    /** 用戶角色 */
    USER("user");
    @Getter
    private String role;

    UserRole(String role) {
        this.role = role;
    }
}
複製代碼

@Getter 是有 Lombok 提供的,能夠在編譯的時候自動生成 Getter 方法。

接下來咱們將在繼承 BaseEntity 的基礎之上定義用戶模型,以下 UserEntity.java 所示:

package hk.mars.commons;

import hk.mars.commons.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.format.annotation.DateTimeFormat;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Table;
import java.util.Date;

/** * @author qingke.hk@gmmail.com */
@Data
@Entity
@Table(name = "user")
@EqualsAndHashCode(callSuper = false)
@SuppressWarnings("WeakerAccess")
public class UserEntity extends BaseEntity {

    private String name;

    @Column(name = "first_name")
    private String firstName;

    @Column(name = "last_name")
    private String lastName;

    @DateTimeFormat(pattern = "yyyy-mm-dd")
    private Date birthday;

    private String email;

    private String password;

    @Enumerated(EnumType.STRING)
    private UserRole role;
}
複製代碼

@Data@EqualsAndHashCode 註解是有 Lombok 提供,用來簡化 Java 代碼。

@Entity 註解是最主要的一個註解,表示這個類表示數據庫中的一個表。

@Table 註解主要是聲明表的配置,例如上文能夠設置表名,固然還能夠設置 數據庫的名稱、設置索引等等。

@Column 註解主要是對數據庫表字段與 Java 類屬性之間的關係進行配置,上文僅僅是設置了字段名稱,同時還能夠設施是否惟1、長度限制、是否可控等等。

@Enumerated 表示屬性是一個註解類型,可是在存儲的時候字符串形式進行存儲。

@DateTimeFormat 註解是有 Spring 提供的,在這裏由於 birthday 是時間類型,而調用 RESTful 接口的時候是以 JSON 形式,若是不走任何處理是沒法將 JSON 字符串轉化爲時間類型的,所以咱們在這裏使用了 @DataTimeFormat 註解。

  • todo: 後面寫一些關於 SpringData JPA 的更多的用法。

3.3 UserRepository.java: 數據庫操做

SpringBoot JPA 能夠將接口中的方法名解析爲相應的 SQL 語句,但同時針對一些常見的 CRUD 操做提供了一個 CrudRepository 的接口,經過繼承能夠實現一些常見的 CRUD 操做,固然若是 CrudRepository 提供的操做不能知足業務的需求能夠編寫新的接口,以下,定義了一個新的分頁查詢的接口:

UserRepository.java

package hk.mars.user;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.CrudRepository;

/** * @author qingke.hk@gmmail.com */
public interface UserRepository extends CrudRepository<UserEntity, String> {
    /** * 查詢全部的用戶信息 - 分頁 * * @param pageConf 分頁參數 * @return 用戶信息 */
    Page<UserEntity> findAll(Pageable pageConf);
}
複製代碼

3.4 此時項目的結構

當你已經完成上述的操做後,此時的項目的結構以下所示:

../blog/
├── blog.iml
├── build.gradle
└── src
    ├── main
    │   ├── java
    │   │   └── hk.mars
    │   │       ├── Application.java
    │   │       ├── commons
    │   │       │   ├── BaseEntity.java
    │   │       │   └── response
    │   │       │       ├── ResultResponse.java
    │   │       │       └── ResultResponseBuilder.java
    │   │       ├── config
    │   │       └── user
    │   │           ├── UserController.java
    │   │           ├── UserEntity.java
    │   │           ├── UserRepository.java
    │   │           ├── UserRole.java
    │   │           └── UserService.java
    │   └── resources
    │       └── application.properties
    └── test
        └── java
            └── hk
                └── mars
複製代碼

固然此時的項目代碼多了一些別的東西,不過都與 SpringData JPA 無關,在下載源碼以後你們能夠自行查看便可。

4. 測試

4.1 Controller 與 Service

接下來就能夠編寫 Service 和 Controller 來對以前的代碼進行測試:

UserService.java:

package hk.mars.user;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import java.util.List;

/** * @author qingke.hk@gmmail.com */
@SuppressWarnings("WeakerAccess")
@Service
public class UserService {

    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public List<UserEntity> getUserList(Pageable pageConf) {
        Page<UserEntity> userInPage = userRepository.findAll(pageConf);
        return userInPage.getContent();
    }

    public void saveUser(UserEntity userEntity) {
        String password = userEntity.getPassword();
        userEntity.setPassword(DigestUtils.md5DigestAsHex(password.getBytes()));

        this.userRepository.save(userEntity);
    }
}
複製代碼

UserController.java:

package hk.mars.user;

import hk.mars.commons.response.ResultResponse;
import hk.mars.commons.response.ResultResponseBuilder;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/** * @author qingke.hk@gmmail.com */
@RestController
public class UserController {
    private static final int DEFAULT_PAGE_NUMS = 10;

    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping("/user")
    public ResultResponse<List<UserEntity>> getUsers(
            @RequestParam(value = "page-num", defaultValue = "0") Integer pageNum,
            @RequestParam(value = "page-size", defaultValue = "10") Integer pageSize) {

        Pageable pageRequest = PageRequest.of(pageNum, pageSize == null ? DEFAULT_PAGE_NUMS : pageSize);
        List<UserEntity> userList = this.userService.getUserList(pageRequest);
        return ResultResponseBuilder.buildSuccess(userList);
    }

    @PostMapping("/user")
    public ResultResponse saveUser(UserEntity user) {
        if (user == null) {
            return ResultResponseBuilder.buildError("use information is null");
        }

        this.userService.saveUser(user);
        return ResultResponseBuilder.buildSuccess();
    }
}
複製代碼

4.2 啓動項目

而後就是啓動項目,開始測試:

image-20190714124939870

同時在啓動的過程當中經過觀察日誌輸出能夠看到 SpringData JPA 會在項目啓動的過程當中刪除掉數據庫的舊錶,而後從新建表:

image-20190714125239702

4.3 進行測試

對於 RESTful 風格的接口能夠經過 IDEA 自帶的功能進行測試,能夠新建一個 rest-api.http 文件,經過該文件就能夠直接在 IDEA 中進行測試,文件內容以下:

POST http://localhost:8080/user?name=Tom&password=123456&birthday=2019-07-11&email=test@test.test
Accept: */*
Cache-Control: no-cache

###
GET http://localhost:8080/user?page-num=0&page-size=10
Accept: */*
Cache-Control: no-cache
複製代碼

經過點擊左側邊欄上的三角標誌就能夠啓動一次測試,同時 IDEA 會將測試結果保存爲一個 JSON 文件,這樣就能夠查看測試的歷史,查看的方法是在下圖的 2019-07-14T125418.200.json 使用快捷鍵 Command + B 就能夠直接轉跳到文件中去。

image-20190714125817691

點擊測試以後會顯示以下,能夠看到測試的返回結果是成功了,而後咱們去查看數據庫中的信息,能夠看到數據庫中已經插入了一條數據:

image-20190714125712905

image-20190714130245023

而後運行第二個測試,看分頁查詢是否成功:

image-20190714130409708

5. 總結

至此本片博客就基本上完成了,咱們已經能夠簡單的使用 SpringData JPA 進行操做。

  • 上傳項目至 Github
相關文章
相關標籤/搜索