プログラミング逆引き辞典

~ 多言語対応のプログラミングレシピ ~

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();
        }
    }
}