NamedParameterJdbcTemplateの使用方法をサンプルコードで解説!!

この記事は約55分で読めます。
NamedParameterJdbcTemplateアイキャッチ

JavaのSpring bootで、DBと連携する方法は、
Spring Data JDBC、Spring Data JPA、JdbcTemplate、NamedParamaterJdbcTemplateなどがあります。

今回は、その中のNamedParamaterJdbcTemplateの使用方法をサンプルコードを用いて
ご紹介します。

NamedParamaterJdbcTemplateは、Spring Data JDBCやSpring Data JPA
と違い、SQL文を直書きできるため、複雑なSQLの実行が可能です。

なお、使用例についてはJUnitを用い、詳細説明はコード内の
コメントを用いてしていきます。
使用したコードについては、以下のGitHubにも載せています。

GitHub - tkmttkm/SQL
Contribute to tkmttkm/SQL development by creating an account on GitHub.

GitHubよりソースをダウンロードし、エクリプスで見た方が
見やすいかもしれません。



また、NamedJdbcTemplateと非常によく似ている、
JdbcTemplateの使用方法についても以下で解説しています。

僕としては、NamedJdbcTemplateの方が使いやすい方とは思いますが、
興味があれば、ご覧ください。

環境・定義

build.gradle

plugins {
	id 'java'
	id 'org.springframework.boot' version '3.2.0'
	id 'io.spring.dependency-management' version '1.1.4'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'

java {
	sourceCompatibility = '17'
}

configurations {
	compileOnly {
		extendsFrom annotationProcessor
	}
}

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.3'
	compileOnly 'org.projectlombok:lombok'
	runtimeOnly 'com.h2database:h2'
	runtimeOnly 'org.bgee.log4jdbc-log4j2:log4jdbc-log4j2-jdbc4:1.16'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testImplementation 'org.junit.jupiter:junit-jupiter:5.5.2'
}

tasks.named('test') {
	useJUnitPlatform()
}

DBはh2データベースを用い、
Getter, Setterの生成はLomocを用います。

application.properties

spring.datasource.driver-class-name=net.sf.log4jdbc.sql.jdbcapi.DriverSpy
spring.datasource.url=jdbc:log4jdbc:h2:mem:hogedb
spring.datasource.username=sa
spring.datasource.password=

spring.jpa.hibernate.ddl-auto=validate
spring.sql.init.mode=always
spring.sql.init.schema-locations=classpath:schema.sql
spring.sql.init.data-locations=classpath:data.sql

log4jdbc.log4j2.properties

log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator

schema.sql

CREATE TABLE test_table
(
  id INT NOT NULL AUTO_INCREMENT,
  first_name VARCHAR(10) NOT NULL,
  last_name VARCHAR(10) NOT NULL,
  birth_day INT NULL,
  PRIMARY KEY(id)
);

data.sql

INSERT INTO test_table(id,first_name, last_name, birth_day)
VALUES(1,'テスト', '太郎', 20240101);
INSERT INTO test_table(id,first_name, last_name, birth_day)
VALUES(2,'テスト', '二郎', 20240101);
INSERT INTO test_table(id,first_name, last_name, birth_day)
VALUES(3,'テスト', '三郎', 20240101);
INSERT INTO test_table(id,first_name, last_name, birth_day)
VALUES(4,'テスト', '花子', 20250101);

ファイル階層

  • /
    • src/
      • main/
        • java/
          • com/example/demo/
            • Entity/
              • JDBCEntity.java
            • Dao/
              • NamedJDBCTempDao.java
              • RowMapper/
                • JDBCEntityRowMapper.java
        • resources/
          • application.properties
          • log4jdbc.log4j2.properties
          • shema.sql
          • data.sql
          • ……
      • test/
        • java/com/example/demo/Dao/
          • NamedJDBCTempDaoTest.java
    • build.gradle
    • ……

サンプルコード

JDBCEntity.java

package com.example.demo.Entity;

import java.util.ArrayList;
import java.util.List;

import lombok.AllArgsConstructor;
import lombok.Getter;

/**
 * <pre>
 * テーブルtest_tableのマッピングクラス
 * </pre>
 * @author Takumi
 */
@Getter
@AllArgsConstructor
public class JDBCEntity {
 
	//カラム名
	/** プライマリキー */
	private int id;
	private String first_name;
	private String last_name;
	private int birth_day;

	//カラム名の文字列
	public static final String TEST = "test_table";
	public static final String ID = "id";
	public static final String FIRST_NAME = "first_name";
	public static final String LAST_NAME = "last_name";
	public static final String BIRTHDAY = "birth_day";

