最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

spring boot實(shí)現(xiàn)軟刪除的示例代碼

 更新時(shí)間:2018年07月03日 13:42:50   作者:myskies  
這篇文章主要介紹了spring boot實(shí)現(xiàn)軟刪除的示例代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧

本文開發(fā)環(huán)境:spring-boot:2.0.3.RELEASE + java1.8

WHY TO DO

軟刪除:即不進(jìn)行真正的刪除操作。由于我們實(shí)體間的約束性(外鍵)的存在,刪除某些數(shù)據(jù)后,將導(dǎo)致其它的數(shù)據(jù)不完整。比如,計(jì)算機(jī)1801班的教師是張三,此時(shí),我們?nèi)绻褟埲齽h除掉,那么在查詢計(jì)算機(jī)1801班時(shí),由于張三不存了,所以就會(huì)報(bào)EntityNotFound的錯(cuò)誤。當(dāng)然了,在有外鍵約束的數(shù)據(jù)庫中,如果張三是1801班的教師,那么我們直接刪除張三將報(bào)一個(gè)約束性的異常。也就是說:直接刪除張三這個(gè)行為是無法執(zhí)行的。

但有些時(shí)候,我們的確有刪除的需求。比如說,有個(gè)員工離職了,然后我們想在員工管理中刪除該員工。但是:該員工由于在數(shù)據(jù)表中存在歷史記錄。比如我們記錄了17年第二學(xué)期的數(shù)據(jù)結(jié)構(gòu)是張三教的。那么,由于約束性的存在,刪除張三時(shí)就會(huì)報(bào)約束性錯(cuò)誤。也就是說:出現(xiàn)了應(yīng)該刪除,但卻刪除不了的尷尬。

這就用到了本文所提到的軟刪除,所謂軟刪除,就是說我并不真正的刪除數(shù)據(jù)表中的數(shù)據(jù),而是在給這條記錄加一個(gè)是否刪除的標(biāo)記。

spring jpa是支持軟刪除的,我們可以找到較多質(zhì)量不錯(cuò)的文章來解決這個(gè)問題。大體步驟為:1. 加入@SqlDelete("update xxxx set deleted = 1 where id = ?")。2.加入@Where(clause = "deleted = false")的注解。但這個(gè)解決方案并不完美。具體表現(xiàn)在:

我們還以張三是1801班的教師舉例。

加入注解后,我們的確是可以做到可以成功的刪除張三了,刪除操作后,我們查看數(shù)據(jù)表,張三的記錄的確也還在。但此時(shí),如果我們進(jìn)行all或是page查詢,將得到一個(gè)500 EntiyNotFound錯(cuò)誤。這是由于在all查詢時(shí),jpa自動(dòng)加入了@Where中的的查詢參數(shù),由于關(guān)聯(lián)數(shù)據(jù)的deleted = true,進(jìn)而發(fā)生了未找到關(guān)聯(lián)實(shí)體的異常。

但事實(shí)是:實(shí)體雖然被刪除,但實(shí)際在還在,我們想將其應(yīng)用到關(guān)聯(lián)查詢中。并不希望其發(fā)生500 EntiyNotFound異常。

本文的方案實(shí)現(xiàn)了:

  1. 可以成功的實(shí)現(xiàn)軟刪除。
  2. 再進(jìn)行關(guān)聯(lián)刪除時(shí),不發(fā)生500 EntiyNotFound錯(cuò)誤。

解決方案

  1. 即然500是由于注解@Where(clause = "deleted = false")引起的,那么我們棄用該注解。
  2. 我們需要在查詢時(shí),加入deleted = false的查詢。那么我們新建一個(gè)接口,并繼承jpa的CrudRepository,然后重寫其查詢相關(guān)的方法。在重寫過程中,加入deleted = false的查詢條件。

實(shí)施

初始化

