SpringBoot ExcelファイルアップロードとDB登録のサンプルソース
■環境
OS:Windows
言語:Java11
DB:MySQL
フレームワーク:SpringBoot
ビルドツール:Gradle
下記テーブルを用意
・employeeテーブル
※アップロードファイルの値を登録するテーブル
create table employee (
id int primary key auto_increment
, name varchar(100)
, age int
, del_flag int
);
・mappingテーブル
※アップロードファイルのヘッダーの論理名を物理名に変換するマッピングテーブル
create table mapping (
id int primary key auto_increment
, col_nm varchar(100)
, physical_nm varchar(100)
);
insert into mapping values (1, '名前', 'name');
insert into mapping values (2, '年齢', 'age');
※MyBatis Generatorを実行してドメインクラスやマッパークラスを自動生成しておく
MyBatis Generatorの設定方法と実行
・build.gradle
plugins {
id 'org.springframework.boot' version '2.4.1'
id 'io.spring.dependency-management' version '1.0.10.RELEASE'
id 'java'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
<!-- スネークケースからキャメルケースに変換する為のライブラリ -->
implementation 'com.google.guava:guava:30.0-jre'
<!-- poi・poi-ooxmlはExcel操作のライブラリ -->
implementation 'org.apache.poi:poi-ooxml:4.1.2'
implementation 'org.apache.po:poi:4.1.2'
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.1.1'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'mysql:mysql-connector-java'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
test {
useJUnitPlatform()
}
・MappingMapper.java
・
・
・
<!-- 論理名をキーに物理名を取得するSQLを呼び出すメソッド -->
String getPhysicalName(String headerStr);
・MappingMapper.xml
・
・
・
<!-- 論理名をキーに物理名を取得するSQL -->
<select id="getPhysicalName" parameterType="java.lang.String" resultType="java.lang.String">
select physical_nm from mapping where col_nm = #{col_nm}
</select>
・MainController.java
package com.example.controller;
import java.util.List;
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.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.multipart.MultipartFile;
import com.example.domain.Employee;
import com.example.domain.EmployeeExample;
import com.example.form.UploadForm;
import com.example.mybatis.mapper.EmployeeMapper;
import com.example.service.MainService;
import com.mysql.cj.util.StringUtils;
@Controller
public class MainController {
@Autowired
EmployeeExample employeeExample;
@Autowired
EmployeeMapper employeeMapper;
@Autowired
MainService maindService;
@GetMapping
String index(@ModelAttribute UploadForm uploadForm, Model model) {
//従業員テーブルのレコードを取得
List<Employee> employeeList = employeeMapper.selectByExample(employeeExample);
//モデルにセット
model.addAttribute("employeeList", employeeList);
return "index";
}
@PostMapping("/upload")
String upload(UploadForm uploadForm) {
//アップロードファイルのリスト
List<MultipartFile> multipartFileList = uploadForm.getMultipartFileList();
//アップロード処理
multipartFileList.forEach(multipartFile -> {
if (!StringUtils.isNullOrEmpty(multipartFile.getOriginalFilename())) {
//ファイル名が存在する場合はアップロード処理実行
maindService.excelUpload(multipartFile);
//ファイル名を引数にファイル読み込み処理実行
maindService.excelInsert(multipartFile.getOriginalFilename());
}
});
return "redirect:/";
}
}
・MainService.java
package com.example.service;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Optional;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import com.example.domain.Employee;
import com.example.mybatis.mapper.EmployeeMapper;
import com.example.mybatis.mapper.MappingMapper;
import com.google.common.base.CaseFormat;
@Service
public class MainService {
@Autowired
MappingMapper mappingMapper;
@Autowired
EmployeeMapper emploeeMapper;
//格納先のディレクトリ
private final String DIR = "C:/Users/goone/Desktop/Upload/";
//読み込むExcelファイルのシート名
private final String SHEET_NAME = "sheet1";
//String型
private final String STRING_TYPE = "class java.lang.String";
//Integer型
private final String INTEGER_TYPE = "class java.lang.Integer";
/**
* アップロード処理
* @param multipartFile
*/
public void excelUpload(MultipartFile multipartFile) {
//アップロードファイル名
String fileName = multipartFile.getOriginalFilename();
//アップロードファイルの格納先ディレクトリ
Path fullPath = Paths.get(DIR + fileName);
try {
//アップロードファイルをバイト値に変換
byte[] bytes = multipartFile.getBytes();
//格納先に書き込みファイルを作成
OutputStream stream = Files.newOutputStream(fullPath);
//書き込み
stream.write(bytes);
//ストリームを閉じる
stream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Excelファイルの読み込んでDB登録
* @param fileName
*/
public void excelInsert(String fileName) {
//読み込むファイルのフルパス
String fullPath = DIR + fileName;
try {
//読み込んだExcelのインスタンスを作成
Workbook workbook = WorkbookFactory.create(new File(fullPath));
//シートを指定
Sheet sheet = workbook.getSheet(SHEET_NAME);
//1行目のヘッダーを取得
Row headerRow = sheet.getRow(0);
//最終列を取得
int lastCol = headerRow.getLastCellNum();
//最終行を取得
int lastRowNbr = sheet.getLastRowNum();
//行 ※値は2行目(rowNbr = 1)から「lastRowNbr」行目までなので「>=」とする
for (int rowNbr = 1; lastRowNbr >= rowNbr; rowNbr++) {
Employee employee = new Employee();
//列
for (int col = 0; lastCol > col; col++) {
//ヘッダーの値を1列ずつ取得
Cell headerCell = headerRow.getCell(col);
String headerStr = headerCell.getStringCellValue();
//ヘッダーの値を元に物理名に変換
String headerPhysicalNm = mappingMapper.getPhysicalName(headerStr);
//ヘッダーの物理名をスネークケースからキャメルケースに変換
String refrectName = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, headerPhysicalNm);
//セッターメソッドの文字列を生成
String setterMethodName = "set" + refrectName;
//フィールドの型を取得
Class<Employee> clazz = Employee.class;
Field[] fields = clazz.getDeclaredFields();
String type = "";
for (Field field : fields) {
if (field.getName().equals(headerPhysicalNm)) {
type = field.getType().toString();
break;
}
}
//アップロードファイルの値
Row rowVal = sheet.getRow(rowNbr);
Cell cellVal = rowVal.getCell(col);
Optional<Cell> optVal = Optional.ofNullable(cellVal);
if (type.equals(STRING_TYPE)) {
//String型の場合
optVal.ifPresent(e -> {
try {
//アップロードファイルの値を取得
String strVal = e.getStringCellValue();
//セッターメソッドを呼び出し
Method method = Employee.class.getMethod(setterMethodName, String.class);
method.invoke(employee, strVal);
} catch (Exception e1) {
e1.printStackTrace();
}
});
} else if (type.equals(INTEGER_TYPE)) {
//Integer型の場合
optVal.ifPresent(e -> {
try {
//アップロードファイルの値を取得
int intVal = (int)e.getNumericCellValue();
//セッターメソッドを呼び出し
Method method = Employee.class.getMethod(setterMethodName, Integer.class);
method.invoke(employee, intVal);
} catch (Exception e1) {
e1.printStackTrace();
}
});
} else {
System.out.println("定義されていない型:" + type);
throw new Exception();
}
}
//削除フラグはデフォルトで「0」をセット
employee.setDelFlag(0);
//DB登録
emploeeMapper.insert(employee);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}