	/**
	 * <pre>
	 * バッチアップデートなどで、カラム名と挿入したい値の順番を連携するために使用
	 * 同じリストを用いることで順番を対応づける
	 * </pre>
	 * @return
	 */
	public static List<String> GetSetQueryList_forBatchUpdate() {
		List<String> setQueryList = new ArrayList<String>();

		setQueryList.add(JDBCEntity.ID);
		setQueryList.add(JDBCEntity.FIRST_NAME);
		setQueryList.add(JDBCEntity.LAST_NAME);
		setQueryList.add(JDBCEntity.BIRTHDAY);

		return setQueryList;
	}
}

JDBCEntityRowMapper.java

package com.example.demo.Dao.RowMapper;

import java.sql.ResultSet;
import java.sql.SQLException;

import org.springframework.jdbc.core.RowMapper;

import com.example.demo.Entity.JDBCEntity;

/**
 * @author Takumi
 * <pre>
 * {@link JDBCEntity}のフィールドとマッピングをするためのクラス
 * {@link #mapRow(ResultSet, int)}で対応するカラムと値を設定する({@code override}使用
 * </pre>
 */
public class JDBCEntityRowMapper implements RowMapper<JDBCEntity> {

	/**
	 * <pre>
	 * 引数にテーブルのカラム名を渡し、型を合わせることで
	 * マッピングされる
	 * </pre>
	 */
	@Override
	public JDBCEntity mapRow(ResultSet rs, int rowNum) throws SQLException {
		return new JDBCEntity(
				rs.getInt(JDBCEntity.ID),
				rs.getString(JDBCEntity.FIRST_NAME),
				rs.getString(JDBCEntity.LAST_NAME),
				rs.getInt(JDBCEntity.BIRTHDAY)
				);
	}

}

NamedJDBCDao.java

package com.example.demo.Dao;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.namedparam.EmptySqlParameterSource;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.core.namedparam.SqlParameterSourceUtils;
import org.springframework.stereotype.Repository;
import org.springframework.util.CollectionUtils;

import com.example.demo.Dao.RowMapper.JDBCEntityRowMapper;
import com.example.demo.Entity.JDBCEntity;

/**
 * @author Takumi
 * <pre>
 * {@link NamedParameterJdbcTemplate}を使用して
 * DB接続するクラス
 * :パラメータ名 を設定し、
 * {@link MapSqlParameterSource}などを使用することで、
 * パラメータをマッピング
 *</pre>
 */
@Repository
public class NamedJDBCDao {

	@Autowired
	private NamedParameterJdbcTemplate namedJdbc;

	/**
	 * <pre>
	 * テーブルのデータを{@code List<Map<String, Object>>}ですべて取得。
	 * {@code Map<String, Object>}のkeyはカラム名、valueは取得データ
	 * {@link NamedParameterJdbcTemplate#queryForList(String, SqlParameterSource)}使用
	 * </pre>
	 * @return {@link JDBCEntity#TEST TEST}テーブルのデータを{@code List<Map<String, Object>>}で取得
	 * @throws DataAccessException
	 */
	public List<Map<String, Object>> findAll() throws DataAccessException {
		try {
			List<String> sqlList = new ArrayList<String>();

			sqlList.add("SELECT");
			sqlList.add("*");
			sqlList.add("FROM");
			sqlList.add(JDBCEntity.TEST);

			String sql = String.join(" ", sqlList);

			EmptySqlParameterSource empty = new EmptySqlParameterSource();

			return namedJdbc.queryForList(sql, empty);
		} catch (DataAccessException e) {
			System.err.println(e.getMessage() + "¥r¥n"
					+ e.getStackTrace());
			throw e;
		}
	}

	/**
	 * <pre>
	 * 取得したいデータを
	 * 引数にid(プライマリキー)を渡すことで、取得
	 * 型は{@code Map<String, Object>}でkeyはカラム名、valueは取得データ
	 * {@link NamedParameterJdbcTemplate#queryForMap(String, SqlParameterSource)}使用
	 * </pre>
	 * @param id 取得したいデータおid(プライマリキー)
	 * @return idを渡したデータの{@code Map<String, Object>}
	 * @throws DataAccessException
	 */
	public Map<String, Object> findById(int id) throws DataAccessException {
		try {
			List<String> sqlList = new ArrayList<String>();

			sqlList.add("SELECT");
			sqlList.add("*");
			sqlList.add("FROM");
			sqlList.add(JDBCEntity.TEST);
			sqlList.add("WHERE");
			sqlList.add(JDBCEntity.ID + " = :" + JDBCEntity.ID);

			String sql = String.join(" ", sqlList);

			MapSqlParameterSource params = new MapSqlParameterSource();
			params.addValue(JDBCEntity.ID, id);

			return namedJdbc.queryForMap(sql, params);
		} catch (DataAccessException e) {
			System.err.println(e.getMessage() + "¥r¥n"
					+ e.getStackTrace());
			return new HashMap<String, Object>();
		}
	}

