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
- Entity/
- com/example/demo/
- resources/
- application.properties
- log4jdbc.log4j2.properties
- shema.sql
- data.sql
- ……
- java/
- test/
- java/com/example/demo/Dao/
- NamedJDBCTempDaoTest.java
- java/com/example/demo/Dao/
- main/
- build.gradle
- ……
- src/
サンプルコード
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;
}
}