親バカエンジニアのナレッジ帳

webのエンジニアをやっており、日頃の開発で詰まったことや書き残しておきたいことを載せています。

Spring BootでSQLクエリを直接実行させる方法

Spring Data JPAについて

みなさんSpring Bootを使用する場合、Spring Data JPAを使用してSQLを記載せずにDBを扱っている場合が多いのではないでしょうか。
Spring Data JPAでは、JpaRepositoryを使用したinterfaceファイルを作成するだけで、Entityの参照や更新が簡単に行えます。
例えばfindAllやfindBy〜などのメソッド使用すればEntityを取得できますし、saveやdeleteBy〜などを実行すればEntityの更新が行えます。

チームとして開発を行う際は、基本的に共通の書き方になるのでメリットは大きいですよね。

SQLを記載したいとき

上記のようにSpring Data JPAにメリットは多いですが、たまに複雑なSQLを実行させたい時もあると思います。
その場合はどうしたら良いのか共有したいと思います。

方法1: EntityManagerを使用

まずはEntityManagerをDIして使用できるようにします。
Queryもimportしておきます。

import javax.persistence.EntityManager;
import javax.persistence.Query;

@Autowired
EntityManager entityManager;

そして以下のようにQuery型の構文を作成し、実行すれば良いです。

// Query型でSQLを作成
Query query = entityManager
.createNativeQuery("SELECT test1 FROM test_table WHERE test_id = :test_id")
.setParameter(test_id, 1);

// SQLを実行
List result = query.getSingleResult();

上記のsetParameterは、変数の値を代入することができるので、条件を絞る場合に使用できます。
getSingleResult()では結果はObject型で取得することになりますが、Queryインターフェースにはまた色々なメソッドがあるので、実行させたい内容によって使い分けるのが良いでしょう。
テーブルを結合させたりするのももちろん大丈夫です。
以下はQueryインターフェースの一部を抜粋しています。

public interface Query {

    // List型で結果を取得
    List getResultList();

    // Object型で結果を取得
    Object getSingleResult();

    // UPDATEを実行し、更新数を取得
    int executeUpdate();

    // 最大値を取得
    int getMaxResults();

    // 最初の行を取得
    Query setFirstResult(int var1);

    // ヒント句を付与
    Query setHint(String var1, Object var2);
}

EntityManagerを使用する方法は、動的SQLを作成したい場合に有効です。
createNativeQueryには文字列をセットすれば良いので、動的にSQLを生成したい場合は最終的に作成されたSQLさえセットできれば実行できます。
条件次第でテーブルの結合有無やカラムの取得有無を切り分けたい場合に使えますね。

方法2: Repositoryで@Queryアノテーションを使用したメソッドを実装

動的SQLを作成したい場合以外は、こちらで書いた方がすっきりするのではないでしょうか。
@Repositoryアノテーションが付与されたRepositoryクラスでメソッドを作成し、@Queryアノテーション内にSQLを記載すれば良いのです。

@Repository
public interface TestEntityRepository extends JpaRepository<TestEntity, Integer> {

	@Query(value = "SELECT test1 FROM test_table " +
		"WHERE test_id = :test_id ", nativeQuery = true)
	List sampleMethod(@Param("test_id") Integer test_id;
}

上記はsampleMethodというメソッド名にしていますが、名前の規則は特にないのでどんなメソッド名でも構いません。
変数は@Paramアノテーションに記載すればOKです。
「nativeQuery = true」も必要なので忘れずに。

あとは呼び出し元で以下のようにすれば実行することができます。

@Autowired
TestEntityRepository testEntityRepository;

(省略)

List result = testEntityRepository.sampleMethod(1);

ちなみに結果をEntityの型で返したい場合、以下のようにEntity名を指定すればEntityの型で取得できます。

@Repository
public interface TestEntityRepository extends JpaRepository<TestEntity, Integer> {

	@Query(value = "SELECT test1 FROM test_table " +
		"WHERE test_id = :test_id ", nativeQuery = true)
	List<TestEntity> sampleMethod(@Param("test_id") Integer test_id;
}

呼び出し元で以下のようになりますね。

List<TestEntity> result = sampleMethod(1);

指定したEntityに存在しないカラムを取得しようとした場合は、そのカラムに関しては取得することができません。