Spring Boot DB連携

Spring BootのWEB画面上でCRUDを含むOracleアクセス処理を実装してみた(ソースコード編)

今回も、C(Create)・R(Read)・U(Update)・D(Delete)を一通り含むOracle接続処理を含むサンプルプログラムについて共有する。前回は、サンプルプログラムの完成イメージについて記載していたが、今回は、具体的なサンプルプログラムのソースコードを共有する。

前提条件

下記記事のSpring BootでのOracle接続処理の実装が完了していること。

Spring BootでOracle接続処理を実装してみたSpring BootのWEBアプリケーションを開発する際、なんらかのデータベースにアクセスすることが多いが、SpringのJPAライブ...

また、以下の記事の「hibernate_sequence」シーケンスの作成が完了していること。

Spring BootのWEB画面上でCRUDを含むOracleアクセス処理を実装してみた(完成イメージ編)今回は、C(Create)・R(Read)・U(Update)・D(Delete)を一通り含むOracle接続処理をSpring Boo...

作成したサンプルプログラムの内容

作成したサンプルプログラムの構成は以下の通り。
サンプルプログラムの構成

「UserDataRepository.java」についてはfindByIdメソッドを追加し、「application.properties」についてはSQLログ出力・バインドパラメータ出力の定義を追加している。その内容は以下の通り。

package com.example.demo;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

/**
 * ユーザーデータテーブル(user_data)アクセス用リポジトリ
 */
// JpaRepositoryクラスを継承することによって、ユーザーデータテーブル(user_data)への
// select, insert, delete, updateを行うメソッドを自動で生成してくれる
@Repository
public interface UserDataRepository extends JpaRepository<UserData, Long> {
    public UserData findById(long id);
}
server.port = 8084
# DB接続情報
spring.datasource.url=jdbc:oracle:thin:@localhost:1521:xe
spring.datasource.username=USER01
spring.datasource.password=USER01
spring.datasource.driverClassName=oracle.jdbc.driver.OracleDriver
# SQLログ出力
logging.level.org.hibernate.SQL=debug
# バインドパラメータ出力
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=trace
logging.level.org.hibernate.type.EnumType=trace

上記のSQLログ・バインドパラメータ出力の定義を追加すると、以下のように、コンソールログにSQLログとバインドパラメータが出力されることが確認できる。
コンソールログ出力例

サラリーマン型フリーランスSEという働き方でお金の不安を解消しよう先日、「サラリーマン型フリーランスSE」という働き方を紹介するYouTube動画を視聴しましたので、その内容をご紹介します。 「サ...

さらに、「DemoController.java」の内容は以下の通り。

package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.support.SessionStatus;
import java.util.ArrayList;
import java.util.List;

@Controller
@SessionAttributes(types = {DemoForm.class})
public class DemoController {

    /**
     * ユーザーデータテーブル(user_data)へアクセスするリポジトリ
     */
    @Autowired
    private UserDataRepository repository;

    /**
     * ユーザーデータテーブル(user_data)のデータを取得して返却する
     * @return ユーザーデータリスト
     */
    @ModelAttribute("demoFormList")
    public List<DemoForm> userDataList(){
        List<DemoForm> demoFormList = new ArrayList<>();
        return demoFormList;
    }

    /**
     * Formオブジェクトを初期化して返却する
     * @return Formオブジェクト
     */
    @ModelAttribute("demoForm")
    public DemoForm createDemoForm(){
        DemoForm demoForm = new DemoForm();
        return demoForm;
    }

    /**
     * 初期表示(一覧)画面に遷移する
     * @param model Modelオブジェクト
     * @return 一覧画面へのパス
     */
    @RequestMapping("/")
    public String index(Model model){
        List<DemoForm> demoFormList = new ArrayList<>();
        //ユーザーデータテーブル(user_data)から全データを取得する
        List<UserData> userDataList = repository.findAll(
                new Sort(Sort.Direction.ASC,"id"));
        for(UserData userData : userDataList){
            demoFormList.add(getDemoForm(userData));
        }
        //ユーザーデータリストを更新
        model.addAttribute("demoFormList", demoFormList);
        return "list";
    }

    /**
     * 更新処理を行う画面に遷移する
     * @param id 更新対象のID
     * @param model Modelオブジェクト
     * @return 入力・更新画面へのパス
     */
    @GetMapping("/update")
    public String update(@RequestParam("id") String id, Model model){
        UserData userData = repository.findById(Long.parseLong(id));
        model.addAttribute("demoForm", getDemoForm(userData));
        return "input";
    }