	/**
	 * <pre>
	 * 指定したid(プライマリキー)のデータを更新する
	 * 引数にid(プライマリキー)とkeyにカラム名、valueに更新したい値を入れた{@code Map<String, String}を渡すことで
	 * テーブルを更新する
	 * {@link NamedParameterJdbcTemplate#update(String, SqlParameterSource)}使用
	 * </pre>
	 * @param id
	 * @param updateDataMap
	 * @return 更新数
	 * @throws DataAccessException
	 */
	public int updateById(int id, Map<String, String> updateDataMap) throws DataAccessException {
		try {
			List<String> sqlList = new ArrayList<String>();
			MapSqlParameterSource params = new MapSqlParameterSource();
			params.addValue(getUniqueKey(updateDataMap, JDBCEntity.ID), id);

			sqlList.add("UPDATE");
			sqlList.add(JDBCEntity.TEST);
			sqlList.add("SET");
			sqlList.add(updateSet(updateDataMap, params));
			sqlList.add("WHERE");
			sqlList.add(JDBCEntity.ID + " = :" + JDBCEntity.ID);

			String sql = String.join(" ", sqlList);

			return namedJdbc.update(sql, params);
		} catch (DataAccessException e) {
			System.err.println(e.getMessage() + "¥r¥n"
					+ e.getStackTrace());
			throw e;
		}
	}
	
	/**
	 * <pre>
	 * データを挿入する
	 * 引数に{@code Map<STring, String>} key:カラム名 value:挿入したい値
	 * を渡すことでデータを挿入する
	 * {@link NamedParameterJdbcTemplate#update(String, SqlParameterSource)}使用
	 * </pre>
	 * @param insertDataMap key:カラム名 value:挿入したい値
	 * @return 挿入数
	 * @throws DataAccessException
	 */
	public int insert(Map<String, String> insertDataMap) throws DataAccessException {
		try {
			MapSqlParameterSource params = new MapSqlParameterSource();

			List<String> sqlList = new ArrayList<String>();
			sqlList.add("INSERT INTO");
			sqlList.add(JDBCEntity.TEST);
			sqlList.add(insertSet(insertDataMap, params));
			String sql = String.join(" ", sqlList);

			return namedJdbc.update(sql, params);
		} catch (DataAccessException e) {
			System.err.println(e.getMessage() + "\r\n"
					+ e.getStackTrace());
			throw e;
		}
	}

	/**
	 * <pre>
	 * 引数に削除したいデータのid(プライマリキー)を渡すことでデータを削除する
	 * {@link NamedParameterJdbcTemplate#update(String, SqlParameterSource)}使用
	 * </pre>
	 * @param id 削除したいデータのid
	 * @return 削除数
	 * @throws DataAccessException
	 */
	public int deleteById(int id) throws DataAccessException {
		try {
			List<String> sqlList = new ArrayList<String>();
			MapSqlParameterSource params = new MapSqlParameterSource();
			params.addValue(JDBCEntity.ID, id);

			sqlList.add("DELETE");
			sqlList.add(JDBCEntity.TEST);
			sqlList.add("WHERE");
			sqlList.add(JDBCEntity.ID + " = :" + JDBCEntity.ID);

			String sql = String.join(" ", sqlList);

			return namedJdbc.update(sql, params);
		} catch (DataAccessException e) {
			System.err.println(e.getMessage() + "¥r¥n"
					+ e.getStackTrace());
			throw e;
		}
	}

	/**
	 * <pre>
	 * データの一括更新
	 * 引数に更新したいデータの{@code List<JDBCEntity>}を渡すことで
	 * データを一括更新する
	 * {@link NamedParameterJdbcTemplate#batchUpdate(String, SqlParameterSource[])}使用
	 * </pre>
	 * @param updateList 更新したいデータのリスト
	 * @return 更新数
	 * @throws DataAccessException
	 */
	public int batchUpdate(List<JDBCEntity> updateList) throws DataAccessException {
		SqlParameterSource[] params = SqlParameterSourceUtils.createBatch(updateList);

		List<String> sqlList = new ArrayList<String>();

		sqlList.add("UPDATE");
		sqlList.add(JDBCEntity.TEST);
		sqlList.add("SET");
		sqlList.add(batchUpdateSet());
		sqlList.add("WHERE");
		sqlList.add(JDBCEntity.ID + " = :" + JDBCEntity.ID);
		
		String sql = String.join(" ", sqlList);
		
		int[] batchUpdate = namedJdbc.batchUpdate(sql, params);
		int returnCount = 0;
		for(int count : batchUpdate) {
			returnCount += count;
		}
		
		return returnCount;
	}
	
