SpringBoot + MySQL DB設定, JdbcTemplate 使った簡易メモ

SpringBoot で MySQL 使った DB設定と JdbcTemplate 使う時の簡易メモ
application.properties でDB接続とSQL実行の設定と JdbcTemplate 使って CRUD処理をざっくりやる感じ

# 実行環境

- Windows11
- Pleiades 2024 Full Edition
- Maven v3.9.6
- SpringFramework Boot v3.3.0
- Java v21
- spring-boot-starter-thymeleaf
- spring-boot-starter-web
- spring-boot-starter-data-jdbc
- MySQL v8.0.28

# 接続DB準備

接続するDBとユーザを作成する
dbname, dbuser, dbpass は好きな値に変えてOKです

```sql
# DB作成
CREATE DATABASE dbname;
# ユーザ作成・権限追加
CREATE USER 'dbuser'@'localhost' IDENTIFIED BY 'dbpass';
GRANT ALL PRIVILEGES ON dbname.* TO 'dbuser'@'localhost';
```


# プロジェクト作成

Springスタータ・プロジェクト でプロジェクト作成する
依存関係は以下を設定する

- Spring Boot DevTools
- Spring Web
- Lombok
- Thymeleaf
- MySQL Driver
- Spring Data JDBC # 先のこと考えると Spring Data JPA で良いかも...

# DB接続, SQL実行の設定追加

- application.properties に以下を追加する

    ```properties:application.properties
    # Datasource 設定 (dbname, dbuser, dbpass は接続するDBnの情報にする)
    spring.datasource.url=jdbc:mysql://localhost:3306/dbname
    spring.datasource.username=dbuser
    spring.datasource.password=dbpassword
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    
    # SQL実行設定 (schema はテーブル作成とか, data は初期データ)
    spring.sql.init.mode=always
    spring.sql.init.schema-locations=classpath:data/schema.sql
    spring.sql.init.data-locations=classpath:data/data.sql
    ``````
    sql実行のログ出力したいときは以下も追加
    ```properties:application.properties
    # SQLログ出力設定
    logging.level.org.springframework.jdbc.core.JdbcTemplate=DEBUG
    logging.level.org.springframework.jdbc.core.StatementCreatorUtils=TRACE
    ``````

- /src/main/resources/data の中に schema.sql, data.sql を作る
    ここではサンプルで適当なテーブル作ってデータ登録, 各々のアプリでDB考える必要あり
    常に実行にしてるのでテーブル、データともにあったら作らないようする
    - schema.sql
        ```sql:schema.sql
        CREATE TABLE IF NOT EXISTS sampwk (
            id INT AUTO_INCREMENT,
            name VARCHAR(50) NOT NULL,
            remark VARCHAR(255),
            PRIMARY KEY(id)
        );
        ```
    - data.sql
        ```sql:data.sql
        INSERT IGNORE INTO sampwk(id, name, remark) VALUES 
        	  (1, 'test1', 'remark1')
            , (2, 'test2', 'remark2')
            , (3, 'test3', 'remark3');
        ```

# JdbcTemplate 使えるようにする

クラス変数に @Autowired で定義する
@Controller, @Service... などDIコンテナで管理されてるクラスならどれでもOK

```Java
	@Autowired
	JdbcTemplate jdbcTemplate;
```

# JdbcTemplate データ取得

何パターンかコンソールに結果を出しながらやってみる
sql へのパラメータは `?` つかう

## `List<Map>` で取得 queryForList

- sqlパラメータなし queryForList(sql)
    ```Java
    String sql = "SELECT id, name, remark FROM sampwk";
    List<Map<String, Object>> list = jdbcTemplate.queryForList(sql);
    list.forEach(m -> System.out.println(m.get("id") + ":" + m.get("name") + ":" + m.get("remark")));
    ```
    
- sqlパラメータあり queryForList(sql, args)
    ```Java
    String sql = "SELECT id, name, remark FROM sampwk WHERE id in (?, ?)";
    Object[] args = {1, 2};
    List<Map<String, Object>> list = jdbcTemplate.queryForList(sql, args);
    list.forEach(m -> System.out.println(m.get("id") + ":" + m.get("name") + ":" + m.get("remark")));
    ```

## `List<Class>` で取得 query

- sqlパラメータなし query(sql, rowMapper)
    ```Java
    String sql = "SELECT id, name, remark FROM sampwk";
    RowMapper<Sampwk> rowMapper = new BeanPropertyRowMapper<>(Sampwk.class);
    List<Sampwk> list = jdbcTemplate.query(sql, rowMapper);
    list.forEach(m -> System.out.println(m.getId() + ":" + m.getName() + ":" + m.getRemark()));
    ```
    
- sqlパラメータあり query(sql, rowMapper, args)
    ```Java
    String sql = "SELECT id, name, remark FROM sampwk WHERE id in (?, ?)";
    RowMapper<Sampwk> rowMapper = new BeanPropertyRowMapper<>(Sampwk.class);
    Object[] args = {1, 2};
    List<Sampwk> list = jdbcTemplate.query(sql, rowMapper, args);
    list.forEach(m -> System.out.println(m.getId() + ":" + m.getName() + ":" + m.getRemark()));
    ```

- 設定してる `Sampwk.class`
    ```Java
    import lombok.Data;
    
    @Data
    public class Sampwk {
    	private Integer id;
    	private String name;
    	private String remark;
    }
    ```


## `Map` で取得 queryForMap
SQLの取得結果が 1件以外（0 or 複数）だとエラーになる

- sqlパラメータあり queryForMap(sql, args)
    ```Java
    String sql = "SELECT id, name, remark FROM sampwk WHERE id = ?";
    Map<String, Object> map = jdbcTemplate.queryForMap(sql, 1);
    System.out.println(map.get("id") + ":" + map.get("name") + ":" + map.get("remark"));
    ```

## `Class` で取得 queryForObject
SQLの取得結果が 1件以外（0 or 複数）だとエラーになる

- sqlパラメータあり queryForObject(sql, rowMapper, args)
    ```Java
    String sql = "SELECT id, name, remark FROM sampwk WHERE id = ?";
    RowMapper<Sampwk> rowMapper = new BeanPropertyRowMapper<>(Sampwk.class);
    Sampwk sampwk = jdbcTemplate.queryForObject(sql, rowMapper, 1);
    System.out.println(sampwk.getId() + ":" + sampwk.getName() + ":" + sampwk.getRemark());
    ```
- 設定してる `Sampwk.class`
    ```Java
    import lombok.Data;
    
    @Data
    public class Sampwk {
    	private Integer id;
    	private String name;
    	private String remark;
    }
    ```


# JdbcTemplate データ更新(登録, 削除)

更新, 登録, 削除 はどれも使うのは update なので登録を参考までに
更新, 登録, 削除 するなら JPA でやった方が楽そう．．．

- sqlパラメータあり update(sql, args)
    ```Java
    String sql = "INSERT INTO sampwk (id, name, remark) VALUES (?, ?, ?)";
    Object[] args = {100, "name100", "remark100"};
    int cnt = jdbcTemplate.update(sql, args);
    ```