Spring Boot DB連携

Spring BootでJNDIによるDBアクセスを実装してみた

今回は、JNDI(Java Naming and Directory Interface)を利用したデータベースアクセスを実行してみたので、そのサンプルプログラムを共有する。

なお、JNDIとは、コンピュータネットワーク上にあらかじめ登録されたデータやオブジェクトを、名前で発見し参照するための標準的なインターフェース仕様を定義したものをいう。

前提条件

下記記事の実装が完了していること

SQLログ出力内容をカスタマイズしてみた今回は、SQLログ出力内容をカスタマイズし、SQLの実行時間や呼出メソッドをSQLログに出力してみたので、そのサンプルプログラムを共有す...

サンプルプログラムの内容

今回のサンプルプログラムの構成は以下の通り。
サンプルプログラムの構成
なお、上記赤枠が前提条件のプログラムと変更になった箇所で、「DemoDbConfig.java」「DemoDbRepository.java」が新規で作成したプログラムで、他は変更したプログラムとなる。また、MyBatisに関連する「UserDataMapper.java」「UserDataMapper.xml」「DemoSqlInvocation.java」は削除している。

build.gradleの内容は以下の通りで、mybatisについての設定を削除し、tomcat-jdbcを利用するための設定を追加している。

plugins {
	id 'org.springframework.boot' version '2.1.7.RELEASE'
	id 'java'
}

apply plugin: 'io.spring.dependency-management'

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
	mavenCentral()
}

configurations {
	//log4j2を利用するため、Spring BootデフォルトのLogbackを利用しないよう設定
	all*.exclude module : 'spring-boot-starter-logging'
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	compileOnly 'org.projectlombok:lombok:1.18.10'
	annotationProcessor 'org.projectlombok:lombok:1.18.10'
	compile files('lib/ojdbc6.jar')
	compile group: 'org.springframework.data', name: 'spring-data-commons-core', version: '1.1.0.RELEASE'
	//log4j2を利用するための設定
	compile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.12.1'
	compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.12.1'
	//AOPを利用するための設定
	implementation 'org.springframework.boot:spring-boot-starter-aop'
	//log4j2の設定でymlファイルを利用するための設定
	compile group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-yaml', version: '2.10.1'
	compile group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.10.1'
	compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.10.1'
	//Apache Common JEXLを利用するための設定
	compile group: 'org.apache.commons', name: 'commons-jexl3', version: '3.0'
	//tomcat-jdbcを利用するための設定
	compile group: 'org.apache.tomcat', name: 'tomcat-jdbc', version: '9.0.30'
}

application.ymlは以下の通りで、「jdbc/demoJndiResource」というJNDI名を追加している。

server:
  port: 8084
# DB接続情報
spring:
  datasource:
    name: jdbc/demoJndiResource
    url: jdbc:oracle:thin:@localhost:1521:xe
    username: USER01
    password: USER01
    driverClassName: oracle.jdbc.driver.OracleDriver
# 一覧画面で1ページに表示する行数
demo:
  list:
    pageSize: 2



フリーランスエンジニアのエージェントは就業中でも無料で登録できるITエンジニアには、フリーランスという働き方がある。 フリーランスとは、会社や団体などに所属せず、仕事に応じて自由に契約する人のこ...

さらに、データソースの設定クラスは以下の通りで、組み込みTomcatにデータベースJNDI接続情報を登録している。

package com.example.demo;

import org.apache.catalina.Context;
import org.apache.catalina.startup.Tomcat;
import org.apache.tomcat.util.descriptor.web.ContextResource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.embedded.tomcat.TomcatWebServer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;

/**
 * データソース設定クラス
 */
@Configuration
public class DemoDbConfig {

    /**
     * データソース名(application.propertiesから取得)
     */
    @Value("${spring.datasource.name}")
    private String jndiName;

    /**
     * データベースURL(application.propertiesから取得)
     */
    @Value("${spring.datasource.url}")
    private String url;

    /**
     * データベースユーザー名(application.propertiesから取得)
     */
    @Value("${spring.datasource.username}")
    private String username;

    /**
     * データベースパスワード(application.propertiesから取得)
     */
    @Value("${spring.datasource.password}")
    private String password;

