Spring CRUDの基本

データの作成(Create)、検索(Read)、更新(Update)、削除(Delete)は、データベースアクセスの4つの基本操作と言えます。ここでは、これらの基本操作について説明します。

Queryでデータ一覧を取得する

データベース操作の基本はCRUDと呼ばれます。以前、いくつかのダミーデータを保存し、データ一覧を表示する例を作成しました。

参考: Spring Data JPAの利用 - データベースを使ってみよう。

例を見ると、CreateとReadの基本はすでに作成されているはずです。では、どのように行ったのか確認してみましょう。

package com.devkuma.spring.db;
 
import java.util.List;
 
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Query;
 
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
 
public class App {
 
    public static void main(String[] args) {
        ApplicationContext app = new
            AnnotationConfigApplicationContext(SampleEntityConfig.class);
 
        EntityManagerFactory factory = app.getBean(EntityManagerFactory.class);
        EntityManager manager = factory.createEntityManager();
 
        makeDummyData(manager);
 
        // get list
        Query query = manager.createQuery("from SampleEntity");
        List list = query.getResultList();
        printList(list);
 
        System.out.println("...ok.");
    }
 
    public static void makeDummyData(EntityManager manager) { ...省略... }
 
    public static void printList(List list) { ...省略... }
}

まずはデータ一覧の取得です。ここではQueryというクラスを利用しています。

Query query = manager.createQuery("from SampleEntity");

これはQueryというクラスのインスタンスを作成し、そのメソッドを呼び出してエンティティ一覧全体を検索します。Queryとは、SQLクエリ文を簡略化したような文を使ってデータベースへアクセスするためのものです。

インスタンスの作成にはnewではなく、createQueryというメソッドを利用します。引数にはfrom SampleEntityというテキストを入れました。これがSampleEntityエンティティをすべて取得することを表す構文です。

List list = query.getResultList();

このように作成したQuerygetResultListを呼び出すと、取得したエンティティをリストとして得られます。その後、結果リストから順番にエンティティを取り出して処理すればよいです。

createQueryの引数にどのようなテキストを指定すると、どのような結果が得られるのかが一番のポイントです。まずここでは、from エンティティという形で書くと、そのエンティティ一覧を取得できることだけ覚えておきましょう。

新しいエンティティの追加

続いて、新規エンティティを保存します。考え方としては非常に簡単です。エンティティクラスのインスタンスを作成し、EntityManagerにある保存用メソッドを呼び出すだけです。

ただし、データベース操作は、単にデータを読むだけなら簡単ですが、データを更新する場合には注意が必要です。データベースは同時に複数の場所からアクセスされることがあります。そのため、他のデータアクセス中にデータが更新されると問題が起こる可能性があります。

したがって、データの更新が必要な場合はトランザクションを使用する必要があります。トランザクションは、複数の処理をまとめて実行できるようにする機能です。

トランザクションを指定している間は、その処理を行っている対象データに対して、外部からアクセスできなくなります。トランザクション処理が完了した後は、外部からデータへアクセスできるようになります。

package com.devkuma.spring.db;
 
import java.util.List;
 
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Query;
 
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
 
public class App {
 
    public static void main(String[] args) {
        ApplicationContext app = new
            AnnotationConfigApplicationContext(SampleEntityConfig.class);
 
        EntityManagerFactory factory = app.getBean(EntityManagerFactory.class);
        EntityManager manager = factory.createEntityManager();
 
        makeDummyData(manager);
 
        // get list
        Query query = manager.createQuery("from SampleEntity");
        List list = query.getResultList();
        printList(list);
 
        System.out.println("...ok.");
    }
 
    // create dummy entity data.
    public static void makeDummyData(EntityManager manager) {
        EntityTransaction transaction = manager.getTransaction();
        transaction.begin();
        manager.persist(new SampleEntity("tuyano", "syoda@tuyano.com"));
        manager.persist(new SampleEntity("hanako", "hanako@flower"));
        manager.persist(new SampleEntity("taro", "taro@yamada"));
        manager.persist(new SampleEntity("sachiko", "sachico@happy"));
        manager.flush();
        transaction.commit();
    }
 
    public static void printList(List list) { ...省略... }
}