新建ClazzTest, Clazz, Teacher三個(gè)實(shí)體,新建BaseEntity抽象類實(shí)體。其中ClazzTest用于演示使用@Where(clause = "deleted = false")注解時(shí)發(fā)生的異常。

package com.mengyunzhi.springbootsamplecode.softdelete.entity;

import javax.persistence.MappedSuperclass;

@MappedSuperclass
public abstract class BaseEntity {
  private Boolean deleted = false;
  // setter and getter
}

package com.mengyunzhi.springbootsamplecode.softdelete.entity;

import org.hibernate.annotations.SQLDelete;

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

/**
 * 班級(jí)
 */
@Entity
@SQLDelete(sql = "update `klass` set deleted = 1 where id = ?")
public class Klass extends BaseEntity {
  @Id
  @GeneratedValue
  private Long id;
  private String name;
  // setter and getter
}
@Entity
@SQLDelete(sql = "update `klass_test` set deleted = 1 where id = ?")
@Where(clause = "deleted = false")
public class KlassTest extends BaseEntity {
  @Id @GeneratedValue
  private Long id;
  private String name;
}

重寫CrudRepository

package com.mengyunzhi.springbootsamplecode.softdelete.core;


import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.NoRepositoryBean;

import javax.transaction.Transactional;
import java.util.Optional;

/**
 * 應(yīng)用軟刪除
 * 默認(rèn)的@Where(clause = "deleted = 0")會(huì)導(dǎo)致hibernate內(nèi)部進(jìn)行關(guān)聯(lián)查詢時(shí),發(fā)生ObjectNotFound的異常
 * 在此重新定義接口
 * 參考:https://stackoverflow.com/questions/19323557/handling-soft-deletes-with-spring-jpa/22202469
 * @author 河北工業(yè)大學(xué) 夢(mèng)云智軟件開發(fā)團(tuán)隊(duì)
 */
@NoRepositoryBean
public interface SoftDeleteCrudRepository<T, ID> extends CrudRepository<T, ID> {
  @Override
  @Transactional
  @Query("select e from #{#entityName} e where e.id = ?1 and e.deleted = false")
  Optional<T> findById(ID id);

  @Override
  @Transactional
  default boolean existsById(ID id) {
    return findById(id).isPresent();
  }

  @Override
  @Transactional
  @Query("select e from #{#entityName} e where e.deleted = false")
  Iterable<T> findAll();

  @Override
  @Transactional
  @Query("select e from #{#entityName} e where e.id in ?1 and e.deleted = false")
  Iterable<T> findAllById(Iterable<ID> ids);

  @Override
  @Transactional
  @Query("select count(e) from #{#entityName} e where e.deleted = false")
  long count();
}

新建倉庫類

繼承spring的CrudRepository。

/**
 * 班級(jí)
 * @author panjie
 */
public interface KlassRepository extends SoftDeleteCrudRepository<Klass, Long>{
}
public interface KlassTestRepository extends SoftDeleteCrudRepository<KlassTest, Long> {
}
public interface TeacherRepository extends CrudRepository<Teacher, Long> {
}

測(cè)試

package com.mengyunzhi.springbootsamplecode.softdelete.repository;

import com.mengyunzhi.springbootsamplecode.softdelete.entity.Klass;
import com.mengyunzhi.springbootsamplecode.softdelete.entity.KlassTest;
import com.mengyunzhi.springbootsamplecode.softdelete.entity.Teacher;
import org.assertj.core.api.Assertions;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.orm.jpa.JpaObjectRetrievalFailureException;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.List;
import java.util.Optional;


/**
 * @author panjie
 */
@SpringBootTest
@RunWith(SpringRunner.class)
public class TeacherRepositoryTest {
  private final static Logger logger = LoggerFactory.getLogger(TeacherRepositoryTest.class);
  @Autowired KlassRepository klassRepository;
  @Autowired KlassTestRepository klassTestRepository;
  @Autowired TeacherRepository teacherRepository;

