이 기사의 개발 환경 : Spring-Boot : 2.0.3. Release + Java1.8
왜 해야하는지
소프트 삭제 : 즉, 실제 삭제 작업은 수행되지 않습니다. 엔티티 간의 바인딩 (외국 키)이 존재하기 때문에 일부 데이터를 삭제하면 다른 데이터가 불완전하게됩니다. 예를 들어, 컴퓨터 클래스 1801의 교사는 Zhang San입니다. 현재 Zhang San을 삭제하면 컴퓨터 클래스 1801을 쿼리 할 때 Zhang San이 더 이상 존재하지 않으므로 EntityNotFound 오류를보고합니다. 물론, 외국의 주요 제약 조건이있는 데이터베이스에서 Zhang San이 1801 클래스의 교사 인 경우 Zhang San을 직접 삭제하고 바인딩 예외를보고합니다. 다시 말해, Zhang San을 직접 삭제하는 동작은 실행할 수 없습니다.
그러나 때로는 삭제해야 할 필요가 있습니다. 예를 들어, 직원이 떠난 다음 직원 관리에서 직원을 삭제하려고합니다. 그러나 직원은 데이터 테이블에 기록이 있습니다. 예를 들어, 우리는 2017 년 두 번째 학기의 데이터 구조가 Zhang Sanjiao라고 기록했습니다. 그런 다음 제약이 존재하기 때문에 Zhang San을 삭제할 때 바인딩 오류 가보고됩니다. 다시 말해 : 삭제되어야하지만 삭제할 수는 없다는 당황.
이것은이 기사에서 언급 한 소프트 삭제를 사용합니다. 소위 소프트 삭제는 데이터 테이블에서 데이터를 실제로 삭제하지 않고 대신 삭제할지 여부를 레코드에 추가한다는 것을 의미합니다.
Spring JPA는 소프트 삭제를 지원 하며이 문제를 해결하기 위해 양질의 품질로 더 많은 기사를 찾을 수 있습니다. 일반적인 단계는 다음과 같습니다. 1. add @sqldelete ( "xxxx set deleted = 1 where id = ?")。2.加入@Where(clause = "deleted = false") . 그러나이 솔루션은 완벽하지 않습니다. 구체적으로 :
Zhang San을 1801 년 클래스 교사로 데려가 봅시다.
주석을 추가 한 후 실제로 Zhang San을 성공적으로 삭제할 수 있습니다. 삭제 작업 후, 우리는 데이터 테이블을 확인했으며 Zhang San의 레코드는 실제로 존재합니다. 그러나 현재 all 또는 page 쿼리를 수행하면 500 EntiyNotFound 오류가 발생합니다. 모든 쿼리 중에 JPA는 @Where 에 쿼리 매개 변수를 자동으로 추가하기 때문입니다. 관련 데이터의 deleted = true 로 인해 관련 엔티티를 찾을 수 없다는 예외가 있습니다.
그러나 사실 : 엔티티가 삭제되었지만 여전히 존재하며 우리는이를 협회 쿼리에 적용하려고합니다. 500 EntiyNotFound 예외가있을 것으로 예상되지 않습니다.
이 기사의 해결책은 다음과 같습니다.
해결책
구현
초기화
ClazzTest, Clazz, Teacher 세 가지 새로운 엔티티를 만들고 새로운 BaseEntity Abstract Class 엔티티를 만듭니다. 여기서 ClazzTest @Where(clause = "deleted = false") .
패키지 com.mengyunzhi.springbootsamplecode.softdelete.entity; import javax.persistence.mappedSuperClass; @MappedSuperClassPublic Abstract BaseEntity {private boolean deleted = false; // Setter and Getter} packet com.mengyunzhi.springbootsamplecode.softdelete.entity; import org.hibernate.annotations.sqldelete; import javax.persistence.entity; import javax.persistence.generatedValue; import javax.persistence.id; class*/enter@sqldete (sqledet) `klass` set deleted = 1 where id =? ") public class klass는 baseentity {@id @generatedvalue private long id; 개인 문자열 이름; // Setter and Getter} @entity @sqldelete (sql = "update`klass_test` set deleted = 1 where id =?") @where (clause = "deleted = false") public class klasstest 확장 기반 {@id @generatedvalue private long id; 개인 문자열 이름;} Crudrepository를 다시 작성하십시오
패키지 com.mengyunzhi.springbootsamplecode.softdelete.core; import org.springframework.data.jpa.jpa.repository. import org.spramframework.data.repository.crudrepository; import org.springframework.data.repository .norebean; javax.transaction.transactional; import java.util.optional;/*** 소프트 삭제를 적용* 기본값 @where (clause = "deleted = 0")는 동점 내부에서 연관된 쿼리가 수행 될 때 객체를 제외 할 것입니다* 참조 : 참조 : 참조 : 참조 : 참조 : https://stackoverflow.com/questions/19323557/handling-soft-deletes-with-spring-jpa/22202469 * @author Mengyunzhi Hebei University of Technology */ @norepositoryBeanpublic Interface SoftDeleteCrudrepositority <T, id> id, id, id> id. @transactional @Query ( " #{ #entityName} e에서 e를 select e.id =? 1 및 e.deleted = false") 선택 사항 <t> findByid (id id); @override @transactional default boolean ensistSbyId (id id) {return findByid (id) .ispresent (); } @Override @transactional @Query ( " #{ #entityName} e에서 e를 선택하십시오. @override @transactional @Query ( " #{ #entityName} e에서 e를 선택하십시오. @override @transactional @Query ( " #{ #entityName} e에서 e.deleted = false")에서 #{ #entityName} e에서 count (e)를 선택하십시오.) long count ();} 새 창고 수업을 만듭니다
Spring의 Crudpository 상속.
/*** class* @Author Panjie*/public interface klassrepository 확장 SoftDeleteCrudrepository <klass, long> {} 공개 인터페이스 KlasStestRepository 확장 SoftDeleteCrudRepository <Klasstest, long> {} 공개 인터페이스 교사 재구성은 crudrepository <Teacher, Long> {}을 확장합니다. 시험
패키지 com.mengyunzhi.springbootsamplecode.softdelete.repository; import com.mengyunzhi.springbootsamplecode.softdelete.entity.klass; import com.mengyunzhi.springbootsamplecode.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; org.springframework.bean.beans.annotation.autowired; import org.springframework.boot.test.context.springboottest; import org.springframework.orm.jpa.jpaobjectretrievalluexception; import org.springframework.test.junt.junt.junt.junt.juxt.juxt.juxt.juxt.juxt.juxt.junt. java.util.list; import java.util.optional;/** * @author panjie */@springboottest@runwith (springrunner.class) 공개 클래스 TeacherRepositoryTest {private final static logger = loggeractory.getLogger (teachertortoritorytest.class); @autowired klassrepository klassrepository; @autowired klasstestrepository klasstestrepository; @autowired TeacherRepository TeacherRepository; @test public void findByid () {logger.info ( "Klass 및 Klasstest를 가진 새 교사 만들기"); klass klass = new Klass (); klassrepository.save (klass); klasstest klasstest = new Klasstest (); klasstestrepository.save (klasstest); 교사 교사 = 새로운 교사 (); Teacher.setKlass (klass); 교사 .setKlasstest (klasstest); TeacherRepository.save (교사); logger.info ( "교사를 찾아, 실체가 발견되었다고 주장하고 예외는 예외가 발생하지 않았다"); 선택적 <prection> el Assertions.assertThat (TeacherOptional.get ()). isnotnull (); logger.info ( "관련 KLASS를 삭제 한 다음 교사 엔티티를 찾아서 엔티티가 발견되었다고 주장합니다. 예외는 예외가 발생하지 않았습니다. 교사 실체에 여전히 삭제 된 KLASS 엔티티가 있다고 주장합니다."); 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 ( "교사 목록 찾기, 예외는 발생하지 않았다. 교사 엔티티에 삭제 된 klass 엔티티 레코드가 있다고 주장한다"); list <peacher> el for (Teacher Teacher1 : TeacherList) {Assertions.assertThat (Teacher1.getKlass (). getId ()). isequalto (klass.getId ()); } logger.info ( "관련 KlasStest를 삭제 한 다음 교사 엔티티를 찾아 삭제 된 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 ( "교사 목록을 다시 찾아보고, jpaobjectretievalfailureException이 발생할 것이라고 주장합니다 (EntityNoTfound 예외는 잡히고 캡슐화되고 던져졌습니다)); 부울 catchexception = false; try {teacherrepository.findall (); } catch (jpaobjectretievalfailureException e) {catchexception = true; } assertions.AssertThat (catchException) .istrue (); }} 요약
기본 @SQLDELETE 및 @Where Annotation을 사용하는 경우 JPA 데이터는 FindById () 메소드를 잘 처리 할 수 있지만 FindAll () 메소드를 잘 처리하지 못합니다. 여기서는 Crunrepository 방법을 다시 작성하는 방법을 구현했습니다. 기본 쿼리를 수행하면 사용자 정의 된 메소드를 사용하여 Deleted = True를 추가합니다. JPA가 연관 쿼리를 수행 할 때, @where 주석을 설정하지 않기 때문에 모든 데이터가 쿼리되므로 FoundAll () 쿼리가 수행 될 때 때때로 발생하는 예외를 피합니다.
이 기사에서는 예제 코드 만 제공합니다.
완전한 코드가 필요한 경우 https://github.com/mengyunzhi/springbootsamplecode/tree/master/softdelete를 클릭하십시오.
위는이 기사의 모든 내용입니다. 모든 사람의 학습에 도움이되기를 바랍니다. 모든 사람이 wulin.com을 더 지원하기를 바랍니다.