    @Bean
    public TomcatServletWebServerFactory tomcatFactory(){
        //組み込みTomcatにデータベースJNDI接続情報を登録
        return new TomcatServletWebServerFactory(){
            @Override
            protected TomcatWebServer getTomcatWebServer(Tomcat tomcat){
                tomcat.enableNaming();
                return super.getTomcatWebServer(tomcat);
            }
            @Override
            protected void postProcessContext(Context context) {
                ContextResource resource = new ContextResource();
                resource.setProperty("factory", "org.apache.tomcat.jdbc.pool.DataSourceFactory");
                resource.setName(jndiName);
                resource.setType(DataSource.class.getName());
                resource.setProperty("driverClassName", "oracle.jdbc.driver.OracleDriver");
                resource.setProperty("url", url);
                resource.setProperty("username", username);
                resource.setProperty("password", password);
                context.getNamingResources().addResource(resource);
            }
        };
    }
}



また、SQLによるデータベースアクセス処理は以下の通りで、DBコネクションの取得はgetDbConnectionメソッド内で行っていて、InitialContextクラスのlookupメソッドによりJNDI接続情報の取得を行っている。

package com.example.demo;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Repository;

import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.Statement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

@Repository
public class DemoDbRepository {

    //ログ出力のためのクラス
    private Logger logger = LogManager.getLogger(DemoDbRepository.class);

    /**
     * データソース名(application.propertiesから取得)
     */
    @Value("${spring.datasource.name}")
    private String jndiName;

    /**
     * 指定したIDをもつユーザーデータテーブル(user_data)のデータを取得するSQL
     */
    private static final String findByIdSql = "SELECT id, name"
            + ", birth_year AS birthY, birth_month AS birthM, birth_day AS birthD "
            + ", sex, memo FROM USER_DATA WHERE id = ?";

    /**
     * 指定したIDをもつユーザーデータテーブル(user_data)のデータを削除するSQL
     */
    private static final String deleteSql = "DELETE FROM USER_DATA WHERE id = ?";

    /**
     * 指定したユーザーデータテーブル(user_data)のデータを追加するSQL
     */
    private static final String insertSql = "INSERT INTO USER_DATA ( id, name "
            + ", birth_year, birth_month, birth_day, sex, memo ) "
            + " VALUES ( ?, ?, ?, ?, ?, ?, ? ) ";

    /**
     * 指定したユーザーデータテーブル(user_data)のデータを更新するSQL
     */
    private static final String updateSql = "UPDATE USER_DATA SET name = ? "
            + ", birth_year = ?, birth_month = ?, birth_day = ?, sex = ?"
            + ", memo = ? WHERE id = ?";

    /**
     * ユーザーデータテーブル(user_data)の最大値IDを取得するSQL
     */
    private static final String findMaxIdSql
            = "SELECT NVL(max(id), 0) AS maxId FROM USER_DATA";

    /**
     * ユーザーデータテーブル(user_data)から検索条件に合うデータを取得する
     * @param searchForm 検索用Formオブジェクト
     * @param pageable ページネーションオブジェクト
     * @return ユーザーデータテーブル(user_data)の検索条件に合うデータ
     */
    public List<UserData> findBySearchForm(SearchForm searchForm, Pageable pageable){
        List<UserData> userDataList = new ArrayList<>();
        Connection conn = getDbConnection();
        PreparedStatement stmt = null;
        ResultSet rs = null;
        try{
            String selectSql = getFindBySearchSql(searchForm, pageable);
            stmt = conn.prepareStatement(selectSql);
            logger.info(selectSql);
            rs = stmt.executeQuery();
            while(rs.next()){
                UserData userData = new UserData();
                userData.setId(rs.getLong("id"));
                userData.setName(rs.getString("name"));
                userData.setBirthY(rs.getInt("birthY"));
                userData.setBirthM(rs.getInt("birthM"));
                userData.setBirthD(rs.getInt("birthD"));
                userData.setSex(String.valueOf(rs.getInt("sex")));
                userData.setMemo(rs.getString("memo"));
                userData.setSex_value(rs.getString("sex_value"));
                userDataList.add(userData);
            }
        }catch (SQLException e){
            e.printStackTrace();
        }finally {
            closeResultSet(rs);
            closeStatement(stmt);
            closeDbConnection(conn);
        }
        return userDataList;
    }