トランザクションによって特定の作業を実行している間、外部からアクセスできないようにし、安全にデータベースを更新できるということです。では、処理の流れを見てみましょう。

EntityTransaction transaction = manager.getTransaction ();

EntityManagergetTransactionは、EntityTransactionというクラスのインスタンスを取得します。これがトランザクションクラスです。

transaction.begin ();

beginメソッドを実行して、トランザクション処理を開始します。

manager.persist(new SampleEntity ( "tuyano", "syoda@tuyano.com"));
manager.persist(new SampleEntity ( "hanako", "hanako @ flower"));
manager.persist(new SampleEntity ( "taro", "taro @ yamada"));
manager.persist(new SampleEntity ( "sachiko", "sachico @ happy"));

エンティティの新規追加には、EntityManagerpersistメソッドを使用します。引数にエンティティクラスのインスタンス、ここではSampleEntityを指定して呼び出すことで、そのエンティティをデータベースへ保存します。

manager.flush ();
transaction.commit ();

最後にEntityManagerflushを呼び出してバッファを空にし、EntityTransactioncommitメソッドを呼び出してコミットすると、persistしたエンティティがすべてデータベースへ保存されます。コミットするとすぐにトランザクション処理は終了し、データベースは開放されます。

データ追加では、トランザクションの使い方を理解しておく必要があります。これはデータベース更新の基本なので、ここで基本的な使い方を必ず覚えておきましょう。

エンティティの修正

続いて、エンティティの変更です。すでにデータベースに保存されているデータを更新するには、該当データのエンティティを取得し、その値を変更して保存する作業が必要です。

次のような簡単な例を用意しました。

package com.devkuam.spring.db;
 
import java.util.List;
 
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Query;
 
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
 
public class App {
    private static EntityManager manager;
 
    public static void main(String[] args) {
        ApplicationContext app = new
            AnnotationConfigApplicationContext(SampleEntityConfig.class);
 
        EntityManagerFactory factory = app.getBean(EntityManagerFactory.class);
        manager = factory.createEntityManager();
 
        // make dummy
        makeDummyData();
         
        // update entity
        updateEntity(1L);
        updateEntity(3L);
 
        // get list
        Query query = manager.createQuery("from SampleEntity");
        List list = query.getResultList();
        printList(list);
 
        System.out.println("...ok.");
    }
 
    public static void makeDummyData() { ...省略... }
    public static void printList(List list) { ...省略... }
     
    // update entity
    public static void updateEntity(long id) {
        SampleEntity entity = manager.find(SampleEntity.class, id);
        entity.setName("**update name**");
        entity.setMail("**update@mail**");
        EntityTransaction transaction = manager.getTransaction();
        transaction.begin();
        manager.merge(entity);
        manager.flush();
        transaction.commit();
    }
}

実行すると、4つのダミーデータのうち2つの内容を変更して出力します。

変更処理はupdateEntityメソッドにまとめています。変更するデータのID番号を引数として渡せるようになっています。

では、処理の流れを見てみましょう。

EntityManagerFactory factory = app.getBean(EntityManagerFactory.class);
manager = factory.createEntityManager();

まず、EntityManagerインスタンスを取得し、それを使用して処理しています。

SampleEntity entity = manager.find(SampleEntity.class, id);

SampleEntityインスタンスを取得します。これはnewで新しく作るのではなく、EntityManagerfindメソッドを呼び出します。findは特定IDのエンティティを検索するものです。引数には、エンティティクラスのclass値と、検索するエンティティのID値をそれぞれ指定します。

最初に一覧リストを取得してから処理することを考えると面倒ですが、このようにEntityManagerのメソッドを使用すれば、IDを指定するだけでエンティティを取得できます。ただし、IDがわからない状態では使用できません。

次に、取得したエンティティのメソッドを呼び出して内容を更新します。

entity.setName("** update name **");
entity.setMail("** update @ mail **");

編集が終わったら、新規作成と同じようにトランザクションを使用して更新処理を行います。これは基本的に新規作成の流れと同じですが、更新する部分だけが異なります。

EntityTransaction transaction = manager.getTransaction ();
transaction.begin ();
manager.merge (entity);
manager.flush ();
transaction.commit ();