	/**
	 * <pre>
	 * データの一括挿入
	 * 引数に挿入したいデータの{@code List<JDBCEntity>}を渡すことで、データを一括挿入する
	 * {@link NamedParameterJdbcTemplate#batchUpdate(String, SqlParameterSource[])}使用
	 * </pre>
	 * @param insertList 挿入したいデータのリスト
	 * @return 挿入数
	 * @throws DataAccessException
	 */
	public int batchInsert(List<JDBCEntity> insertList) throws DataAccessException {
		try {
			SqlParameterSource[] params = SqlParameterSourceUtils.createBatch(insertList);

			List<String> sqlList = new ArrayList<String>();

			sqlList.add("INSERT INTO");
			sqlList.add(JDBCEntity.TEST);
			sqlList.add(batchInsertSet());

			String sql = String.join(" ", sqlList);

			int[] batchUpdate = namedJdbc.batchUpdate(sql, params);
			int returnCount = 0;
			for (int count : batchUpdate) {
				returnCount += count;
			}

			return returnCount;
		} catch (DataAccessException e) {
			System.err.println(e.getMessage() + "\r\n" + e.getStackTrace());
			throw e;
		}
	}
	
	/**
	 * <pre>
	 * データの一括削除
	 * 引数に削除したいデータの{@code List<JDBCEntity>}を渡すことでデータを一括削除する
	 * この引数にセットする値はid(プライマリキー)のみでOK
	 * {@link NamedParameterJdbcTemplate#batchUpdate(String, SqlParameterSource[])}使用
	 * </pre>
	 * @param deleteList 削除したいデータのリスト(セットする値はidにみでOK)
	 * @return 削除数
	 */
	public int batchDelete(List<JDBCEntity> deleteList) {
		SqlParameterSource[] params = SqlParameterSourceUtils.createBatch(deleteList);

		List<String> sqlList = new ArrayList<String>();

		sqlList.add("DELETE");
		sqlList.add(JDBCEntity.TEST);
		sqlList.add("WHERE");
		sqlList.add(JDBCEntity.ID + " = :" + JDBCEntity.ID);
		
		String sql = String.join(" ", sqlList);
		
		int[] batchUpdate = namedJdbc.batchUpdate(sql, params);
		int returnCount = 0;
		for(int count : batchUpdate) {
			returnCount += count;
		}
		
		return returnCount;
	}
	
	/**
	 * <pre>
	 * テーブルないのデータを{@code List<JDBCEntity>}で取得。
	 * {@link NamedParameterJdbcTemplate#query(String, org.springframework.jdbc.core.RowMapper)}使用
	 * </pre>
	 * @return テーブル内の全データ
	 * @throws DataAccessException
	 */
	public List<JDBCEntity> getAllJDBCEntity() throws DataAccessException {
		try {
			List<String> sqlList = new ArrayList<String>();

			sqlList.add("SELECT");
			sqlList.add("*");
			sqlList.add("FROM");
			sqlList.add(JDBCEntity.TEST);

			String sql = String.join(" ", sqlList);

			return namedJdbc.query(sql, new JDBCEntityRowMapper());
		} catch (DataAccessException e) {
			System.err.println(e.getMessage() + "\r\n" + e.getStackTrace());
			throw e;
		}
	}
	
	/**
	 * <pre>
	 * 引数に取得したいデータのid(プライマリキー)を渡すことで
	 * データを{@code List<JDBCEntity>}で取得
	 * {@link NamedParameterJdbcTemplate#query(String, org.springframework.jdbc.core.RowMapper)}使用
	 * </pre>
	 * @param id 取得したいデータのid
	 * @return 渡したidのデータ
	 * @throws DataAccessException
	 */
	public JDBCEntity getJDBCEntityById(int id) throws DataAccessException {
		try {
			MapSqlParameterSource params = new MapSqlParameterSource();
			params.addValue(JDBCEntity.ID, id);
			
			List<String> sqlList = new ArrayList<String>();

			sqlList.add("SELECT");
			sqlList.add("*");
			sqlList.add("FROM");
			sqlList.add(JDBCEntity.TEST);
			sqlList.add("WHERE");
			sqlList.add(JDBCEntity.ID + " = :" + JDBCEntity.ID);

			String sql = String.join(" ", sqlList);

			List<JDBCEntity> data = namedJdbc.query(sql, params, new JDBCEntityRowMapper());
			return data.get(0);
		} catch (DataAccessException e) {
			System.err.println(e.getMessage() + "\r\n" + e.getStackTrace());
			throw e;
		}
	}
	