    /**
     * ユーザーデータテーブル(user_data)から検索条件に合うデータを取得するSQLを生成する
     * @param searchForm 検索用Formオブジェクト
     * @param pageable ページネーションオブジェクト
     * @return ユーザーデータテーブル(user_data)から取得するSQL
     */
    private String getFindBySearchSql(SearchForm searchForm, Pageable pageable){
        StringBuilder sb = new StringBuilder();
        sb.append("SELECT u.id, u.name, u.birth_year as birthY, u.birth_month as birthM ");
        sb.append(", u.birth_day as birthD, u.sex, u.memo, u.sex_value ");
        sb.append("FROM ( SELECT ");
        sb.append("u1.id, u1.name, u1.birth_year, u1.birth_month, u1.birth_day ");
        sb.append(", u1.sex, u1.memo, m.sex_value, ROW_NUMBER() OVER (ORDER BY u1.id) AS rn ");
        sb.append(" FROM USER_DATA u1, M_SEX m ");
        sb.append(" WHERE u1.sex = m.sex_cd ");
        if(!DateCheckUtil.isEmpty(searchForm.getSearchName())){
            sb.append(" AND u1.name like '%" + searchForm.getSearchName() + "%' ");
        }
        if(!DateCheckUtil.isEmpty(searchForm.getFromBirthYear())){
            sb.append(" AND " + searchForm.getFromBirthYear()
                    + " || lpad(" + searchForm.getFromBirthMonth() +  ", 2, '0')"
                    + " || lpad(" + searchForm.getFromBirthDay() +  ", 2, '0')"
                    + " <= u1.birth_year || lpad(u1.birth_month, 2, '0') "
                    + "|| lpad(u1.birth_day, 2, '0') ");
        }
        if(!DateCheckUtil.isEmpty(searchForm.getToBirthYear())){
            sb.append(" AND u1.birth_year || lpad(u1.birth_month, 2, '0') "
                    + "|| lpad(u1.birth_day, 2, '0') "
                    + " <= " + searchForm.getToBirthYear()
                    + "|| lpad(" + searchForm.getToBirthMonth() +  ", 2, '0')"
                    + "|| lpad(" + searchForm.getToBirthDay() + ", 2, '0') ");
        }
        if(!DateCheckUtil.isEmpty(searchForm.getSearchSex())){
            sb.append(" AND u1.sex = " + searchForm.getSearchSex());
        }
        sb.append(" ORDER BY u1.id");
        sb.append(" ) u");
        if(pageable != null && pageable.getPageSize() > 0){
            sb.append(" WHERE u.rn BETWEEN " + pageable.getOffset()
                    + " AND " + (pageable.getOffset() + pageable.getPageSize() - 1));
        }
        return sb.toString();
    }

    /**
     * 指定したIDをもつユーザーデータテーブル(user_data)のデータを取得する
     * @param id ID
     * @return ユーザーデータテーブル(user_data)の指定したIDのデータ
     */
    public UserData findById(Long id){
        UserData userData = null;
        Connection conn = getDbConnection();
        PreparedStatement stmt = null;
        ResultSet rs = null;
        try{
            stmt = conn.prepareStatement(findByIdSql);
            stmt.setLong(1, id);
            logger.info(findByIdSql);
            logger.info("Parameters: " + id + "(Long)");
            rs = stmt.executeQuery();
            while(rs.next()){
                userData = new UserData();
                userData.setId(rs.getLong("id"));
                userData.setName(rs.getString("name"));
                userData.setBirthY(rs.getInt("birthY"));
                userData.setBirthM(rs.getInt("birthM"));
                userData.setBirthD(rs.getInt("birthD"));
                userData.setSex(String.valueOf(rs.getInt("sex")));
                userData.setMemo(rs.getString("memo"));
            }
        }catch (SQLException e){
            e.printStackTrace();
        }finally {
            closeResultSet(rs);
            closeStatement(stmt);
            closeDbConnection(conn);
        }
        return userData;
    }