  @Test
  public void findById() {
    logger.info("新建一個(gè)有Klass和KlassTest的教師");
    Klass klass = new Klass();
    klassRepository.save(klass);
    KlassTest klassTest = new KlassTest();
    klassTestRepository.save(klassTest);
    Teacher teacher = new Teacher();
    teacher.setKlass(klass);
    teacher.setKlassTest(klassTest);
    teacherRepository.save(teacher);

    logger.info("查找教師,斷言查找了實(shí)體,并且不發(fā)生異常");
    Optional<Teacher> teacherOptional = teacherRepository.findById(teacher.getId());
    Assertions.assertThat(teacherOptional.get()).isNotNull();


    logger.info("刪除關(guān)聯(lián)的Klass, 再查找教師實(shí)體,斷言查找到了實(shí)體,不發(fā)生異常。斷言教師實(shí)體中,仍然存在已經(jīng)刪除的Klass實(shí)體");
    klassRepository.deleteById(klass.getId());
    teacherOptional = teacherRepository.findById(teacher.getId());
    Assertions.assertThat(teacherOptional.get()).isNotNull();
    Assertions.assertThat(teacherOptional.get().getKlass().getId()).isEqualTo(klass.getId());

    logger.info("查找教師列表,不發(fā)生異常。斷言教師實(shí)體中,存在已刪除的Klass實(shí)體記錄");
    List<Teacher> teacherList = (List<Teacher>) teacherRepository.findAll();
    for (Teacher teacher1 : teacherList) {
      Assertions.assertThat(teacher1.getKlass().getId()).isEqualTo(klass.getId());
    }

    logger.info("刪除關(guān)聯(lián)的KlassTest,再查找教師實(shí)體, 斷言找到了刪除的klassTest");
    klassTestRepository.deleteById(klassTest.getId());
    teacherOptional = teacherRepository.findById(teacher.getId());
    Assertions.assertThat(teacherOptional.get()).isNotNull();
    Assertions.assertThat(teacherOptional.get().getKlassTest().getId()).isEqualTo(klassTest.getId());

    logger.info("再查找教師列表,斷言將發(fā)生JpaObjectRetrievalFailureException(EntityNotFound 異常被捕獲后,封裝拋出)異常");
    Boolean catchException = false;
    try {
      teacherRepository.findAll();
    } catch (JpaObjectRetrievalFailureException e) {
      catchException = true;
    }
    Assertions.assertThat(catchException).isTrue();
  }

}

總結(jié)

使用默認(rèn)的@SqlDelete以及@Where注解時(shí),jpa data能夠很好的處理findById()方法,但卻未能很好的處理findAll()方法。在此,我們通過重寫CrunRepository的方法,實(shí)現(xiàn)了,將進(jìn)行基本的查詢時(shí),使用我們自定義的加入了deleted = true的方法。而當(dāng)jpa進(jìn)行關(guān)聯(lián)查詢時(shí),由于我們未設(shè)置@Where注解,所以將查詢出所有的數(shù)據(jù),進(jìn)而避免了當(dāng)進(jìn)行findAll()查詢時(shí),有被刪除的關(guān)聯(lián)數(shù)據(jù)時(shí)而發(fā)生的異常。

本文中,我們只給了部分示例代碼。

如果你需要完整的代碼,請(qǐng)點(diǎn)擊:https://github.com/mengyunzhi/springBootSampleCode/tree/master/softDelete。

