上面兩篇博文拉開了jpa使用姿式的面紗一角,接下來咱們繼續往下扯,數據插入db以後,並非說就一層不變了,就比如我在銀行開了戶,固然是準備往裏面存錢了,有存就有取(特別是當下銀行利率這麼低還不如買比特幣屯着,截止19年6月22日,btc已經突破1.1w$,惋惜沒錢買😭)這就是咱們今天的主題,數據更新--update的使用姿式java
經過本篇博文,您至少能夠選到mysql
save()
直接根據id來修改記錄- 利用
jpl
實現查詢修改的使用姿式 - 初識事物的神祕面紗
I. 環境準備
在開始以前,固然得先準備好基礎環境,如安裝測試使用mysql,建立SpringBoot項目工程,設置好配置信息等,關於搭建項目的詳情能夠參考前一篇文章git
下面簡單的看一下演示添加記錄的過程當中,須要的配置github
1. 表準備
沿用前一篇的表,結構以下spring
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. 項目配置
配置信息,與以前有一點點區別,咱們新增了更詳細的日誌打印;本篇主要目標集中在添加記錄的使用姿式,對於配置說明,後面單獨進行說明sql
## 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
3. 數據準備
數據修改嘛,因此咱們先向表裏面插入兩條數據,用於後面的操做數據庫
INSERT INTO `money` (`id`, `name`, `money`, `is_deleted`, `create_at`, `update_at`)
VALUES
(21, 'jpa 修改->一灰灰', 1212, 0, '2019-06-22 21:41:13', '2019-06-22 21:41:13'),
(22, 'jpa 修改->一灰灰', 6666, 0, '2019-06-22 21:41:13', '2019-06-22 21:41:13');
II. Update使用教程
下面開始進入正題,爲方便初看的同窗(沒空或者沒興趣瞅前面幾個博文的同窗)會有部份內容和前面的博文相同,看過的請無視api
1. 表關聯POJO
前面插入篇已經介紹了POJO的逐步建立過程,已經對應的註解含義,下面直接貼出成果緩存
@Data
@DynamicInsert
@Entity
@Table(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;
}
上面類中的幾個註解,說明以下
@Data
屬於lombok註解,與jpa無關,自動生成getter/setter/equals/hashcode/tostring
等方法@Entity
,@Table
jpa註解,表示這個類與db的表關聯,具體匹配的是表money
@Id
@GeneratedValue
做用與自增主鍵@Column
代表這個屬性與表中的某列對應@CreateDate
根據當前時間來生成默認的時間戳
2. Repository API聲明
接下來咱們新建一個api繼承自CurdRepository
,而後經過這個api來與數據庫打交道
public interface MoneyUpdateRepository extends CrudRepository<MoneyPO, Integer> {
}
3. 使用姿式
a. save
在前面一篇插入博文中,咱們知道當POJO的id存在時,調用save方法可能有兩種狀況
- 若db中這個id對應的字段不存在,則插入
- 若db中這個id對應的字段存在,則更新
咱們來試一下更新的效果,下面的代碼演示了兩塊,一個是當po中的全部成員值有效,更新其中的一個時,會怎樣;另一個演示的是部分更新時會怎樣(name爲空,表示我不但願更新name)
public void simpleUpdateById() {
MoneyPO record = moneyUpdateRepository.findById(21).get();
// 直接修改這個record的內容
record.setMoney(3333L);
moneyUpdateRepository.save(record);
record = moneyUpdateRepository.findById(21).get();
System.out.println("after updateMoney record: " + record);
record.setName(null);
record.setMoney(6666L);
moneyUpdateRepository.save(record);
record = moneyUpdateRepository.findById(21).get();
System.out.println("after updateMoney record: " + record);
}
輸出結果發現,前面執行成功,後面執行失敗
上面爲第一個執行結果,從拼接的sql能夠知道,是全量的修改;輸出結果也如咱們預期
後面將name設置爲空以後,再次更新,發現拋出異常,以下,這個是由於咱們的db限制,字段不容許有null的存在
從拼接的sql上看,咱們知道是由於每一個成員都做爲了update sql
家族的一員,在insert這一篇中咱們也遇到了相似的問題,當時是在POJO上添加註解@DynamicInsert
,根據實際須要選擇插入,那麼在更新這裏是否是也有相似的註解呢
@Data
@DynamicUpdate
@DynamicInsert
@Entity
@Table(name = "money")
public class MoneyPO {
}
在pojo上添加註解@DynamicUpdate
以後,再次進行嘗試,結果以下
竟然仍是失敗了,從輸出的sql來看,竟然把name
和money
都當成了sql的一部分,難道是由於咱們調用了setter方法的緣由麼。待着猜想,再來一次
MoneyPO toUpdate = new MoneyPO();
toUpdate.setId(21);
toUpdate.setMoney(6666L);
moneyUpdateRepository.save(toUpdate);
record = moneyUpdateRepository.findById(21).get();
System.out.println("after updateMoney record: " + record);
輸出結果以下,看來咱們上面的猜想並不對,拼接sql應該是根據哪一個字段發生了變化,就把哪一個作爲sql的一部分來作的
上面這個使用姿式看完以後,會有一個明顯的感受,這個更新的支持,必須先獲取目標對象再進行修改,很難知足咱們的平常業務場景;
b. 查詢更新
根據某個條件來更新對應的數據,這個就比較常見了,在jpa中,沒有找到根據方法名來支撐這種場景的方式,可是發現了另一個有意思的東西--jql
直接在方法方面,添加註解,註解內部寫sql
/** * 根據金錢來修改狀態 * * @param money * @param state */
@Modifying
@Query("update MoneyPO m set m.isDeleted=?2 where m.money=?1")
void updateStateByMoney(Long money, Byte state);
/** * 表達式計算 * * @param id * @param money */
@Modifying
@Query("update MoneyPO m set m.money=m.money + ?2 where m.id=?1")
void addMoneyById(Integer id, Long money);
上面就是一個查詢更新的case,注意兩個註解
@Modifying
這個必須有,告訴框架咱們執行的是更新/刪除操做@Query
內部是正常的sql語句,可是須要注意的是表名,不是實際的表,而是咱們前面定義的POJO
而後來測試一下使用
public void updateByQuery() {
// 經過查詢修改
moneyUpdateRepository.updateStateByMoney(6666L, (byte) 0x01);
MoneyPO record = moneyUpdateRepository.findById(21).get();
System.out.println("after update record: " + record);
moneyUpdateRepository.addMoneyById(21, 3333L);
record = moneyUpdateRepository.findById(21).get();
System.out.println("after addMoney record: " + record);
}
執行上面的代碼,悲催的發現竟然報錯了 Caused by: javax.persistence.TransactionRequiredException: Executing an update/delete query
從堆棧的描述上來看,更新/刪除必需要開啓事務,那麼事務是什麼東西呢?下面推薦幾篇博文
關於jpa中更多事務相關的,後面再引入,咱們先回到本文主題,如何解決問題: 在調用上面的方法上面添加事務註解便可
@Transactional
public void testUpdate() {
simpleUpdateById();
updateByQuery();
}
再次執行以後,結果以下
看上面的結果,發現money+3333以後的輸出結果竟然仍是6666;可是咱們再看db的結果,已是9999了,爲何會這樣呢?
上面這個問題比較有意思了,初步猜想與事物已經jpa的內部緩存機制有關係,至於具體是否是這樣,有必要專門開坑來驗證一二
4. 小結
利用JPA實現表數據的更新,上面主要介紹了兩種方式, save + jql
save
經過save更新時,須要指定id來實現單條記錄的修改
jql
語法與sql差很少,配合兩個註解 @Modifying
, @Query
來使用,下面是一個實例,兩點須要注意
- 表名是咱們定義的與db中表關聯的POJO
- 參數傳遞格式爲
?index
, index爲參數位置
@Modifying
@Query("update MoneyPO m set m.isDeleted=?2 where m.money=?1")
void updateStateByMoney(Long money, Byte state);
數據修改和刪除,須要顯示聲明事務,不然會報錯,一個是在調用的方法上添加註解 @Transactional
,或者直接在repository api的接口上添加註解 @Transactional
II. 其餘
0. 源碼與相關博文
源碼
- 工程:https://github.com/liuyueyi/spring-boot-demo
- module: https://github.com/liuyueyi/spring-boot-demo/blob/master/spring-boot/102-jpa
相關博文
- mysql之鎖與事務詳解
- Spring學習之事務的使用姿式
- Spring學習之事務管理與傳播屬性
- 190612-SpringBoot系列教程JPA之基礎環境搭建
- 190614-SpringBoot系列教程JPA之新增記錄使用姿式
1. 一灰灰Blog
盡信書則不如,以上內容,純屬一家之言,因我的能力有限,不免有疏漏和錯誤之處,如發現bug或者有更好的建議,歡迎批評指正,不吝感激
下面一灰灰的我的博客,記錄全部學習和工做中的博文,歡迎你們前去逛逛
- 一灰灰Blog我的博客 https://blog.hhui.top
- 一灰灰Blog-Spring專題博客 http://spring.hhui.top