すでに保存されているエンティティの変更は、mergeというメソッドを使って行います。引数に更新するエンティティクラスのインスタンスを指定すると、そのエンティティの値が最新の状態へ更新されます。

その後、EntityManagerflushし、EntityTransactioncommitすれば作業は完了です。更新は新規作成と異なり、findで特定IDのエンティティを取得してから、mergeで更新処理を行えばよいです。

エンティティの削除

残る操作はエンティティの削除です。

削除も基本的にはエンティティの更新と同じです。IDを指定してエンティティのインスタンスを取得し、削除してトランザクションをコミットする流れです。

次のような簡単な例を用意しました。

package com.devkuma.spring.db;
 
import java.util.List;
 
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Query;
 
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
 
public class App {
    private static EntityManager manager;
 
    public static void main(String[] args) {
        ApplicationContext app = new
            AnnotationConfigApplicationContext(SampleEntityConfig.class);
 
        EntityManagerFactory factory = app.getBean(EntityManagerFactory.class);
        manager = factory.createEntityManager();
 
        // make dummy
        makeDummyData();
         
        // delete entity
        deleteEntity(1L);
        deleteEntity(3L);
 
        // get list
        Query query = manager.createQuery("from SampleEntity");
        List list = query.getResultList();
        printList(list);
 
        System.out.println("...ok.");
    }
 
    public static void makeDummyData() { 省略 }
    public static void printList(List list) { 省略 }
     
    // delete entity
    public static void deleteEntity(long id) {
        SampleEntity entity = manager.find(SampleEntity.class, id);
        EntityTransaction transaction = manager.getTransaction();
        transaction.begin();
        manager.remove(entity);
        manager.flush();
        transaction.commit();
    }
}

実行すると、ID=1ID=3の要素を削除し、残りの2つを表示します。では、これも流れを整理してみましょう。

SampleEntity entity = manager.find (SampleEntity.class, id);

まず、EntityManagerfindを使用して、指定されたIDのエンティティ、ここではSampleEntityを取得します。

EntityTransaction transaction = manager.getTransaction ();
transaction.begin ();

続いて、EntityTransactionインスタンスを取得してトランザクション処理を開始します。

manager.remove(entity);

エンティティを削除します。これはEntityManagerremoveメソッドを使用します。引数にデータベースから検索したエンティティのインスタンスを指定すると、データベースからそのエンティティを削除します。

manager.flush();
transaction.commit();

最後にEntityManagerflushし、EntityTransactioncommitすると、データベース更新が完了してデータが削除されます。エンティティの変更がわかれば、削除もほぼ同じ感覚で実行できます。

JPQLクエリ文の基本

これでCRUDの基本操作はできるようになりました。ここでもう少し知っておくべきなのは、基本中の基本であるReadの部分です。

前の例では、すべてのエンティティを取得する簡単なサンプルがありました。しかし実際の処理では、より細かい条件を指定してエンティティを検索する必要があるでしょう。

エンティティを取得するとき、Queryというクラスを使用しました。このQueryインスタンスを作成するとき、from SampleEntityという文を引数に入れました。この文はSQLクエリ文と同じような機能を持つもので、JPQLと呼ばれます。ただし、SQLとは少し書き方が異なります。まず、エンティティ検索の基本的な書き方について説明します。

JPQL検索の基本

from エンティティ where 条件式

SQLでもおなじみのwhere句を使用すれば、基本的な検索はほぼ行えます。条件となる式は、等号などの比較演算子(=<>)を使用して作成します。

IDが1のエンティティを返す

from SampleEntity where id = 1

IDが2より大きいエンティティを返す

from SampleEntity where id > 2

また、テキストを検索するには、あいまい検索のlikeを使用できます。%記号を使ってワイルドカードを指定することも可能です。

nameが’K’で始まるエンティティを返す

from SampleEntity where name like 'K %'

複数の条件式を設定するには、andorを使用できます。whereでは、条件式 and 条件式のような形式で複数の条件式をandorでつなげます。

idが10以上20以下のエンティティを取得

from SampleEntity where id> = 10 and id <= 20

まずは、これらの基本的な検索方法を理解しておけば、初歩的なデータ検索処理はおおむねできるようになるでしょう。実際にJPQLクエリ文を作成して実行してみると面白いです。