	/**
	 * <pre>
	 * {@link NamedParameterJdbcTemplate#getJdbcOperations()#executeDrop(String)}を使用してテーブルを作成する
	 * 引数に作成したいテーブル名、作成したいテーブル名と型、NULL許容などの{@code Map<String, String>}、プライマリキーに設定したい{@code List<String>}を渡す
	 * ※SQLインジェクション対策ができないため推奨はできない
	 * </pre>
	 * @param tableName テーブル名
	 * @param column_columnInfo カラム名とその型やNULL許容などのマップ
	 * @param primaryKeyList プライマリキーのから無名を詰めたリスト
	 * @throws DataAccessException
	 */
	public void executeCreate(String tableName, Map<String, String> column_columnInfo, List<String> primaryKeyList) throws DataAccessException {
		try {
			List<String> sqlList = new ArrayList<>();
			
			sqlList.add("CREATE TABLE");
			sqlList.add(tableName);
			sqlList.add("(");
			
			List<String> columnList = new ArrayList<String>();
			for(var keyValue : column_columnInfo.entrySet()) {
				columnList.add(keyValue.getKey() + " " + keyValue.getValue());
			}
			sqlList.add(String.join(", ", columnList));
			
			if(!CollectionUtils.isEmpty(primaryKeyList)) {
				sqlList.add(", PRIMARY KEY (");
				sqlList.add(String.join(", ", primaryKeyList));
				sqlList.add(")");
			}
			
			sqlList.add(")");
			
			namedJdbc.getJdbcOperations().execute(String.join(" ", sqlList));
		} catch (DataAccessException e) {
			System.err.println(e.getMessage() + "r\n" + e.getStackTrace());
			throw e;
		}
	}
	
	/**
	 * <pre>
	 * {@link NamedParameterJdbcTemplate#getJdbcOperations()#executeDrop(String)}を用いてテーブルを削除する
	 * 引数に削除したいテーブルの名前を渡す
	 * ※SQLインジェクション対策ができないため推奨できない
	 * </pre>
	 * @param tableName テーブル名
	 * @throws DataAccessException
	 */
	public void executeDrop(String tableName) throws DataAccessException {
		try {
			MapSqlParameterSource params = new MapSqlParameterSource();
			params.addValue("tableName", tableName);
			List<String> sqlList = new ArrayList<>();
			
			sqlList.add("DROP TABLE");
			sqlList.add(":tableName");
			
			namedJdbc.getJdbcOperations().execute(String.join(" ", sqlList));
		} catch (DataAccessException e) {
			System.err.println(e.getMessage() + "r\n" + e.getStackTrace());
			throw e;
		}
	}

	/**
	 * <pre>
	 * カラム名とその値のMapをイコールでつなぐ
	 * </pre>
	 * @param map
	 * @return カラム名 = 値, カラム名 = 値, ....
	 */
	private List<String> joinEqual(Map<String, String> map) {
		List<String> list = new ArrayList<String>();

		for (var entry : map.entrySet()) {
			list.add(entry.getKey() + " = " + entry.getValue());
		}

		return list;
	}

	/**
	 * <pre> 
	 * リストにつめた文字列をカンマでつなぐ
	 * </pre>
	 * @param list
	 * @return 値, 値, 値, ...
	 */
	private String joinComma(List<String> list) {
		return String.join(", ", list);
	}

	/**
	 * <pre>
	 * UPDATEのSET句を作成する
	 * {@code Map<String, Object>}key:カラム名 value:更新したい値
	 * を key:カラム名 value: :カラム名
	 * に変更する
	 * </pre>
	 * @param map key:カラム名 value:更新したい値
	 * @return カラム名 = :カラム名, カラム名 = :カラム名, ....
	 */
	private String updateSet(Map<String, String> map, MapSqlParameterSource params) {
		for (var entry : map.entrySet()) {
			params.addValue(entry.getKey(), entry.getValue());
			map.replace(entry.getKey(), ":" + entry.getKey());
		}

		return joinComma(joinEqual(map));
	}
	
	/**
	 * <pre>
	 * インサート文の(...) VALUES (...)を作成する
	 * </pre>
	 * @param column_valueMap keu:カラム名 value:値
	 * @return (カラム名, カラム名, ...) VALUES (:カラム名, :カラム名, :カラム名, ...)
	 */
	private String insertSet(Map<String, String> column_valueMap, MapSqlParameterSource params) {
		String[] columnArray = new String[column_valueMap.size()];
		String[] valueArray = new String[column_valueMap.size()];
		
		int index = 0;
		for(var column_value : column_valueMap.entrySet()) {
			columnArray[index] = column_value.getKey();
			valueArray[index] = ":" + column_value.getKey();
			params.addValue(column_value.getKey(), column_value.getValue());
			index++;
		}
		
		List<String> insertSqlList = new ArrayList<>();
		insertSqlList.add("(");
		insertSqlList.add(String.join(", ", columnArray));
		insertSqlList.add(") VALUES (");
		insertSqlList.add(String.join(", ", valueArray));
		insertSqlList.add(")");
		
		return String.join(" ", insertSqlList);
	}

	/**
	 * <pre>
	 * パラメーターの被りが生じないために採番をつける 
	 * </pre>
	 * @param map
	 * @param key
	 * @return
	 */
	private String getUniqueKey(Map<String, String> map, String key) {
		String uniqueKey = key;

		int index = 0;
		while (map.containsKey(key)) {
			uniqueKey = key + index;
			index++;
		}

		return uniqueKey;
	}

