1. 序章

テーブルからデータを物理的に削除することは、データベースを操作する際の一般的な要件です。 ただし、データベースからデータを完全に削除しないというビジネス要件がある場合があります。 これらの要件、たとえば、データ履歴の追跡または監査の必要性、および参照整合性にも関連します。

データを物理的に削除する代わりに、そのデータを非表示にして、アプリケーションのフロントエンドからアクセスできないようにすることができます。

このチュートリアルでは、ソフト削除と、 SpringJPAを使用してこの手法を実装する方法について学習します。

2. ソフト削除とは何ですか?

ソフト削除は、データベース内のテーブルからデータを物理的に削除するのではなく、一部のデータを削除済みとしてマークする更新プロセスを実行します。ソフト削除を実装する一般的な方法は、データが削除されたかどうかを示すフィールドを追加することです。か否か。

たとえば、次の構造の製品テーブルがあるとします。

次に、テーブルからレコードを物理的に削除するときに実行するSQLコマンドを見てみましょう。

delete from table_product where id=1

このSQLコマンドは、 id =1の製品をデータベースのテーブルから完全に削除します。

次に、上記のソフト削除メカニズムを実装しましょう。

と呼ばれる新しいフィールドを追加したことに注意してください削除されました。 このフィールドには値が含まれます 0 また 1

1はデータが削除されたことを示し、0はデータが削除されていないことを示します。 0 をデフォルト値として設定する必要があります。データ削除プロセスごとに、SQL deleteコマンドを実行せず、代わりに次のSQLupdateコマンドを実行します。

update from table_product set deleted=1 where id=1

このSQLコマンドを使用して、実際には行を削除せず、削除済みとしてマークしただけです。 したがって、読み取りクエリを実行するときに、削除されていない行のみが必要な場合は、SQLクエリにフィルターのみを追加する必要があります。

select * from table_product where deleted=0

3. SpringJPAでソフト削除を実装する方法

Spring JPAにより、ソフト削除の実装がはるかに簡単になりました。 この目的のために必要なJPAアノテーションはほんのわずかです。

ご存知のように、JPAでは通常少数のSQLコマンドのみを使用します。 舞台裏でSQLクエリの大部分を作成して実行します。

上記と同じテーブルの例を使用して、SpringJPAにソフト削除を実装しましょう。

3.1. エンティティクラス

最も重要な部分は、エンティティクラスを作成することです。

Productエンティティクラスを作成しましょう。

@Entity
@Table(name = "table_product")
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    private double price;

    private boolean deleted = Boolean.FALSE;

    // setter getter methods
}

ご覧のとおり、デフォルト値がFALSEに設定されたdeletedプロパティを追加しました。

次のステップは、JPAリポジトリーのdeleteコマンドをオーバーライドすることです。

デフォルトでは、JPAリポジトリのdeleteコマンドはSQL削除クエリを実行するため、最初にエンティティクラスにいくつかのアノテーションを追加しましょう。

@Entity
@Table(name = "table_product")
@SQLDelete(sql = "UPDATE table_product SET deleted = true WHERE id=?")
@Where(clause = "deleted=false")
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    private double price;

    private boolean deleted = Boolean.FALSE;
   
    // setter getter method
}

@SQLDeleteアノテーションを使用して削除コマンドをオーバーライドしています。削除コマンドを実行するたびに、実際にはが削除されたフィールド値をtrueに変更するSQL更新コマンドに変換しました ]データを完全に削除する代わりに。

一方、@ Whereアノテーションは、商品データを読み取るときにフィルターを追加します。したがって、上記のコード例によれば、値 selected =trueの商品データ結果には含まれません。

3.2. リポジトリ

リポジトリクラスに特別な変更はありません。SpringBootアプリケーションの通常のリポジトリクラスのように記述できます。

public interface ProductRepository extends CrudRepository<Product, Long>{
    
}

3.3. サービス

サービスクラスについても、まだ特別なことは何もありません。 必要なリポジトリから関数を呼び出すことができます。

この例では、3つのリポジトリ関数を呼び出してレコードを作成してから、ソフト削除を実行してみましょう。

@Service
public class ProductService {
    
    @Autowired
    private ProductRepository productRepository;

    public Product create(Product product) {
        return productRepository.save(product);
    }

    public void remove(Long id){
        productRepository.deleteById(id);
    }

    public Iterable<Product> findAll(){
        return productRepository.findAll();
    }
}

4. 削除されたデータを取得する方法は?

@Where アノテーションを使用すると、削除されたデータにアクセスできるようにしたい場合に備えて、削除された製品データを取得できません。 この例は、フルアクセス権を持ち、「削除」されたデータを表示できる管理者レベルのユーザーです。

これを実装するには、@Whereアノテーションではなく、@FilterDefと@Filterの2つの異なるアノテーションを使用する必要があります。 これらのアノテーションを使用すると、必要に応じて条件を動的に追加できます。

@Entity
@Table(name = "tbl_products")
@SQLDelete(sql = "UPDATE tbl_products SET deleted = true WHERE id=?")
@FilterDef(name = "deletedProductFilter", parameters = @ParamDef(name = "isDeleted", type = "boolean"))
@Filter(name = "deletedProductFilter", condition = "deleted = :isDeleted")
public class Product {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    private double price;

    private boolean deleted = Boolean.FALSE;
}

ここで、 @FilterDef アノテーションは、@ Filterアノテーションによって使用される基本的な要件を定義します。 さらに、動的パラメーターまたはフィルターを処理するために、 ProductServiceサービスクラスのfindAll()関数も変更する必要があります。

@Service
public class ProductService {
    
    @Autowired
    private ProductRepository productRepository;

    @Autowired
    private EntityManager entityManager;

    public Product create(Product product) {
        return productRepository.save(product);
    }

    public void remove(Long id){
        productRepository.deleteById(id);
    }

    public Iterable<Product> findAll(boolean isDeleted){
        Session session = entityManager.unwrap(Session.class);
        Filter filter = session.enableFilter("deletedProductFilter");
        filter.setParameter("isDeleted", isDeleted);
        Iterable<Product> products =  productRepository.findAll();
        session.disableFilter("deletedProductFilter");
        return products;
    }
}

ここでは、Productエンティティの読み取りプロセスに影響を与えるオブジェクトFilterに追加するisDeletedパラメーターを追加します。

5. 結論

SpringJPAを使用してソフト削除手法を実装するのは簡単です。 行が削除されたかどうかを格納するフィールドを定義する必要があります。 次に、その特定のエンティティクラスで @SQLDelete アノテーションを使用して、削除コマンドをオーバーライドする必要があります。

If we want more control, we can use the @FilterDef and @Filter annotations so we can determine if query results should include deleted data or not.

この記事のすべてのコードは、GitHubから入手できます。