SpringBoot系列教程JPA之新增記錄使用姿勢
上一篇文章介紹瞭如何快速的搭建一個JPA的專案環境,並給出了一個簡單的演示demo,接下來我們開始業務教程,也就是我們常說的CURD,接下來進入第一篇,如何新增資料
通過本篇文章,你可以get到以下技能點
- POJO物件如何與表關聯
- 如何向DB中新增單條記錄
- 如何批量向DB中新增記錄
- save 與 saveAndFlush的區別
<!-- more -->
I. 環境準備
實際開始之前,需要先走一些必要的操作,如安裝測試使用mysql,建立SpringBoot專案工程,設定好配置資訊等,關於搭建專案的詳情可以參考前一篇文章
下面簡單的看一下演示新增記錄的過程中,需要的配置
1. 表準備
沿用前一篇的表,結構如下
CREATE TABLE `money` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(20) NOT NULL DEFAULT '' COMMENT '使用者名稱', `money` int(26) NOT NULL DEFAULT '0' COMMENT '錢', `is_deleted` tinyint(1) NOT NULL DEFAULT '0', `create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '建立時間', `update_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時間', PRIMARY KEY (`id`), KEY `name` (`name`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
2. 專案配置
配置資訊,與之前有一點點區別,我們新增了更詳細的日誌列印;本篇主要目標集中在新增記錄的使用姿勢,對於配置說明,後面單獨進行說明
## DataSource spring.datasource.url=jdbc:mysql://127.0.0.1:3306/story?useUnicode=true&characterEncoding=UTF-8&useSSL=false spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.username=root spring.datasource.password= ## jpa相關配置 spring.jpa.database=MYSQL spring.jpa.hibernate.ddl-auto=none spring.jpa.show-sql=true spring.jackson.serialization.indent_output=true spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
II. Insert使用教程
在開始之前,先宣告一下,因為個人實際專案中並沒有使用到JPA,對JPA的原則和hibernate的一些特性瞭解的也不多,目前處於學習探索階段,主要是介紹下使用姿勢,下面的東西都是經過測試得出,有些地方描述可能與規範不太一樣,或者有些差錯,請發現的大佬指正
接下來我們進入正題,如何通過JPA實現我們常見的Insert功能
1. POJO與表關聯
首先第一步就是將POJO物件與表關聯起來,這樣就可以直接通過java的操作方式來實現資料庫的操作了;
我們直接建立一個MoneyPo物件,包含上面表中的幾個欄位
@Data
public class MoneyPO {
private Integer id;
private String name;
private Long money;
private Byte isDeleted;
private Timestamp createAt;
private Timestamp updateAt;
}
自然而然地,我們就有幾個問題了
- 這個POJO怎麼告訴框架它是和表Money繫結的呢?
- Java中變數命令推薦駝峰結構,那麼
isDeleted
又如何與表中的is_deleted
關聯呢? - POJO中成員變數的型別如何與表中的保持一致呢,如果不一致會怎樣呢?
針對上面的問題,一個一個來說明
對hibernate熟悉的同學,可能知道我可以通過xml配置的方式,來關聯POJO與資料庫表(當然mybatis也是這麼玩的),友情連結一下hibernate的官方說明教程;我們使用SpringBoot,當然是選擇註解的方式了,下面是通過註解的方式改造之後的DO物件
package com.git.hui.boot.jpa.entity;
import lombok.Data;
import org.springframework.data.annotation.CreatedDate;
import javax.persistence.*;
import java.sql.Timestamp;
/**
* Created by @author yihui in 21:01 19/6/10.
*/
@Data
@Entity(name="money")
public class MoneyPO {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Integer id;
@Column(name = "name")
private String name;
@Column(name = "money")
private Long money;
@Column(name = "is_deleted")
private Byte isDeleted;
@Column(name = "create_at")
@CreatedDate
private Timestamp createAt;
@Column(name = "update_at")
@CreatedDate
private Timestamp updateAt;
}
有幾個有意思的地方,需要我們注意
a. entity註解
@Entity
這個註解比較重要,用於宣告這個POJO是一個與資料庫中叫做 money
的表關聯的物件;
@Entity
註解有一個引數name,用於指定表名,如果不主動指定時,預設用類名,即上面如果不指定那麼,那麼預設與表moneypo
繫結
另外一個常見的方式是在類上添加註解 @Table
,然後指定表名,也是可以的
@Data
@Entity
@Table(name = "money")
public class MoneyPO {
}
b. 主鍵指定
我們可以看到id上面有三個註解,我們先看下前面兩個
@Id
顧名思義,用來表明這傢伙是主鍵,比較重要,需要特殊關照@GeneratedValue
設定初始值,談到主鍵,我們一般會和”自增“這個一起說,所以你經常會看到的取值為strategy = GenerationType.IDENTITY
(由資料庫自動生成)
這個註解主要提供了四種方式,分別說明如下
取值 | 說明 |
---|---|
GenerationType.TABLE | 使用一個特定的資料庫表格來儲存主鍵 |
GenerationType.SEQUENCE | 根據底層資料庫的序列來生成主鍵,條件是資料庫支援序列 |
GenerationType.IDENTITY | 主鍵由資料庫自動生成(主要是自動增長型) |
GenerationType.AUTO | 主鍵由程式控制 |
關於這幾種使用姿勢,這裡不詳細展開了,有興趣的可以可以看一下這博文: @GeneratedValue
c. Column註解
這個註解就是用來解決我們pojo成員名和資料庫列名不一致的問題的,這個註解內部的屬性也不少,相對容易理解,後面會單開一章來記錄這些常用註解的說明查閱
d. CreateDate註解
這個註解和前面不一樣的是它並非來自jpa-api
包,而是spring-data-common
包中提供的,表示會根據當前時間建立一個時間戳物件
e. 其他
到這裡這個POJO已經建立完畢,後續的表中新增記錄也可以直接使用它了,但是還有幾個問題是沒有明確答案的,先提出來,期待後文可以給出回答
- POJO屬性的型別與表中型別
- mysql表中列可以有預設值,這個在POJO中怎麼體現
- 一個表包含另一個表的主鍵時(主鍵關聯,外來鍵)等特殊的情況,POJO中有體現麼?
2. Repository API宣告
jpa非常有意思的一點就是你只需要建立一個介面就可以實現db操作,就這麼神奇,可惜本文裡面見不到太多神奇的用法,這塊放在查詢篇來見證奇蹟
我們定義的API需要繼承自org.springframework.data.repository.CrudRepository
,如下
package com.git.hui.boot.jpa.repository;
import com.git.hui.boot.jpa.entity.MoneyPO;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.CrudRepository;
/**
* 新增資料
* Created by @author yihui in 11:00 19/6/12.
*/
public interface MoneyCreateRepository extends CrudRepository<MoneyPO, Integer> {
}
好的,到這裡就可以直接新增資料了 (感覺什麼都沒幹,你居然告訴我可以插入資料???)
3. 使用姿勢
a. 基礎使用case
常規的使用姿勢,無非單個插入和批量插入,我們先來看一下常規操作
@Component
public class JpaInsertDemo {
@Autowired
private MoneyCreateRepository moneyCreateRepository;
public void testInsert() {
addOne();
addMutl();
}
private void addOne() {
// 單個新增
MoneyPO moneyPO = new MoneyPO();
moneyPO.setName("jpa 一灰灰");
moneyPO.setMoney(1000L);
moneyPO.setIsDeleted((byte) 0x00);
Timestamp now = new Timestamp(System.currentTimeMillis());
moneyPO.setCreateAt(now);
moneyPO.setUpdateAt(now);
MoneyPO res = moneyCreateRepository.save(moneyPO);
System.out.println("after insert res: " + res);
}
private void addMutl() {
// 批量新增
MoneyPO moneyPO = new MoneyPO();
moneyPO.setName("batch jpa 一灰灰");
moneyPO.setMoney(1000L);
moneyPO.setIsDeleted((byte) 0x00);
Timestamp now = new Timestamp(System.currentTimeMillis());
moneyPO.setCreateAt(now);
moneyPO.setUpdateAt(now);
MoneyPO moneyPO2 = new MoneyPO();
moneyPO2.setName("batch jpa 一灰灰");
moneyPO2.setMoney(1000L);
moneyPO2.setIsDeleted((byte) 0x00);
moneyPO2.setCreateAt(now);
moneyPO2.setUpdateAt(now);
Iterable<MoneyPO> res = moneyCreateRepository.saveAll(Arrays.asList(moneyPO, moneyPO2));
System.out.println("after batchAdd res: " + res);
}
}
看下上面的兩個插入方式,就這麼簡單,
- 通過IoC/DI注入 repository
- 建立PO物件,然後呼叫
save
,saveAll
方法就ok了
上面是一般的使用姿勢,那麼非一般使用姿勢呢?
b. 插入時預設值支援方式
在建立表的時候,我們知道欄位都有預設值,那麼如果PO物件中某個成員我不傳,可以插入成功麼?會是預設的DB值麼?
private void addWithNull() {
// 單個新增
try {
MoneyPO moneyPO = new MoneyPO();
moneyPO.setName("jpa 一灰灰 ex");
moneyPO.setMoney(2000L);
moneyPO.setIsDeleted(null);
MoneyPO res = moneyCreateRepository.save(moneyPO);
System.out.println("after insert res: " + res);
} catch (Exception e) {
System.out.println("addWithNull field: " + e.getMessage());
}
}
當看到上面的try/catch
可能就有預感,上面的執行多半要跪(