	/**
	 * <pre>
	 * updateのset句を作成する
	 * {@link JDBCEntity}のフィールドを使用
	 * </pre>
	 * @return id = :id, birth_day = :birth_day, first_name = :first_name, last_name = :last_name
	 */
	private String batchUpdateSet() {
		List<String> batchUpdateSetList = new ArrayList<>();
		
		batchUpdateSetList.add(JDBCEntity.ID + " = :" + JDBCEntity.ID);
		batchUpdateSetList.add(JDBCEntity.BIRTHDAY + " = :" + JDBCEntity.BIRTHDAY);
		batchUpdateSetList.add(JDBCEntity.FIRST_NAME + " = :" + JDBCEntity.FIRST_NAME);
		batchUpdateSetList.add(JDBCEntity.LAST_NAME + " = :" + JDBCEntity.LAST_NAME);
		
		return String.join(", ", batchUpdateSetList);
	}
	
	/**
	 * <pre>
	 * バッチインサート用の(...) VALUES (...)を作成
	 * </pre>
	 * @return (カラム名, カラム名, ... ) VALUES (:カラム名, :カラム名, ...)
	 */
	private String batchInsertSet() {
		List<String> batchInsertColumnSetList = new ArrayList<>();
		List<String> batchInsertValueSetList = new ArrayList<>();

		for (String columnName : JDBCEntity.GetSetQueryList_forBatchUpdate()) {
			batchInsertColumnSetList.add(columnName);
			batchInsertValueSetList.add(":" + columnName);
		}
		
		List<String> sql = new ArrayList<>();
		sql.add("(");
		sql.add(String.join(", ", batchInsertColumnSetList));
		sql.add(") VALUES (");
		sql.add(String.join(", ", batchInsertValueSetList));
		sql.add(")");

		return String.join(" ", sql);
	}
}

NamedJDBCDaoTest.java

package com.example.demo.Dao;

import static org.junit.jupiter.api.Assertions.*;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.annotation.Transactional;

import com.example.demo.Entity.JDBCEntity;

/**
 * @author Takumi
 */
@SpringBootTest
@Transactional
class NamedJDBCDaoTest {

	@Autowired
	private NamedJDBCDao dao;

	/**
	 * <pre>
	 * テーブルデータを{@code List<Map<String, Object>>}ですべて取得
	 * </pre>
	 */
	@Test
	void testFindAll() {
		List<Map<String, Object>> allData = dao.findAll();
		assertTrue(allData.size() == 4);
	}

	/**
	 * <pre>
	 * 取得したいデータのid(プライマリキー)を渡してデータを取得する
	 * </pre>
	 */
	@Test
	void testFindById() {
		Map<String, Object> data = dao.findById(1);

		//取得データの確認
		assertEquals(data.get(JDBCEntity.BIRTHDAY), 20240101);
		assertEquals(data.get(JDBCEntity.FIRST_NAME).toString().strip(), "テスト");
		assertEquals(data.get(JDBCEntity.LAST_NAME).toString().strip(), "太郎");
	}

	/**
	 * <pre>
	 * 更新したいデータの{@code Map<String, String}を作成。
	 * key:カラム名 value:更新したい値
	 * 引数にid(プライマリキー)とこの{@code Map<STring, String>}を渡すことでデータを更新
	 * </pre>
	 */
	@Test
	void testUpdateById() {
		//更新前のデータ確認
		Map<String, Object> beforeData = dao.findById(1);
		assertEquals(beforeData.get(JDBCEntity.FIRST_NAME).toString().strip(), "テスト");
		assertEquals(beforeData.get(JDBCEntity.LAST_NAME).toString().strip(), "太郎");

		//データの更新
		Map<String, String> updateMap = new HashMap<>();
		updateMap.put(JDBCEntity.FIRST_NAME, "更新した");
		updateMap.put(JDBCEntity.LAST_NAME, "太郎くん");
		int updateCount = dao.updateById(1, updateMap);
		assertTrue(updateCount == 1);

		//更新データの確認
		Map<String, Object> updateData = dao.findById(1);
		assertEquals(updateData.get(JDBCEntity.FIRST_NAME).toString().strip(), "更新した");
		assertEquals(updateData.get(JDBCEntity.LAST_NAME).toString().strip(), "太郎くん");
	}