    /**
     * 指定したIDをもつユーザーデータテーブル(user_data)のデータを削除する
     * @param id ID
     */
    public void deleteById(Long id){
        Connection conn = getDbConnection();
        PreparedStatement stmt = null;
        try{
            stmt = conn.prepareStatement(deleteSql);
            stmt.setLong(1, id);
            logger.info(deleteSql);
            logger.info("Parameters: " + id + "(Long)");
            stmt.executeUpdate();
            commitDbConnection(conn);
        }catch (SQLException e){
            rollbackDbConnection(conn);
            e.printStackTrace();
        }finally {
            closeStatement(stmt);
            closeDbConnection(conn);
        }
    }

    /**
     * 指定したユーザーデータテーブル(user_data)のデータを追加する
     * @param userData ユーザーデータテーブル(user_data)の追加データ
     */
    public void create(UserData userData){
        Connection conn = getDbConnection();
        PreparedStatement stmt = null;
        try{
            stmt = conn.prepareStatement(insertSql);
            stmt.setLong(1, userData.getId());
            stmt.setString(2, userData.getName());
            stmt.setInt(3, userData.getBirthY());
            stmt.setInt(4, userData.getBirthM());
            stmt.setInt(5, userData.getBirthD());
            stmt.setString(6, userData.getSex());
            stmt.setString(7, userData.getMemo());
            logger.info(insertSql);
            logger.info("Parameters: " + userData.getId() + "(Long), "
                    + userData.getName() + "(String), "
                    + userData.getBirthY() + "(Integer), "
                    + userData.getBirthM() + "(Integer), "
                    + userData.getBirthD() + "(Integer), "
                    + userData.getSex() + "(String), "
                    + userData.getMemo() + "(String) ");
            stmt.executeUpdate();
            commitDbConnection(conn);
        }catch (SQLException e){
            rollbackDbConnection(conn);
            e.printStackTrace();
        }finally {
            closeStatement(stmt);
            closeDbConnection(conn);
        }
    }

    /**
     * 指定したユーザーデータテーブル(user_data)のデータを更新する
     * @param userData ユーザーデータテーブル(user_data)の更新データ
     */
    public void update(UserData userData){
        Connection conn = getDbConnection();
        PreparedStatement stmt = null;
        try{
            stmt = conn.prepareStatement(updateSql);
            stmt.setString(1, userData.getName());
            stmt.setInt(2, userData.getBirthY());
            stmt.setInt(3, userData.getBirthM());
            stmt.setInt(4, userData.getBirthD());
            stmt.setString(5, userData.getSex());
            stmt.setString(6, userData.getMemo());
            stmt.setLong(7, userData.getId());
            logger.info(updateSql);
            logger.info("Parameters: " + userData.getName() + "(String), "
                    + userData.getBirthY() + "(Integer), "
                    + userData.getBirthM() + "(Integer), "
                    + userData.getBirthD() + "(Integer), "
                    + userData.getSex() + "(String), "
                    + userData.getMemo() + "(String), "
                    + userData.getId() + "(Long)");
            stmt.executeUpdate();
            commitDbConnection(conn);
        }catch (SQLException e){
            rollbackDbConnection(conn);
            e.printStackTrace();
        }finally {
            closeStatement(stmt);
            closeDbConnection(conn);
        }
    }

    /**
     * ユーザーデータテーブル(user_data)の最大値IDを取得する
     * @return ユーザーデータテーブル(user_data)の最大値ID
     */
    public long findMaxId(){
        long maxId = 0;
        Connection conn = getDbConnection();
        PreparedStatement stmt = null;
        ResultSet rs = null;
        try{
            stmt = conn.prepareStatement(findMaxIdSql);
            logger.info(findMaxIdSql);
            rs = stmt.executeQuery();
            while(rs.next()){
                maxId = rs.getLong("maxId");
            }
        }catch (SQLException e){
            e.printStackTrace();
        }finally {
            closeResultSet(rs);
            closeStatement(stmt);
            closeDbConnection(conn);
        }
        return maxId;
    }

