您好,登錄后才能下訂單哦!
本篇內容介紹了“SpringBoot JPA中delete的使用方法”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
常見db中的四個操作curd,前面的幾篇博文分別介紹了insert,update,接下來我們看下delete的使用姿勢,通過JPA可以怎樣刪除數據
一般來講是不建議物理刪除(直接從表中刪除記錄)數據的,在如今數據就是錢的時代,更常見的做法是在表中添加一個表示狀態的字段,然后通過修改這個字段來表示記錄是否有效,從而實現邏輯刪除;這么做的原因如下
物理刪除,如果出問題恢復比較麻煩
無法保證代碼一定準確,在出問題的時候,刪錯了數據,那就gg了
刪除數據,會導致重建索引
Innodb數據庫對于已經刪除的數據只是標記為刪除,并不真正釋放所占用的磁盤空間,這就導致InnoDB數據庫文件不斷增長,也會導致表碎片
邏輯刪除,保留數據,方便后續針對數據的挖掘或者分析
<!-- more -->
在開始之前,當然得先準備好基礎環境,如安裝測試使用mysql,創建SpringBoot項目工程,設置好配置信息等,關于搭建項目的詳情可以參考前一篇文章
190612-SpringBoot系列教程JPA之基礎環境搭建
下面簡單的看一下演示添加記錄的過程中,需要的配置
沿用前一篇的表,結構如下
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;
配置信息,與之前有一點點區別,我們新增了更詳細的日志打印;本篇主要目標集中在添加記錄的使用姿勢,對于配置說明,后面單獨進行說明
## 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
數據修改嘛,所以我們先向表里面插入兩條數據,用于后面的操作
INSERT INTO `money` (`id`, `name`, `money`, `is_deleted`, `create_at`, `update_at`) VALUES (20, 'jpa 一灰灰5', 2323, 0, '2019-07-02 08:42:41', '2019-07-02 08:42:41'), (21, 'jpa 一灰灰6', 2333, 0, '2019-07-02 08:42:41', '2019-07-02 08:42:41'), (22, 'jpa 一灰灰7', 6666, 0, '2019-07-02 08:42:41', '2019-07-02 08:42:41'), (23, 'jpa 一灰灰8', 2666, 0, '2019-07-02 08:42:41', '2019-07-02 08:42:41');
下面談及到的刪除,都是物理刪除,可以理解為直接將某些記錄從表中抹除掉(并不是說刪了就完全沒有辦法恢復)針對CURD四種操作而言,除了read之外,另外三個insert,update,delete都會加寫鎖(一般來將會涉及到行鎖和gap鎖,從后面也會看到,這三個操作要求顯示聲明事物)
前面插入篇已經介紹了POJO的逐步創建過程,已經對應的注解含義,下面直接貼出成果
@Data @DynamicUpdate @DynamicInsert @Entity @Table(name = "money") public class MoneyPO { @Id // 如果是auto,則會報異常 Table 'mysql.hibernate_sequence' doesn't exist // @GeneratedValue(strategy = GenerationType.AUTO) @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
根據當前時間來生成默認的時間戳
接下來我們新建一個api繼承自CurdRepository
,然后通過這個api來與數據庫打交道
public interface MoneyDeleteRepository extends CrudRepository<MoneyPO, Integer> { /** * 查詢測試 * @param id * @return */ List<MoneyPO> queryByIdGreaterThanEqual(int id); }
先寫一個用于查詢數據的方法,用于校驗我們執行刪除之后,是否確實被刪除了
private void showLeft() { List<MoneyPO> records = moneyDeleteRepository.queryByIdGreaterThanEqual(20); System.out.println(records); }
在執行下面操作之前,先調用上面的,輸出結果如
[MoneyPO(id=20, name=jpa 一灰灰5, money=2323, isDeleted=0, createAt=2019-07-02 08:42:41.0, updateAt=2019-07-02 08:42:41.0), MoneyPO(id=21, name=jpa 一灰灰6, money=2333, isDeleted=0, createAt=2019-07-02 08:42:41.0, updateAt=2019-07-02 08:42:41.0), MoneyPO(id=22, name=jpa 一灰灰7, money=6666, isDeleted=0, createAt=2019-07-02 08:42:41.0, updateAt=2019-07-02 08:42:41.0), MoneyPO(id=23, name=jpa 一灰灰8, money=2666, isDeleted=0, createAt=2019-07-02 08:42:41.0, updateAt=2019-07-02 08:42:41.0)]
這種應該屬于最常見的刪除方式了,為了避免誤刪,通過精確的主鍵id來刪除記錄,是一個非常好的使用姿勢,CrudRepository
這個接口已經提供了對應的方法,所以我們可以直接使用
private void deleteById() { // 直接根據id進行刪除 moneyDeleteRepository.deleteById(21); showLeft(); }
執行完畢之后,輸出結果如下,對比前面的輸出可以知道 id=21
的記錄被刪除了
[MoneyPO(id=20, name=jpa 一灰灰5, money=2323, isDeleted=0, createAt=2019-07-02 08:42:41.0, updateAt=2019-07-02 08:42:41.0), MoneyPO(id=22, name=jpa 一灰灰7, money=6666, isDeleted=0, createAt=2019-07-02 08:42:41.0, updateAt=2019-07-02 08:42:41.0), MoneyPO(id=23, name=jpa 一灰灰8, money=2666, isDeleted=0, createAt=2019-07-02 08:42:41.0, updateAt=2019-07-02 08:42:41.0)]
然后一個疑問自然而然的來了,如果這個id對應的記錄不存在,會怎樣?
把上面代碼再執行一次,發現拋了異常
為什么會這樣呢?我們debug進去,調用的實現是默認的 SimpleJpaRepository
,其源碼如
// 類為: org.springframework.data.jpa.repository.support.SimpleJpaRepository @Transactional public void deleteById(ID id) { Assert.notNull(id, ID_MUST_NOT_BE_NULL); delete(findById(id).orElseThrow(() -> new EmptyResultDataAccessException( String.format("No %s entity with id %s exists!", entityInformation.getJavaType(), id), 1))); } @Transactional public void delete(T entity) { Assert.notNull(entity, "The entity must not be null!"); em.remove(em.contains(entity) ? entity : em.merge(entity)); }
從源碼可以看出,這個是先通過id進行查詢,如果對應的記錄不存在時,直接拋異常;當存在時,走remove邏輯;
如果我們希望刪除一個不存在的數據時,不要報錯,可以怎么辦?
自定義實現一個繼承SimpleJpaRepository
的類,覆蓋刪除方法
@Repository @Transactional(readOnly = true) public class MoneyDeleteRepositoryV2 extends SimpleJpaRepository<MoneyPO, Integer> { @Autowired public MoneyDeleteRepositoryV2(EntityManager em) { this(JpaEntityInformationSupport.getEntityInformation(MoneyPO.class, em), em); } public MoneyDeleteRepositoryV2(JpaEntityInformation<MoneyPO, ?> entityInformation, EntityManager entityManager) { super(entityInformation, entityManager); } public MoneyDeleteRepositoryV2(Class<MoneyPO> domainClass, EntityManager em) { super(domainClass, em); } @Override public void deleteById(Integer id) { Optional<MoneyPO> rec = findById(id); rec.ifPresent(super::delete); } }
然后再調用上面的方法就可以了,不演示具體的測試case了,源碼可以到項目工程中查看 ???? 源碼
雖然根據id進行刪除比較穩妥,但也無法避免某些情況下需要根據其他的字段來刪除,比如我們希望刪除名為 jpa 一灰灰7
的數據,這時則需要我們在MoneyDeleteRepository
新增一個方法
/** * 根據name進行刪除 * * @param name */ void deleteByName(String name);
這里比較簡單的提一下這個方法的命名規則,后面在查詢這一篇會更加詳細的說明;
delete
表示執行的是刪除操作
By
表示根據某個字段來進行條件限定
Name
這個有POJO中的屬性匹配
上面這個方法,如果翻譯成sql,相當于 delete from money where name=xx
調用方式和前面一樣,如下
private void deleteByName() { moneyDeleteRepository.deleteByName("jpa 一灰灰7"); showLeft(); }
然后我們執行上面的測試,發現并不能成功,報錯了
通過前面update的學習,知道需要顯示加一個事物的注解,我們這里直接加在Repository
中
/** * 根據name進行刪除 * * @param name */ @Transactional void deleteByName(String name);
然后再次執行輸出如下,這里我們把sql的日志也打印了
Hibernate: select moneypo0_.id as id1_0_, moneypo0_.create_at as create_a2_0_, moneypo0_.is_deleted as is_delet3_0_, moneypo0_.money as money4_0_, moneypo0_.name as name5_0_, moneypo0_.update_at as update_a6_0_ from money moneypo0_ where moneypo0_.name=? Hibernate: delete from money where id=? Hibernate: select moneypo0_.id as id1_0_, moneypo0_.create_at as create_a2_0_, moneypo0_.is_deleted as is_delet3_0_, moneypo0_.money as money4_0_, moneypo0_.name as name5_0_, moneypo0_.update_at as update_a6_0_ from money moneypo0_ where moneypo0_.id>=? [MoneyPO(id=20, name=jpa 一灰灰5, money=2323, isDeleted=0, createAt=2019-07-02 08:42:41.0, updateAt=2019-07-02 08:42:41.0), MoneyPO(id=23, name=jpa 一灰灰8, money=2666, isDeleted=0, createAt=2019-07-02 08:42:41.0, updateAt=2019-07-02 08:42:41.0)]
從最終剩余的記錄來看,name為jpa 一灰灰7
的被刪除了,再看一下前面刪除的sql,會發現一個有意思的地方,deleteByName
這個方法,翻譯成sql變成了兩條
select * from money where name=xxx
先根據name查詢記錄
delete from money where id = xxx
根據前面查詢記錄的id,刪除記錄
接下來演示一個刪除money在[2000,3000]
區間的記錄,這時我們新增的放入可以是
/** * 根據數字比較進行刪除 * * @param low * @param big */ @Transactional void deleteByMoneyBetween(Long low, Long big);
通過方法命名也可以簡單知道上面這個等同于sql delete from money where money between xxx and xxx
測試代碼為
private void deleteByCompare() { moneyDeleteRepository.deleteByMoneyBetween(2000L, 3000L); showLeft(); }
輸出日志
Hibernate: select moneypo0_.id as id1_0_, moneypo0_.create_at as create_a2_0_, moneypo0_.is_deleted as is_delet3_0_, moneypo0_.money as money4_0_, moneypo0_.name as name5_0_, moneypo0_.update_at as update_a6_0_ from money moneypo0_ where moneypo0_.money between ? and ? Hibernate: delete from money where id=? Hibernate: delete from money where id=? Hibernate: select moneypo0_.id as id1_0_, moneypo0_.create_at as create_a2_0_, moneypo0_.is_deleted as is_delet3_0_, moneypo0_.money as money4_0_, moneypo0_.name as name5_0_, moneypo0_.update_at as update_a6_0_ from money moneypo0_ where moneypo0_.id>=? []
從拼接的sql可以看出,上面的邏輯等同于,先執行了查詢,然后根據id一個一個進行刪除....
我們通過聲明方法的方式來實現條件刪除;需要注意
刪除需要顯示聲明事物 @Transactional
刪除一個不存在的記錄,會拋異常
聲明刪除方法時,實際等同于先查詢記錄,然后根據記錄的id進行精準刪除
“SpringBoot JPA中delete的使用方法”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。