	/**
	 * <pre>
	 * 挿入したいデータの{@code Map<String, String>}を作成。
	 * 引数にこの{@code Map<String, String>}を渡すことでデータを挿入する。
	 * </pre>
	 */
	@Test
	void testInsert() {
		//挿入データの準備
		Map<String, String> insertData = new LinkedHashMap<>();
		insertData.put(JDBCEntity.ID, "10");
		insertData.put(JDBCEntity.FIRST_NAME, "インサート");
		insertData.put(JDBCEntity.LAST_NAME, "太郎");
		insertData.put(JDBCEntity.BIRTHDAY, "20200202");

		//データの挿入
		dao.insert(insertData);

		//素運輸データの確認
		Map<String, Object> data = dao.findById(10);
		assertEquals(data.get(JDBCEntity.FIRST_NAME), "インサート");
		assertEquals(data.get(JDBCEntity.LAST_NAME), "太郎");
		assertEquals(data.get(JDBCEntity.BIRTHDAY), 20200202);
	}

	/**
	 * <pre>
	 * データの削除
	 *引数に削除したいデータのid(プライマリキー)を渡すことでデータを削除する
	 * </pre>
	 */
	@Test
	void testDelete() {
		//削除前のデータ確認
		Map<String, Object> beforeData = dao.findById(1);
		assertEquals(beforeData.get(JDBCEntity.FIRST_NAME).toString().strip(), "テスト");
		assertEquals(beforeData.get(JDBCEntity.LAST_NAME).toString().strip(), "太郎");

		//データの削除
		int deleteCount = dao.deleteById(1);
		assertTrue(deleteCount == 1);

		//削除確認
		List<Map<String, Object>> deleteData = dao.findAll();
		assertFalse(deleteData.contains(beforeData));
	}

	/**
	 * <pre>
	 * データの一括更新
	 * {@code List<JDBCEntity>}に更新したいデータをセットし、引数に渡すことで
	 * データを一括更新する
	 * </pre>
	 */
	@Test
	void testBatchUpdate() {
		//更新データのセット
		List<JDBCEntity> entityList = new ArrayList<>();
		entityList.add(new JDBCEntity(1, "Junit", "たのすいーーーー", 20240202));
		entityList.add(new JDBCEntity(2, "Junit", "楽しいねえ", 20240203));

		//データの更新
		int updateCount = dao.batchUpdate(entityList);
		assertEquals(updateCount, 2);

		//更新データの確認
		Map<String, Object> afterId1 = dao.findById(1);
		assertEquals(afterId1.get(JDBCEntity.FIRST_NAME.toString().strip()), "Junit");
		assertEquals(afterId1.get(JDBCEntity.LAST_NAME.toString().strip()), "たのすいーーーー");
		assertEquals(Integer.parseInt(afterId1.get(JDBCEntity.BIRTHDAY).toString()), 20240202);

		Map<String, Object> afterId2 = dao.findById(2);
		assertEquals(Integer.parseInt(afterId1.get(JDBCEntity.BIRTHDAY).toString()), 20240202);
		assertEquals(afterId2.get(JDBCEntity.FIRST_NAME.toString().strip()), "Junit");
		assertEquals(afterId2.get(JDBCEntity.LAST_NAME.toString().strip()), "楽しいねえ");
		assertEquals(Integer.parseInt(afterId2.get(JDBCEntity.BIRTHDAY).toString()), 20240203);
	}

	/**
	 * <pre>
	 * データの一括挿入
	 * {@code List<JDBCEntity>}に挿入したいデータをセットすることで、
	 * データを一括挿入する
	 * </pre>
	 */
	@Test
	void testBatchInsert() {
		//挿入データのセット
		List<JDBCEntity> entityList = new ArrayList<>();
		entityList.add(new JDBCEntity(10, "Junit", "たのすいーーーー", 20240202));
		entityList.add(new JDBCEntity(20, "Junit", "楽しいねえ", 20240203));

		//データの一括挿入
		int insertCount = dao.batchInsert(entityList);
		assertEquals(insertCount, 2);

		//挿入データの確認
		Map<String, Object> insertData1 = dao.findById(10);
		assertEquals(insertData1.get(JDBCEntity.FIRST_NAME.toString().strip()), "Junit");
		assertEquals(insertData1.get(JDBCEntity.LAST_NAME.toString().strip()), "たのすいーーーー");
		assertEquals(Integer.parseInt(insertData1.get(JDBCEntity.BIRTHDAY).toString()), 20240202);

		Map<String, Object> insertData2 = dao.findById(20);
		assertEquals(insertData2.get(JDBCEntity.FIRST_NAME.toString().strip()), "Junit");
		assertEquals(insertData2.get(JDBCEntity.LAST_NAME.toString().strip()), "楽しいねえ");
		assertEquals(Integer.parseInt(insertData2.get(JDBCEntity.BIRTHDAY).toString()), 20240203);
	}