    /**
     * 削除確認画面に遷移する
     * @param id 更新対象のID
     * @param model Modelオブジェクト
     * @return 削除確認画面へのパス
     */
    @GetMapping("/delete_confirm")
    public String delete_confirm(@RequestParam("id") String id, Model model){
        UserData userData = repository.findById(Long.parseLong(id));
        model.addAttribute("demoForm", getDemoForm(userData));
        return "confirm_delete";
    }

    /**
     * 削除処理を行う
     * @param demoForm Formオブジェクト
     * @return 一覧画面の表示処理
     */
    @PostMapping(value = "/delete", params = "next")
    @Transactional(readOnly = false)
    public String delete(DemoForm demoForm){
        UserData userData = getUserData(demoForm);
        repository.delete(userData);
        return "redirect:/to_index";
    }

    /**
     * 削除完了後に一覧画面に戻る
     * @param model Modelオブジェクト
     * @return 一覧画面
     */
    @GetMapping("/to_index")
    public String toIndex(Model model){
        return index(model);
    }

    /**
     * 削除確認画面から一覧画面に戻る
     * @param model Modelオブジェクト
     * @return 一覧画面
     */
    @PostMapping(value = "/delete", params = "back")
    public String confirmDeleteBack(Model model){
        return index(model);
    }

    /**
     * 追加処理を行う画面に遷移する
     * @param model Modelオブジェクト
     * @return 入力・更新画面へのパス
     */
    @PostMapping("/add")
    public String add(Model model){
        model.addAttribute("demoForm", new DemoForm());
        return "input";
    }

    /**
     * エラーチェックを行い、エラーが無ければ確認画面に遷移し、
     * エラーがあれば入力画面のままとする
     * @param demoForm Formオブジェクト
     * @param result バインド結果
     * @return 確認画面または入力画面へのパス
     */
    @PostMapping(value = "/confirm", params = "next")
    public String confirm(@Validated DemoForm demoForm, BindingResult result){
        //formオブジェクトのチェック処理を行う
        if(result.hasErrors()){
            //エラーがある場合は、入力画面のままとする
            return "input";
        }
        //アノテーション以外のチェック処理を行い、画面遷移する
        return checkOthers(demoForm, result, "confirm");
    }

    /**
     * 一覧画面に戻る
     * @param model Modelオブジェクト
     * @return 一覧画面の表示処理
     */
    @PostMapping(value = "/confirm", params = "back")
    public String confirmBack(Model model){
        return index(model);
    }

    /**
     * エラーチェックを行い、エラーが無ければ完了画面へのリダイレクトパスに遷移し、
     * エラーがあれば入力画面に戻す
     * @param demoForm Formオブジェクト
     * @param result バインド結果
     * @return 完了画面へのリダイレクトパスまたは入力画面へのパス
     */
    @PostMapping(value = "/send", params = "next")
    @Transactional(readOnly = false)
    public String send(@Validated DemoForm demoForm, BindingResult result){
        //formオブジェクトのチェック処理を行う
        if(result.hasErrors()){
            //エラーがある場合は、入力画面のままとする
            return "input";
        }
    	//アノテーション以外のチェック処理を行い、
    	//エラーがなければ、更新・追加処理を行う
        String normalPath = "redirect:/complete";
        String checkOthersPath = checkOthers(demoForm, result, normalPath);
        if(normalPath.equals(checkOthersPath)){
            //更新・追加処理を行うエンティティを生成
            UserData userData = getUserData(demoForm);
            //更新・追加処理
            repository.saveAndFlush(userData);
        }
        return checkOthersPath;
    }

    /**
     * 完了画面に遷移する
     * @param sessionStatus セッションステータス
     * @return 完了画面
     */
    @GetMapping("/complete")
    public String complete(SessionStatus sessionStatus){
        //セッションオブジェクトを破棄
        sessionStatus.setComplete();
        return "complete";
    }

    /**
     * 入力画面に戻る
     * @return 入力画面
     */
    @PostMapping(value = "/send", params = "back")
    public String sendBack(){
        return "input";
    }