    /**
     * データベースコネクションを取得する
     * @return データベースコネクション
     */
    private Connection getDbConnection(){
        Connection conn = null;
        try{
            // DemoDbConfig.javaで登録したjndiNameに紐付くデータベース接続情報を取得
            InitialContext context = new InitialContext();
            DataSource dataSource = (DataSource)context.lookup("java:comp/env/" + jndiName);
            conn = dataSource.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (NamingException e) {
            e.printStackTrace();
        }
        return conn;
    }

    /**
     * ResultSetオブジェクトを閉じる
     * @param rs Statementオブジェクト
     */
    private void closeResultSet(ResultSet rs){
        try{
            if(rs != null){
                rs.close();
            }
        }catch(SQLException e){
            e.printStackTrace();
        }
    }

    /**
     * データベースコネクションをコミットする
     * @param conn データベースコネクション
     */
    private void commitDbConnection(Connection conn){
        try{
            if(conn != null){
                conn.commit();
            }
        }catch(SQLException e){
            e.printStackTrace();
        }
    }

    /**
     * データベースコネクションをロールバックする
     * @param conn データベースコネクション
     */
    private void rollbackDbConnection(Connection conn){
        try{
            if(conn != null){
                conn.rollback();
            }
        }catch(SQLException e){
            e.printStackTrace();
        }
    }

    /**
     * Statementオブジェクトを閉じる
     * @param stmt Statementオブジェクト
     */
    private void closeStatement(Statement stmt){
        try{
            if(stmt != null){
                stmt.close();
            }
        }catch(SQLException e){
            e.printStackTrace();
        }
    }

    /**
     * データベースコネクションを閉じる
     * @param conn データベースコネクション
     */
    private void closeDbConnection(Connection conn){
        try{
            if(conn != null){
                conn.close();
            }
        }catch(SQLException e){
            e.printStackTrace();
        }
    }
}



さらに、サービスの実装クラスの内容は以下の通りで、SQL実行を先ほどのDemoDbRepository.javaクラスのpublicメソッドを利用するよう修正している。

package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.BindingResult;
import org.springframework.data.domain.Pageable;
import java.util.ArrayList;
import java.util.List;

@Service
public class DemoServiceImpl implements DemoService{

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

    /**
     * 1ページに表示する行数(application.propertiesから取得)
     */
    @Value("${demo.list.pageSize}")
    private String listPageSize;

