Visual Studio CodeによるSpring5 MVC Webアプリ開発　SQLServer接続編

#はじめに
[HelloWorld作成編](https://qiita.com/t_skri/items/24d0d353771735b453f7)で作成したプロジェクトを拡張していきます。
SQLServerは[こちら](https://qiita.com/t_skri/items/8d69c423042e310434d0)で作成したDB、テーブルを流用します。

#環境
OS:Windows 10 Pro 64bit
DB:SQL Server 2019(Cent OS 8 on Hyper-V)
Editor:Visual Studio Code 1.42.1
JDK:AdoptOpenJDK 11.0.6+10 x64
Apache Maven:v3.6.3
Apache Tomcat:v9.0.31

#プロジェクト作成
HelloWorldで作成したプロジェクトを「D:\JAVA\Project\sqlSample」にコピーして作成しました。

#pom.xml
SQLServerにアクセスする為に必要なRepositoryを追加します。

```
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-jdbc</artifactId>
	<version>${spring.version}</version>
	<scope>compile</scope>
</dependency>

<dependency>
	<groupId>com.microsoft.sqlserver</groupId>
    	<artifactId>mssql-jdbc</artifactId>
    	<version>8.2.1.jre11</version>
    	<scope>runtime</scope>
</dependency>

<dependency>
	<groupId>com.zaxxer</groupId>
	<artifactId>HikariCP</artifactId>
	<version>3.4.2</version>
	<scope>compile</scope>
</dependency>
```

pom.xml全体です。

```XML:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.example</groupId>
	<artifactId>sqlSample1</artifactId>
	<version>1.0-SNAPSHOT</version>
	<packaging>war</packaging>

 
	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<java.version>11</java.version>
		<spring.version>5.2.4.RELEASE</spring.version>
        <!-- web.xmlが無い場合でもビルドを実行する -->
        <failOnMissingWebXml>false</failOnMissingWebXml>
	</properties>

	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.1</version>
				<configuration>
					<source>${java.version}</source>
					<target>${java.version}</target>
				</configuration>
			</plugin>
		</plugins>
	</build>
	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.11</version>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${spring.version}</version>
			<scope>compile</scope>
		</dependency>

		<dependency>
    		<groupId>org.springframework</groupId>
    		<artifactId>spring-context-support</artifactId>
    		<version>${spring.version}</version>
    		<scope>compile</scope>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
			<version>${spring.version}</version>
			<scope>compile</scope>
		</dependency>

		<dependency>
    		<groupId>com.microsoft.sqlserver</groupId>
    		<artifactId>mssql-jdbc</artifactId>
    		<version>8.2.1.jre11</version>
    		<scope>runtime</scope>
		</dependency>

		<dependency>
		    <groupId>com.zaxxer</groupId>
		    <artifactId>HikariCP</artifactId>
		    <version>3.4.2</version>
		    <scope>compile</scope>
		</dependency>

		<dependency>
			<groupId>org.thymeleaf</groupId>
			<artifactId>thymeleaf-spring5</artifactId>
			<version>3.0.11.RELEASE</version>
			<scope>compile</scope>
		</dependency>

		<dependency>
			<groupId>org.thymeleaf.extras</groupId>
			<artifactId>thymeleaf-extras-java8time</artifactId>
			<version>3.0.4.RELEASE</version>
			<scope>compile</scope>
		</dependency>

		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>4.0.1</version>
			<scope>provided</scope>
		</dependency>

		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<version>1.18.10</version>
			<scope>provided</scope>
		</dependency>
	</dependencies>
</project>
```

#Model作成
ビジネスロジックを記述する__service__部とDB接続（SQL文発行）する__persistence__部の作成、及びconfigファイルを作成します。

#service部作成
「D:\JAVA\Project\sqlSample\src\main\java\com\example」に「service」フォルダを作成します。
その直下にconfigフォルダを作成します。
configフォルダにServiceConfig.javaを作成します。

```
D:\JAVA\Project\sqlSample\src\main\java\com\example
└─service
    └─config
        └ServiceConfig.java
```

```java:ServiceConfig.java
package com.example.service.config;

import javax.sql.DataSource;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@ComponentScan(basePackages = "com.example.service")
@EnableTransactionManagement
public class ServiceConfig {
	 @Bean
	    public PlatformTransactionManager transactionManager(DataSource dataSource) {
	        DataSourceTransactionManager transactionManager =
	                new DataSourceTransactionManager(dataSource);
	        return transactionManager;
	    }
}

```

#persistence部作成
「D:\JAVA\Project\sqlSample\src\main\java\com\example」に「persistence」フォルダを作成します。
その直下にconfigフォルダ、entityフォルダ、repositoryフォルダを作成します。
configフォルダにPersistenceConfig.javaを作成します。

```
D:\JAVA\Project\sqlSample\src\main\java\com\example
└─persistence
    ├─config
    │　└─PersistenceConfig.java
    ├─entity
    └─repository
```
PersistenceConfig.javaにはSQLServerに接続するための設定を記述します。

```java:PersistenceConfig.java
package com.example.persistence.config;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;

import com.zaxxer.hikari.HikariDataSource;

@Configuration
@ComponentScan(basePackages = "com.example.persistence.repository")
@PropertySource("classpath:jdbc.properties")
public class PersistenceConfig {
	@Bean
    public DataSource dataSource(@Value("${jdbc.driverClassName}") String driverClassName,
                                 @Value("${jdbc.url}") String url,
                                 @Value("${jdbc.username}") String username,
                                 @Value("${jdbc.password}") String password) {
		HikariDataSource dataSource = new HikariDataSource();
        dataSource.setDriverClassName(driverClassName);
        dataSource.setJdbcUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);

        dataSource.setMinimumIdle(10);
        dataSource.setMaximumPoolSize(300);
        dataSource.setConnectionInitSql("SELECT 0");

        return dataSource;
    }

    @Bean
    public NamedParameterJdbcTemplate jdbcTemplate(DataSource dataSource) {
        NamedParameterJdbcTemplate jdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
        return jdbcTemplate;
    }
}

```

#resources部作成
「D:\JAVA\Project\sqlSample\src\main」に「resources」フォルダを作成します。
resourcesフォルダにjdbc.propertiesを作成します。

```
D:\JAVA\Project\sqlSample\src\main
└─resources
  └─jdbc.properties
```

SQLServerに接続するための接続情報を記述します。

```text:jdbc.properties
jdbc.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver
jdbc.url=jdbc:sqlserver://xxx:1433;databaseName=Training01;QuotedID=NO // SQLServerのホスト名もしくはIPアドレスを指定する。
jdbc.username=xxx // 接続ユーザー名を指定する。
jdbc.password=xxx // 接続ユーザーのパスワードを指定する。
```

#WebAppInitializer.java変更
DB接続とビジネスロジック用のModelが使用出来るようにgetRootConfigClassesにPersistenceConfig.classとServiceConfig.classを追加する。

```java
@Override
protected Class<?>[] getRootConfigClasses() {
    return new Class<?>[] {PersistenceConfig.class, ServiceConfig.class};
}
```
WebAppInitializer.java全体です。

```java:WebAppInitializer.java
package com.example.web.config;

import java.nio.charset.StandardCharsets;

import javax.servlet.Filter;

import com.example.persistence.config.PersistenceConfig;
import com.example.service.config.ServiceConfig;

import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
	/**
     * ビジネスロジックなど、Spring MVC以外に関するJava Configクラスを指定します。
     */
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[] {PersistenceConfig.class, ServiceConfig.class};
    }

    /**
     * Spring MVCに関するJava Configクラスを指定します。
     */
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[] {MvcConfig.class};
    }

    /**
     * DispatcherServletに対するURLパターンを指定します。
     * "/"を指定することで、全リクエストをDispatcherServletが受け取ります。
     */
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }

    /**
     * サーブレットフィルターを指定します。
     * 複数のフィルターがあった場合、配列に指定した順番に実行されます。
     */
    @Override
    protected Filter[] getServletFilters() {
        return new Filter[]{
                new CharacterEncodingFilter(StandardCharsets.UTF_8.name(), true)};
    }
}
```
ここまでが前準備になります。

#entity作成
DBからのデータを受けるentityを作成します。

```
D:\JAVA\Project\sqlSample\src\main\java\com\example\persistence
└─entity
  └─ProductsMaster.java
```

```java:ProductsMaster.java
package com.example.persistence.entity;

import lombok.Data;

@Data
public class ProductsMaster {
    private String ProductsCode;
    private String ProductsName;
    private Integer UnitPrice;

    public ProductsMaster() {}

    public ProductsMaster(
        String ProductsCode,
        String ProductsName, 
        Integer UnitPrice
        ) {
            this.ProductsCode = ProductsCode;
            this.ProductsName = ProductsName;
            this.UnitPrice = UnitPrice;
        }   
}
```

#repository作成
DBに接続してSQL文を発行するrepositoryを作成します。

```
D:\JAVA\Project\sqlSample\src\main\java\com\example\persistence
└─repository
  ├─ProductsMasterRepository.java
  └─ProductsMasterRepositoryImpl.java
```

```java:ProductsMasterRepository.java
package com.example.persistence.repository;

import java.util.List;

import com.example.persistence.entity.ProductsMaster;

public interface ProductsMasterRepository {
	List<ProductsMaster> productsMasterList();
}
```

```java:ProductsMasterRepositoryImpl.java
package com.example.persistence.repository;

import java.util.List;

import com.example.persistence.entity.ProductsMaster;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository
public class ProductsMasterRepositoryImpl implements ProductsMasterRepository {
	@Autowired
    NamedParameterJdbcTemplate jdbcTemplate;

	@Override
	public List<ProductsMaster> productsMasterList() {
		String strSQL = "SELECT * FROM TB_TestTable ORDER BY ID";

        List<ProductsMaster> pMList = jdbcTemplate.query(strSQL,
                (rs, rowNum) -> new ProductsMaster(
                        rs.getString("ProductsName"),
                        rs.getString("ProductsCode"),
                        rs.getInt("UnitPrice")                 
                        )
                );

        return pMList;
	}
}
```

#service作成
ビジネスロジックとなるserviceを作成します。
今回の内容ではわざわざ作成する必要性はないのですが、1例として作成します。

```
D:\JAVA\Project\sqlSample\src\main\java\com\example
└─service
  ├─ProductsMasterService.java
  └─ProductsMasterServiceImpl.java
```

```java:ProductsMasterService.java
package com.example.service;

import java.util.List;

import com.example.persistence.entity.ProductsMaster;

public interface ProductsMasterService {
    List<ProductsMaster> productsMasterList();
}
```

```java:ProductsMasterServiceImpl.java
package com.example.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.example.persistence.entity.ProductsMaster;
import com.example.persistence.repository.ProductsMasterRepository;

@Service
public class ProductsMasterServiceImpl implements ProductsMasterService {
	@Autowired
	ProductsMasterRepository productsMasterRepository;

	@Override
	public List<ProductsMaster> productsMasterList() {
		List<ProductsMaster> pMList = productsMasterRepository.productsMasterList();

        return pMList;
	}
}

```
#form作成
「D:\JAVA\Project\sqlSample\src\main\java\com\example\web」に「form」フォルダを作成します。
formフォルダにProductsMasterForm.javeを作成します。

```
D:\JAVA\Project\sqlSample\src\main\java\com\example\web
└─form
  └─ProductsMasterForm.java
```

```java:ProductsMasterForm.java
package com.example.web.form;

import java.util.List;

import com.example.persistence.entity.ProductsMaster;

import lombok.Data;

@Data
public class ProductsMasterForm {
	private List<ProductsMaster> helloList;
}
```

#controller作成
「D:\JAVA\Project\sqlSample\src\main\java\com\example\web\controller」にProductsMasterController.javaを作成します。

```
D:\JAVA\Project\sqlSample\src\main\java\com\example\web
└─controller
  └─ProductsMasterController.java
```

```java:ProductsMasterController.java
package com.example.web.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import com.example.service.ProductsMasterService;
import com.example.web.form.ProductsMasterForm;

@Controller
@RequestMapping("/hello")
public class ProductsMasterController {
	@Autowired
	ProductsMasterService productsMasterService;

	@GetMapping("/index")
	public String indexGet(Model model) {
		ProductsMasterForm productsMasterForm = new ProductsMasterForm();
		productsMasterForm.setHelloList(productsMasterService.productsMasterList());

		model.addAttribute("productsMasterForm", productsMasterForm);
		return "productsMasterForm/index";
	}

}
```

#view作成
「D:\JAVA\Project\sqlSample\src\main\webapp\WEB-INF\templates」に「productsMaster」フォルダを作成します。
productsMasterフォルダにindex.htmlを作成します。

```
D:\JAVA\Project\sqlSample\src\main\webapp\WEB-INF\templates
└─productsMaster
  └─index.html
```

```HTML:index.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">

<head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
	<meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Spring5 MVC sqlSample01</title>
</head>
<body>
    <h1>Hello Spring Products List</h1>
    <table border="1">
		<thead>
			<tr>
				<th>製品コード</th>
				<th>製品名</th>
				<th>単価</th>
			</tr>
		</thead>
		<tbody>
			<tr th:each="pm : ${productsMasterForm.pmList}" th:object="${pm}">
		    	<td th:text="*{ProductsCode}"></td>
		    	<td th:text="*{ProductsName}"></td>
		        <td th:text="*{UnitPrice}"></td>
			</tr>
		</tbody>
	</table>
</body>
</html>
```

#コンパイル/パッケージ
次のコマンドでコンパイル及びwarファイルの作成が出来ます。

```
mvn package
```

#動作確認
コマンドパレット

```
Tomcat: Add Tomcat Server
```

Tomcatフォルダ選択ダイアログが表示されますので、Tomcatフォルダ(D:\JAVA\Tomcat\apache-tomcat-9.0.31)を選択します。

コマンドパレット

```
Tomcat: Run on Tomcat Server
```
warファイル選択ダイアログが表示されますので、「mvn package」で作成されたwarファイル(D:\JAVA\Project\SpringSample01\target\sqlSample1-1.0-SNAPSHOT.war)を選択します。

「[http://localhost:8080/](http://localhost:8080/)」にアクセスして下さい。
![tomcat4.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/591423/91b83b36-e9dc-3660-5064-6fbc2b1990c2.jpeg)

sqlSample-1.0-SNAPSHOTをクリックして下さい。

![sqlSample1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/591423/87f2f869-f5b1-33e2-e7f2-f3519f83c4a2.jpeg)

コード修正→動作確認の際、Tomactに配置したwarファイルを都度削除しないと上手く反映されませんでした。
hot deployが出来れば良いのですか、見つけられませんでした。

#今回のサンプルソース
GitHubにアップしました。
[https://github.com/t-skri1/SpringSample02](https://github.com/t-skri1/SpringSample02)

#まとめ
簡易的ですが、Webアプリになりました。
次回はSpring Securityを組み込みます。