	/**
	 * <pre>
	 * データの一括削除
	 * {@code List<JDBCEntity>}に削除したいデータをセットし、引数に渡すことで
	 * データを一括削除する。
	 * セットする値はid(プライマリキー)のみでOK
	 * </pre>
	 */
	@Test
	void testBatchDelete() {
		//削除したいデータのidをセット
		List<JDBCEntity> entityList = new ArrayList<>();
		entityList.add(new JDBCEntity(1, null, null, 0));
		entityList.add(new JDBCEntity(2, null, null, 0));

		//データの一括削除
		int deleteCount = dao.batchDelete(entityList);
		assertEquals(deleteCount, 2);

		//データの削除確認
		Map<String, Object> afterId1 = dao.findById(1);
		assertTrue(afterId1.size() == 0);
		Map<String, Object> afterId2 = dao.findById(2);
		assertTrue(afterId2.size() == 0);
	}

	/**
	 * <pre>
	 * テーブルの前データを{@code List<JDBCEntity>}として取得する
	 * </pre>
	 */
	@Test
	void testGetAllJDBCEEntity() {
		//データの取得
		List<JDBCEntity> dataList = dao.getAllJDBCEntity();
		assertTrue(dataList.size() == 4);

		//ここでは、idが2のデータのみを確認する。(本来ならば前データを確認するべき)
		List<JDBCEntity> IdTwoData = dataList.stream().filter(data -> data.getId() == 2)
				.collect(Collectors.toList());
		assertTrue(IdTwoData.size() == 1);

		//データは必ず1つ
		JDBCEntity twoData = IdTwoData.get(0);

		//取得データの確認
		assertEquals(twoData.getId(), 2);
		assertEquals(twoData.getBirth_day(), 20240101);
		assertEquals(twoData.getFirst_name(), "テスト");
		assertEquals(twoData.getLast_name(), "二郎");
	}

	/**
	 * <pre>
	 * 取得したいデータのid(プライマリキー)を渡すことで、
	 * データを{@link JDBCEntity}で取得
	 * </pre>
	 */
	@Test
	void testGetJDBCEntityById() {
		//データの取得
		JDBCEntity data = dao.getJDBCEntityById(2);

		//取得データの確認
		assertEquals(data.getId(), 2);
		assertEquals(data.getBirth_day(), 20240101);
		assertEquals(data.getFirst_name(), "テスト");
		assertEquals(data.getLast_name(), "二郎");
	}

	/**
	 * <pre>
	 * 削除したいテーブルの名前を渡すことでテーブルを削除する
	 * ここでは、テーブルを削除してしまい、JUnitが通らなくなるのでDisabled
	 * </pre>
	 */
	@Test
	@Disabled
	void testDrop() {
		dao.executeDrop(JDBCEntity.TEST);
		assertThrows(DataAccessException.class, () -> {
			dao.getAllJDBCEntity();
		});
	}

	/**
	 * <pre>
	 * テーブルの作成
	 * {@code Map<String, String>}でkeyにカラム名、valueに型やNULL許容などをセット、
	 * また{@code List<String>}にプライマリキーにしたいカラム名をセットし、
	 * 引数に作成したいテーブル名とともにこの二つを渡すことで、テーブルを作成する
	 * </pre>
	 */
	@Test
	void testExecuteCreate() {
		Map<String, String> columnInfo = new HashMap<>();
		columnInfo.put("id", "INT");
		columnInfo.put("name", "char(50)");
		columnInfo.put("comment", "char(50)");
		columnInfo.put("message", "char(100)");

		List<String> primaryList = new ArrayList<>();
		primaryList.add("id");
		primaryList.add("name");

		dao.executeCreate("CREATE_TABLE", columnInfo, primaryList);

		List<String> tableNameList = new createTableTest().GetTableNams();
		assertTrue(tableNameList.contains("CREATE_TABLE"));
	}
}

/**
 * @author Takumi
 * {@link NamedJDBCDaoTest#testExecuteCreate()}のテーブル作成確認用クラス
 */
class createTableTest {

	private final JdbcTemplate jdbc;

	/**
	 * h2データベースの設定
	 */
	public createTableTest() {
		jdbc = new JdbcTemplate();
		jdbc.setDataSource(DataSourceBuilder.create()
				.driverClassName("net.sf.log4jdbc.sql.jdbcapi.DriverSpy")
				.url("jdbc:log4jdbc:h2:mem:hogedb")
				.username("sa")
				.password("")
				.build());
	}

	/**
	 * @return 存在するテーブル名のリスト
	 */
	public List<String> GetTableNams() {
		List<Map<String, Object>> tableData = jdbc.queryForList("SELECT"
				+ " TBL.TABLE_NAME AS TABLE_NAME "
				+ "FROM"
				+ " INFORMATION_SCHEMA.TABLES AS TBL "
				+ "WHERE"
				+ " TBL.TABLE_SCHEMA =  SCHEMA()");

		List<String> tableNameList = new ArrayList<>();
		for (Map<String, Object> data : tableData) {
			tableNameList.add(data.get("TABLE_NAME").toString());
		}

		return tableNameList;

	}

}