    /**
     * {@inheritDoc}
     */
    @Override
    public List<DemoForm> demoFormList(SearchForm searchForm, Pageable pageable) {
        List<DemoForm> demoFormList = new ArrayList<>();
        //ユーザーデータテーブル(user_data)から検索条件に合うデータを取得する
        List<UserData> userDataList = repository.findBySearchForm(searchForm, pageable);
        for (UserData userData : userDataList) {
            demoFormList.add(getDemoForm(userData));
        }
        return demoFormList;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public DemoForm findById(String id) {
        Long longId = stringToLong(id);
        UserData userData = repository.findById(longId);
        return getDemoForm(userData);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Transactional(readOnly = false)
    public void deleteById(String id){
        Long longId = stringToLong(id);
        repository.deleteById(longId);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Transactional(readOnly = false)
    public void createOrUpdate(DemoForm demoForm){
        //更新・追加処理を行うエンティティを生成
        UserData userData = getUserData(demoForm);
        //追加・更新処理
        if(demoForm.getId() == null){
            userData.setId(repository.findMaxId() + 1);
            repository.create(userData);
        }else{
            repository.update(userData);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String checkForm(DemoForm demoForm, BindingResult result, String normalPath){
        //formオブジェクトのチェック処理を行う
        if(result.hasErrors()){
            //エラーがある場合は、入力画面のままとする
            return "input";
        }
        //生年月日の日付チェック処理を行う
        //エラーがある場合は、エラーメッセージ・エラーフィールドの設定を行い、
        //入力画面のままとする
        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;
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String checkSearchForm(SearchForm searchForm, BindingResult result){
        int checkDate =DateCheckUtil.checkSearchForm(searchForm);
        switch (checkDate){
            case 1:
                //生年月日_fromが不正な場合のエラー処理
                result.rejectValue("fromBirthYear", "validation.date-invalidate-from");
                result.rejectValue("fromBirthMonth", "validation.empty-msg");
                result.rejectValue("fromBirthDay", "validation.empty-msg");
                return "search";
            case 2:
                //生年月日_toが不正な場合のエラー処理
                result.rejectValue("toBirthYear", "validation.date-invalidate-to");
                result.rejectValue("toBirthMonth", "validation.empty-msg");
                result.rejectValue("toBirthDay", "validation.empty-msg");
                return "search";
            case 3:
                //生年月日_from>生年月日_toの場合のエラー処理
                result.rejectValue("fromBirthYear", "validation.date-invalidate-from-to");
                result.rejectValue("fromBirthMonth", "validation.empty-msg");
                result.rejectValue("fromBirthDay", "validation.empty-msg");
                result.rejectValue("toBirthYear", "validation.empty-msg");
                result.rejectValue("toBirthMonth", "validation.empty-msg");
                result.rejectValue("toBirthDay", "validation.empty-msg");
                return "search";
            default:
                //正常な場合はnullを返却
                return null;
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Pageable getPageable(int pageNumber){
        Pageable pageable = new Pageable() {
            @Override
            public int getPageNumber() {
                //現在ページ数を返却
                return pageNumber;
            }

            @Override
            public int getPageSize() {
                //1ページに表示する行数を返却
                //listPageSizeは、本プログラムの先頭に定義している
                return Integer.parseInt(listPageSize);
            }

            @Override
            public int getOffset() {
                //表示開始位置を返却
                //例えば、1ページに2行表示する場合の、2ページ目の表示開始位置は
                //(2-1)*2+1=3 で計算される
                return ((pageNumber - 1) * Integer.parseInt(listPageSize) + 1);
            }

            @Override
            public Sort getSort() {
                //ソートは使わないのでnullを返却
                return null;
            }
        };
        return pageable;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int getAllPageNum(SearchForm searchForm) {
        //1ページに表示する行数を取得
        int listPageSizeNum = Integer.parseInt(listPageSize);
        if(listPageSizeNum == 0){
            return 1;
        }
        //一覧画面に表示する全データを取得
        //第二引数のpageableにnullを設定することで、一覧画面に表示する全データが取得できる
        List<UserData> userDataList = repository.findBySearchForm(searchForm, null);
        //全ページ数を計算
        //例えば、1ページに2行表示する場合で、全データ件数が5の場合、
        //(5+2-1)/2=3 と計算される
        int allPageNum = (userDataList.size() + listPageSizeNum - 1) / listPageSizeNum;
        return allPageNum == 0 ? 1 : allPageNum;
    }

    /**
     * DemoFormオブジェクトに引数のユーザーデータの各値を設定する
     * @param userData ユーザーデータ
     * @return DemoFormオブジェクト
     */
    private DemoForm getDemoForm(UserData userData){
        if(userData == null){
            return null;
        }
        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());
        demoForm.setMemo(userData.getMemo());
        demoForm.setSex_value(userData.getSex_value());
        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());
        userData.setMemo(demoForm.getMemo());
        userData.setSex_value(demoForm.getSex_value());
        return userData;
    }

    /**
     * 引数の文字列をLong型に変換する
     * @param id ID
     * @return Long型のID
     */
    private Long stringToLong(String id){
        try{
            return Long.parseLong(id);
        }catch(NumberFormatException ex){
            return null;
        }
    }

}

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

サンプルプログラムの実行結果

画面の動作内容は、下記記事と同じ実行結果になる。

SQLログ出力内容をカスタマイズしてみた今回は、SQLログ出力内容をカスタマイズし、SQLの実行時間や呼出メソッドをSQLログに出力してみたので、そのサンプルプログラムを共有す...

また、ログ出力例は以下の通りで、SQL実行内容も含めログ出力される。
コンソールログ出力例

要点まとめ

  • JNDIを利用したDB接続を実装するには、あらかじめ組み込みTomcatにデータベースJNDI接続情報を登録しておき、InitialContextクラスのlookupメソッドで、そのJNDI接続情報を取得すればよい。