以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • 詳解springboot集成mybatis xml方式

    詳解springboot集成mybatis xml方式

    這篇文章主要介紹了詳解springboot集成mybatis xml方式,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-07-07
  • MyBatis接口的簡單實(shí)現(xiàn)原理分析

    MyBatis接口的簡單實(shí)現(xiàn)原理分析

    這里僅僅舉個(gè)簡單例子來說明原理,不是完全針對(duì)MyBatis的,這種思想我們也可以應(yīng)用在其他地方。地mybatis接口實(shí)現(xiàn)原理感興趣的朋友一起看看吧
    2017-07-07
  • 如何使用lamda表達(dá)式對(duì)list進(jìn)行求和

    如何使用lamda表達(dá)式對(duì)list進(jìn)行求和

    這篇文章主要介紹了如何使用lamda表達(dá)式對(duì)list進(jìn)行求和問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • IDEA如何解決代碼沒有提示問題

    IDEA如何解決代碼沒有提示問題

    文章介紹了如何解決IDEA中代碼提示功能缺失的問題,首先,需要調(diào)整快捷鍵設(shè)置,將CyclicExpandWord鍵映射為Ctrl+/,其次,確保輸入框中設(shè)置為Basic,然后添加鍵盤快捷鍵Alt+/,如果問題依然存在,可能是由于電腦省電模式導(dǎo)致的,需要取消相關(guān)設(shè)置
    2024-11-11
  • 解決mybatis-plus-boot-starter與mybatis-spring-boot-starter的錯(cuò)誤問題

    解決mybatis-plus-boot-starter與mybatis-spring-boot-starter的錯(cuò)誤問題

    本文主要講述了在使用MyBatis和MyBatis-Plus時(shí)遇到的綁定異常問題,通過排查和總結(jié),作者發(fā)現(xiàn)使用MyBatis-Plus?Boot?Starter可以解決這個(gè)問題,文章詳細(xì)對(duì)比了MyBatis-Plus?Boot?Starter和MyBatis?Spring?Boot?Starter的功能和使用場景
    2025-01-01
  • Java安全之Filter權(quán)限繞過的實(shí)現(xiàn)

    Java安全之Filter權(quán)限繞過的實(shí)現(xiàn)

    在一些需要挖掘一些無條件RCE中,大部分類似于一些系統(tǒng)大部分地方都做了權(quán)限控制的,而這時(shí)候想要利用權(quán)限繞過就顯得格外重要,本文就介紹了如何實(shí)現(xiàn),一起來了解一下
    2021-05-05
  • 如何基于Spring使用工廠模式實(shí)現(xiàn)程序解耦

    如何基于Spring使用工廠模式實(shí)現(xiàn)程序解耦

    這篇文章主要介紹了如何基于Spring使用工廠模式實(shí)現(xiàn)程序解耦,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-12-12
  • Java中使用qsort對(duì)類進(jìn)行排序的操作代碼

    Java中使用qsort對(duì)類進(jìn)行排序的操作代碼

    這篇文章主要介紹了JAVA中如何使用qsort對(duì)類進(jìn)行排序,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-09-09
  • idea啟動(dòng)tomcat控制臺(tái)中文亂碼的三種情況解決

    idea啟動(dòng)tomcat控制臺(tái)中文亂碼的三種情況解決

    本文主要介紹了idea啟動(dòng)tomcat控制臺(tái)中文亂碼,主要包括三種情況,分別介紹了一下解決方法,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-10-10
  • Java實(shí)現(xiàn)彈窗效果的基本操作(2)

    Java實(shí)現(xiàn)彈窗效果的基本操作(2)

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)彈窗效果的基本操作第二篇,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-07-07

最新評(píng)論

来安县| 修水县| 庄浪县| 大厂| 淮滨县| 榕江县| 左贡县| 麻江县| 旬阳县| 车险| 扶绥县| 瑞安市| 绥棱县| 湟源县| 那曲县| 思茅市| 确山县| 手游| 蓬莱市| 佳木斯市| 灵武市| 福泉市| 张北县| 永城市| 同心县| 克山县| 青河县| 建阳市| 开鲁县| 芒康县| 东乡| 安岳县| 长岭县| 雅安市| 永宁县| 伊金霍洛旗| 常熟市| 江门市| 二手房| 北安市| 淮滨县|