    /**
     * アノテーション以外のチェック処理を行い、画面遷移先を返却
     * @param demoForm Formオブジェクト
     * @param result バインド結果
     * @param normalPath 正常時の画面遷移先
     * @return 確認画面または入力画面へのパス
     */
    private String checkOthers(DemoForm demoForm, BindingResult result
                             , String normalPath){
        //** アノテーション以外のチェック処理を行う
        //** エラーがある場合は、エラーメッセージ・(エラー時に赤反転するための)
        //** エラーフィールドの設定を行い、入力画面のままとする
        //生年月日のチェック処理
        int checkDate = DateCheckUtil.checkDate(demoForm.getBirthYear()
                , demoForm.getBirthMonth(), demoForm.getBirthDay());
        switch(checkDate){
            case 1:
                //生年月日_年が空文字の場合のエラー処理
                result.rejectValue("birthYear", "validation.date-empty"
                        , new String[]{"生年月日_年"}, "");
                return "input";
            case 2:
                //生年月日_月が空文字の場合のエラー処理
                result.rejectValue("birthMonth", "validation.date-empty"
                        , new String[]{"生年月日_月"}, "");
                return "input";
            case 3:
                //生年月日_日が空文字の場合のエラー処理
                result.rejectValue("birthDay", "validation.date-empty"
                        , new String[]{"生年月日_日"}, "");
                return "input";
            case 4:
                //生年月日の日付が不正な場合のエラー処理
                result.rejectValue("birthYear", "validation.date-invalidate");
                //生年月日_月・生年月日_日は、エラーフィールドの設定を行い、
                //メッセージを空文字に設定している
                result.rejectValue("birthMonth", "validation.empty-msg");
                result.rejectValue("birthDay", "validation.empty-msg");
                return "input";
            case 5:
                //生年月日の日付が未来日の場合のエラー処理
                result.rejectValue("birthYear", "validation.date-future");
                //生年月日_月・生年月日_日は、エラーフィールドの設定を行い、
                //メッセージを空文字に設定している
                result.rejectValue("birthMonth", "validation.empty-msg");
                result.rejectValue("birthDay", "validation.empty-msg");
                return "input";
            default:
                //性別が不正に書き換えられていないかチェックする
                if(!demoForm.getSexItems().keySet().contains(demoForm.getSex())){
                    result.rejectValue("sex", "validation.sex-invalidate");
                    return "input";
                }
                //エラーチェックに問題が無いので、正常時の画面遷移先に遷移
                return normalPath;
        }
    }

    /**
     * DemoFormオブジェクトに引数のユーザーデータの各値を設定する
     * @param userData ユーザーデータ
     * @return DemoFormオブジェクト
     */
    private DemoForm getDemoForm(UserData userData){
        DemoForm demoForm = new DemoForm();
        demoForm.setId(String.valueOf(userData.getId()));
        demoForm.setName(userData.getName());
        demoForm.setBirthYear(String.valueOf(userData.getBirthY()));
        demoForm.setBirthMonth(String.valueOf(userData.getBirthM()));
        demoForm.setBirthDay(String.valueOf(userData.getBirthD()));
        demoForm.setSex(userData.getSex());
        return demoForm;
    }

    /**
     * UserDataオブジェクトに引数のフォームの各値を設定する
     * @param demoForm DemoFormオブジェクト
     * @return ユーザーデータ
     */
    private UserData getUserData(DemoForm demoForm){
        UserData userData = new UserData();
        if(!DateCheckUtil.isEmpty(demoForm.getId())){
            userData.setId(Long.valueOf(demoForm.getId()));
        }
        userData.setName(demoForm.getName());
        userData.setBirthY(Integer.valueOf(demoForm.getBirthYear()));
        userData.setBirthM(Integer.valueOf(demoForm.getBirthMonth()));
        userData.setBirthD(Integer.valueOf(demoForm.getBirthDay()));
        userData.setSex(demoForm.getSex());
        return userData;
    }

}

C(Create)・R(Read)・U(Update)・D(Delete)を一通り含むOracle接続処理は、このコントローラクラス内でUserDataRepositoryインタフェース内のメソッドを呼び出すことで実装している。

また、DemoFormオブジェクト・DemoFormオブジェクトのリストの更新は、ModelクラスのaddAttributeメソッドを呼び出すことで行っている。

その他のソースコード内容は、以下のサイトを参照のこと。
https://github.com/purin-it/java/tree/master/spring-boot-crud-2/demo

要点まとめ

  • SQLログ・バインドパラメータの出力定義は、application.propertiesに記載する。
  • C(Create)・R(Read)・U(Update)・D(Delete)を一通り含むOracle接続処理は、JpaRepositoryを継承したインタフェース内のメソッドを呼び出すことで実装している。
  • DemoFormオブジェクト・DemoFormオブジェクトのリストの更新は、ModelクラスのaddAttributeメソッドを呼び出すことで行える。