복붙노트

[SPRING] 스프링 JPA로 소프트 삭제 처리하기

SPRING

스프링 JPA로 소프트 삭제 처리하기

나는 테이블 항목을 다음과 같이 정의했다.

id, <fields>..., active

Active는 soft-delete 플래그이며 항상 1 또는 0입니다. 장기적으로 보면 이력 표가 없어 질 수 있습니다.

public interface StuffRepository extends JpaRepository<StuffEntity, Long> {} 

코드에서는 항상 활성 레코드를 사용합니다. Spring이 항상이 저장소를 위해 생성 된 쿼리에 활성 = 1 조건을 추가하도록하는 방법이 있습니까? 또는 이상적으로는 쿼리를 생성하는 데 사용되는 문법을 확장 할 수 있습니까?

어디에서나 @ 큐 이름을 만들 수 있지만 생성 된 쿼리의 편의성을 잃어버린다는 것을 이해합니다. 또한 "활성"방법으로 인터페이스를 오염시키지 않으려합니다.

그게 중요하다면 내 JPA 구현으로 최대 절전 모드 4.2 사용하고 있습니다.

해결법

  1. ==============================

    1.이것은 오래된 질문이며 이미 답을 찾았을 것입니다. 하지만, 스프링 / JPA / Hibernate 프로그래머가 대답을 구하는 모든 곳에서 -

    이것은 오래된 질문이며 이미 답을 찾았을 것입니다. 하지만, 스프링 / JPA / Hibernate 프로그래머가 대답을 구하는 모든 곳에서 -

    당신은 엔티티가 있다고해라 Dog :

     @Entity
     public class Dog{
    
     ......(fields)....        
    
     @Column(name="is_active")
     private Boolean active;
     }
    

    저장소 :

    public interface DogRepository extends JpaRepository<Dog, Integer> {
    } 
    

    엔티티 레벨에 @Where 주석을 추가하면됩니다.

    @Entity
    @Where(clause="is_active=1")
    public class Dog{
    
    ......(fields)....        
    
    @Column(name="is_active")
    private Boolean active;
    }
    

    저장소가 수행하는 모든 쿼리는 자동으로 "비활성"행을 필터링합니다.

  2. ==============================

    2.@Where (clause = "is_active = 1")는 스프링 데이터 jpa로 소프트 삭제를 처리하는 가장 좋은 방법은 아닙니다.

    @Where (clause = "is_active = 1")는 스프링 데이터 jpa로 소프트 삭제를 처리하는 가장 좋은 방법은 아닙니다.

    첫째, 최대 절전 모드 구현에서만 작동합니다.

    둘째, 스프링 데이터로 소프트 삭제 된 엔티티를 가져올 수 없습니다.

    내 솔루션은 봄 데이터에 의해 제공됩니다. # {# entityName} 표현식은 구체적인 저장소 유형 이름을 나타내는 일반 저장소에서 사용할 수 있습니다.

    코드는 다음과 같습니다.

    //Override CrudRepository or PagingAndSortingRepository's query method:
    @Override
    @Query("select e from #{#entityName} e where e.deleteFlag=false")
    public List<T> findAll();
    
    //Look up deleted entities
    @Query("select e from #{#entityName} e where e.deleteFlag=true")
    public List<T> recycleBin(); 
    
    //Soft delete.
    @Query("update #{#entityName} e set e.deleteFlag=true where e.id=?1")
    @Modifying
    public void softDelete(String id); 
    
  3. ==============================

    3.답변을 바탕으로 softdleep에 대한 재정의 된 메소드를 사용하여 CrudRepository 구현을 만들었습니다.

    답변을 바탕으로 softdleep에 대한 재정의 된 메소드를 사용하여 CrudRepository 구현을 만들었습니다.

    @NoRepositoryBean
    public interface SoftDeleteCrudRepository<T extends BasicEntity, ID extends Long> extends CrudRepository<T, ID> {
      @Override
      @Transactional(readOnly = true)
      @Query("select e from #{#entityName} e where e.isActive = true")
      List<T> findAll();
    
      @Override
      @Transactional(readOnly = true)
      @Query("select e from #{#entityName} e where e.id in ?1 and e.isActive = true")
      Iterable<T> findAll(Iterable<ID> ids);
    
      @Override
      @Transactional(readOnly = true)
      @Query("select e from #{#entityName} e where e.id = ?1 and e.isActive = true")
      T findOne(ID id);
    
      //Look up deleted entities
      @Query("select e from #{#entityName} e where e.isActive = false")
      @Transactional(readOnly = true)
      List<T> findInactive();
    
      @Override
      @Transactional(readOnly = true)
      @Query("select count(e) from #{#entityName} e where e.isActive = true")
      long count();
    
      @Override
      @Transactional(readOnly = true)
      default boolean exists(ID id) {
          return findOne(id) != null;
      }
    
      @Override
      @Query("update #{#entityName} e set e.isActive=false where e.id = ?1")
      @Transactional
      @Modifying
      void delete(Long id);
    
    
      @Override
      @Transactional
      default void delete(T entity) {
          delete(entity.getId());
      }
    
      @Override
      @Transactional
      default void delete(Iterable<? extends T> entities) {
          entities.forEach(entitiy -> delete(entitiy.getId()));
      }
    
      @Override
      @Query("update #{#entityName} e set e.isActive=false")
      @Transactional
      @Modifying
      void deleteAll();
    }
    

    BasicEntity와 함께 사용할 수 있습니다.

    @MappedSuperclass
    public abstract class BasicEntity {
      @Column(name = "is_active")
      private boolean isActive = true;
    
      public abstract Long getId();
    
      // isActive getters and setters...
    }
    

    그리고 마지막 엔티티 :

    @Entity
    @Table(name = "town")
    public class Town extends BasicEntity {
    
        @Id
        @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "town_id_seq")
        @SequenceGenerator(name = "town_id_seq", sequenceName = "town_id_seq", allocationSize = 1)
        protected Long id;
    
        private String name;
    
        // getters and setters...
    }
    
  4. ==============================

    4.현재 버전 (최대 1.4.1)에는 Spring Data JPA의 소프트 삭제를위한 전용 지원이 없습니다. 그러나 DATAJPA-307의 피쳐 브랜치는 현재 출시 예정인 기능이기 때문에 사용을 권장합니다.

    현재 버전 (최대 1.4.1)에는 Spring Data JPA의 소프트 삭제를위한 전용 지원이 없습니다. 그러나 DATAJPA-307의 피쳐 브랜치는 현재 출시 예정인 기능이기 때문에 사용을 권장합니다.

    현재 상태를 사용하려면 1.5.0.DATAJPA-307-SNAPSHOT까지 사용하는 버전을 업데이트하고 작동해야하는 특수 스프링 데이터 공유 버전을 가져 오도록하십시오. 테스트 결과를 얻는 방법을 알아야하는 샘플 테스트 케이스를 따라 할 수 있어야합니다.

    P.S .: 기능에 대한 작업이 끝나면 질문을 업데이트하겠습니다.

  5. ==============================

    5.SimpleJpaRepository에서 확장하고 일반적인 방식으로 소프트 삭제 기능을 정의 할 수있는 사용자 정의 리포지토리를 만들 수 있습니다.

    SimpleJpaRepository에서 확장하고 일반적인 방식으로 소프트 삭제 기능을 정의 할 수있는 사용자 정의 리포지토리를 만들 수 있습니다.

    커스텀 JpaRepositoryFactoryBean을 생성하고 메인 클래스에서 활성화 할 필요가 있습니다.

    내 코드는 여기에서 확인할 수 있습니다. https://github.com/dzinot/spring-boot-jpa-soft-delete

  6. ==============================

    6.최대 절전 모드 특정 주석을 가져 오지 않으려는 경우 데이터베이스보기 (또는 Oracle의 해당 기능)를 사용하는 것이 좋습니다. mySQL 5.5에서 필터 기준이 active = 1처럼 단순 할 경우 이러한보기를 업데이트하고 삽입 할 수 있습니다.

    최대 절전 모드 특정 주석을 가져 오지 않으려는 경우 데이터베이스보기 (또는 Oracle의 해당 기능)를 사용하는 것이 좋습니다. mySQL 5.5에서 필터 기준이 active = 1처럼 단순 할 경우 이러한보기를 업데이트하고 삽입 할 수 있습니다.

    이것이 좋은 아이디어인지는 아마 당신의 데이터베이스에 달려있다. 그러나 그것은 나의 구현에서 훌륭하게 작동한다.

    삭제를 취소하려면 직접 'Stuff'에 액세스 한 엔티티가 필요하지만 그 다음에는 @Where가 필요합니다.

  7. ==============================

    7.@vadim_shb의 솔루션을 사용하여 JpaRepository를 확장했으며 여기에 스칼라 코드가 포함되어 있습니다. 그의 대답을 upvote,이 하나가 아닙니다. 페이징과 정렬을 포함하는 예제를 보여 주기만하면됩니다.

    @vadim_shb의 솔루션을 사용하여 JpaRepository를 확장했으며 여기에 스칼라 코드가 포함되어 있습니다. 그의 대답을 upvote,이 하나가 아닙니다. 페이징과 정렬을 포함하는 예제를 보여 주기만하면됩니다.

    페이징 및 정렬은 쿼리 주석과 함께 잘 작동합니다. 모든 것을 테스트하지는 않았지만 페이징 및 정렬에 대해 묻는 사람들은 Query 주석 맨 위에 레이어 된 것처럼 보입니다. 문제가 해결되면 더 자세히 업데이트하겠습니다.

    import java.util
    import java.util.List
    
    import scala.collection.JavaConverters._
    import com.xactly.alignstar.data.model.BaseEntity
    import org.springframework.data.domain.{Page, Pageable, Sort}
    import org.springframework.data.jpa.repository.{JpaRepository, Modifying, Query}
    import org.springframework.data.repository.NoRepositoryBean
    import org.springframework.transaction.annotation.Transactional
    
    @NoRepositoryBean
    trait BaseRepository[T <: BaseEntity, ID <: java.lang.Long] extends JpaRepository[T, ID] {
    
      /* additions */
      @Query("select e from #{#entityName} e where e.isDeleted = true")
      @Transactional(readOnly = true)
      def findInactive: Nothing
    
      @Transactional
      def delete(entity: T): Unit = delete(entity.getId.asInstanceOf[ID])
    
      /* overrides */
      @Query("select e from #{#entityName} e where e.isDeleted = false")
      override def findAll(sort: Sort):  java.util.List[T]
    
      @Query("select e from #{#entityName} e where e.isDeleted = false")
      override def findAll(pageable: Pageable): Page[T]
    
      @Transactional(readOnly = true)
      @Query("select e from #{#entityName} e where e.isDeleted = false")
      override def findAll: util.List[T]
    
      @Transactional(readOnly = true)
      @Query("select e from #{#entityName} e where e.id in :ids and e.isDeleted = false")
      override def findAll(ids: java.lang.Iterable[ID]): java.util.List[T]
    
      @Transactional(readOnly = true)
      @Query("select e from #{#entityName} e where e.id = :id and e.isDeleted = false")
      override def findOne(id: ID): T
    
      @Transactional(readOnly = true)
      @Query("select count(e) from #{#entityName} e where e.isDeleted = false")
      override def count: Long
    
      @Transactional(readOnly = true)
      override def exists(id: ID): Boolean = findOne(id) != null
    
      @Query("update #{#entityName} e set e.isDeleted=true where e.id = :id")
      @Transactional
      @Modifying
      override def delete(id: ID): Unit
    
      @Transactional
      override def delete(entities: java.lang.Iterable[_ <: T]): Unit = {
        entities.asScala.map((entity) => delete(entity))
      }
    
      @Transactional
      @Modifying
      override def deleteInBatch(entities: java.lang.Iterable[T]): Unit = delete(entities)
    
      override def deleteAllInBatch(): Unit = throw new NotImplementedError("This is not implemented in BaseRepository")
    
      @Query("update #{#entityName} e set e.isDeleted=true")
      @Transactional
      @Modifying
      def deleteAll(): Unit
    }
    
  8. from https://stackoverflow.com/questions/19323557/handling-soft-deletes-with-spring-jpa by cc-by-sa and MIT license