end0tknr's kipple - web写経開発

太宰府天満宮の狛犬って、妙にカワイイ

pythonで、urlの死活監視を行い、NGであれば、sendmail を外部コマンドで通知

以下の通りで、ポイントは

  • urllib.request による HTTP GET
  • 「/usr/sbin/sendmail -t」によるmail送信
  • subprocess による標準入力渡し
  • python3 の標準moduleで実現OK

かと思います。

#!/usr/bin/python3
# -*- coding: utf-8 -*-

import json
import os
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
import sys
import time, datetime
import urllib.request
import subprocess
from subprocess import PIPE

ret_urls = [
    "https://ないしょ1.jp",
    "https://ないしょ2.jp" ]

http_timeout = 10    #sec
retry_sleep =  10    #sec
retry_max   =   5    #times

chk_history_file \
= os.path.dirname(os.path.abspath(__file__)) + "/chk_alive_urls.history"

mail_cond = {
    "cmd" :"/usr/sbin/sendmail -t",
    "from":"ないしょ@ないしょ.com",
    # 宛先複数は「,」で接続
    "to"  :"ないしょ@ないしょ.com",
    "subject": "!!!! server has DOWN URL !!!!" }

def main():
    # 過去のcheck結果をload
    chk_history = load_chk_history()

    # 実際にaccessすることで、check
    for req_url in ret_urls:
        if not req_url in chk_history:
            chk_history[req_url] = {'last_check': '',
                                    'repeat_ok' : 0,
                                    'repeat_ng' : 0  }
        i = 0
        while i < retry_max:
            i += 1
            time.sleep( retry_sleep )
            
            datetime_str = \
                datetime.datetime.fromtimestamp(time.time()).strftime(
                    '%Y-%m-%d %H:%M' )
            res = http_get(req_url)
            if res:
                break
            
            
        chk_history[req_url]['last_check'] = datetime_str
        
        if res:
            chk_history[req_url]['repeat_ok'] += 1
            chk_history[req_url]['repeat_ng']  = 0
        else:
            chk_history[req_url]['repeat_ok']  = 0
            chk_history[req_url]['repeat_ng'] += 1
        
    # check結果を保存
    save_chk_history(chk_history)

    # NGなurlが1つでもあれば、通知メール通知
    for req_url in chk_history:
        if chk_history[req_url]['repeat_ng']:
            return notify_chk_result(chk_history)
    
        
def notify_chk_result(chk_history):

    msg_head_body = ""
    msg_head_body += "From: "   +mail_cond["from"]+"\n"
    msg_head_body += "To: "     +mail_cond["to"]+"\n"
    msg_head_body += "Subject: "+mail_cond["subject"]+"\n"
    msg_head_body += "\n"
    msg_head_body += "PLEASE CHECK BELOW REPEAT_NG !!\n"
    msg_head_body += json.dumps(chk_history, indent=2)

    proc = subprocess.run(mail_cond["cmd"],
                          shell=True,
                          input=msg_head_body.encode(), #★要byte列化
                          stdout=PIPE,
                          stderr=PIPE)
    
        
def save_chk_history(chk_history):
    with open(chk_history_file, mode='w') as file:
        json.dump(chk_history, file, indent=2)
  
def load_chk_history():

    if not os.path.exists(chk_history_file):
        return {}
    
    fh = open(chk_history_file,'r')
    fh_content = fh.read()
    if len(fh_content) == 0:
        return {}

    return json.loads(fh_content)

def http_get(req_url):
    req = urllib.request.Request(req_url)
    try:
        res = urllib.request.urlopen(req,
                                     data=None,
                                     timeout=http_timeout)
    except urllib.error.HTTPError as err:
        print("WARN",err.code, req_url,file=sys.stderr)
        return None
    except urllib.error.URLError as err:
        print("ERROR",err.reason, req_url,file=sys.stderr)
        return None

    # print("DONE HTTP",res.getcode(), res.geturl() )
    # print(res.read() )
    # res.close()
    return res
    

if __name__ == '__main__':
    main()

情報システム部門の役割・機能

改訂版 情報システム部 (図解でわかる部門の仕事) | 修一, 小野, 憲司, 松枝, 実, 鈴木, 和宣, 渡辺 |本 | 通販 | Amazon

インターネットで検索すると、様々ありますが、上記によれば、以下の通り。

# 項目 説明
1 情報戦略策定 経営目的達成の為のIT活用を提案
2 システム企画 企業活動に役立つシステムの整備を計画
3 システム開発・導入 システム化計画を実行
4 システム運用・管理 安全かつ効率的なシステム稼働を図る
5 システム保守 システムの改良・改善を行う
6 システム評価 システムの現状を調査し改善を図る
7 開発プロセス標準化 システム開発の生産性を高める
8 アウトソーシング管理 アウトソーシングの有効性を高める
9 IT情報提供・教育 利用者の情報活用度を高める
10 IT投資マネジメント 効率的な情報化投資を推進
11 情報セキュリティ管理 情報セキュリティレベルを高める

JNSA (日本ネットワークセキュリティ協会)による SecBoK (セキュリティ知識分野人材スキルマップ)

SecBoK 以外にも、様々、公開されています

セキュリティ知識分野(SecBoK)人材スキルマップ2021年版

今回の件と直接の関係はありませんが、 JNSAと、プライバシーマークのJIPDEC(日本情報経済社会推進協会)をよく混同します。

プライバシーマーク制度|一般財団法人日本情報経済社会推進協会(JIPDEC)

どのような情報が「個人情報」にあたりますか?

以前は、「氏名のみでは、個人情報に該当しない」だった気がしますが、 改めて確認すると、そうでもないみたい。

forEach() for java で、ループカンタを使用する場合、配列で定義しましょう

public List<MymemberMst> findByCondition(BeanMap beanMap) {
    String[] atriKeys = new String[beanMap.size()];
    Object[] atriVals = new Object[beanMap.size()];

    int i = 0; //★ココ
    beanMap.forEach((key, value)-> {
        atriKeys[ i ]  = key;
        atriVals[ i ] = value;
        i++;
    });
    :
}

↑こうでななく、↓こう

public List<MymemberMst> findByCondition(BeanMap beanMap) {
    String[] atriKeys = new String[beanMap.size()];
    Object[] atriVals = new Object[beanMap.size()];

    int[] i = { 0 }; //★ココ
    beanMap.forEach((key, value)-> {
        atriKeys[ i[0] ]  = key;
        atriVals[ i[0] ] = value;
        i[0]++;
    });
    :
}

経済産業省によるクラウドサービス利用のための情報セキュリティマネジメントガイドラインと、クラウドセキュリティガイドライン活用ガイドブック

メモ。

2013年のものですが、現在でも参考になります。

クラウドサービス利用のための情報セキュリティマネジメントガイドライン

http://www.meti.go.jp/policy/netsecurity/downloadfiles/cloudsec2013fy.pdf

クラウドセキュリティガイドライン活用ガイドブック

http://www.meti.go.jp/policy/netsecurity/downloadfiles/cloudseckatsuyou2013fy.pdf

org.seasar.struts.util.ResponseUtil for java にある download() の spring版

import org.seasar.struts.util.ResponseUtil;

is = new FileInputStream(file);
ResponseUtil.download(getDecodeFileName(fileName), is, (int)file.length());

seasar2 では、上記のように、ResponseUtil.download() でファイルダウンロードできましたが spring では、多分、以下。

ただし、excel のダウンロードに限っては、 apache poi に付属する AbstractXlsxView を利用する方法もあります。

package jp.end0tknr;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.IOUtils;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MyRestController {

    @Autowired
    XlsxOutService xlsxOutService;

    @RequestMapping(value="/index6")
    public void index6(
            HttpServletRequest request,
            HttpServletResponse response,
            Model model) {
        System.out.println("START index6");

        Workbook workBook = xlsxOutService.outXlsx();

        FileOutputStream out;
        try {
            workBook.write( new FileOutputStream("./TEXT_POI.XLSX") );
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return;
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }

        try {
            InputStream inputStream = new FileInputStream("./TEXT_POI.XLSX");
            OutputStream outputStream = response.getOutputStream();
            byte[] fileByteArray = IOUtils.toByteArray(inputStream);

            response.setContentType("application/octet-stream");
            response.setHeader(
                    "Content-Disposition",
                    "attachment; filename=TEXT_POI.XLSX");
            response.setContentLength(fileByteArray.length);

            outputStream.write(fileByteArray);
            outputStream.flush();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return;
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
    }
}

尚、上記では、IOUtils.toByteArray() を使用していますので、 pom.xml へ commons-io を追加してください。

<dependency>
  <groupId>commons-io</groupId>
  <artifactId>commons-io</artifactId>
  <version>2.9.0</version>
</dependency>

seasar2 for java における ServletContext 取得を spring 用に

ServletContext context = SingletonS2Container.getComponent(ServletContext.class);

↑こう書いていたものを、↓こう書きます。

ServletContext context =
        ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
        .getRequest().getServletContext();

apache poi for java による excel(xlsx)作成

過去、excel(xlsx)の読込や作成は、 perlpythonにて実施していますが、javaでもお試し。

参考url

インターネットで検索すると、上記の2番目が上位に表示されますが、 リファレンスとしては、少々、古いようですので、3番目を参照

apache poi 自体が扱いやすい構成ですので、以下のように書けば、OK

pom.xml

<dependencies>
  <dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>4.0.1</version>
  </dependency>
  <dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>4.0.1</version>
  </dependency>
</dependencies>

jp.end0tknr.XlsxOutService

package jp.end0tknr;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Font;
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.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.stereotype.Service;

@Service
public class XlsxOutService {

    public void outXlsx() {

        Workbook wBook = new XSSFWorkbook();  // for xlsx
        //Workbook wBook = new HSSFWorkbook(); // for xls

        Sheet wSheet = wBook.createSheet();

        // セルの書式の生成
        CellStyle cellStyle = wBook.createCellStyle();

        // フォント設定
        Font font = wBook.createFont();
        font.setFontName("MS ゴシック");
        font.setFontHeightInPoints((short)12); // size
        font.setUnderline(Font.U_SINGLE);      //下線
        font.setBold(true);                    //太字
        cellStyle.setFont(font);

        // 枠線/罫線の表示
        cellStyle.setBorderBottom(BorderStyle.THIN);

        // セルの結合
        wSheet.addMergedRegion(new CellRangeAddress(2, 3, 2, 4)); //行x2 , 列x2


        // 罫線の表示/非表示
        //wSheet.setDisplayGridlines(false);

        Row  outRow = wSheet.createRow(0);
        Cell outCell = outRow.createCell(0);

        outCell.setCellStyle(cellStyle);
        outCell.setCellValue("POIからの出力値");

        // 行高さ 調整
        outRow.setHeightInPoints(2 * wSheet.getDefaultRowHeightInPoints() );
        // セル幅 調整
        wSheet.setColumnWidth(3, 1024); // 1文字分の幅=256
        // セル幅 自動調整
        wSheet.autoSizeColumn(0, true);


        FileOutputStream out;
        try {
            out = new FileOutputStream("./TEXT_POI.XLSX");
            wBook.write(out);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return;
        } catch (IOException e) {
            // TODO 自動生成された catch ブロック
            e.printStackTrace();
            return;
        }
    }
}

org.apache.commons.lang3.StringUtils for java の splitByCharacterTypeCamelCase() で、 camel to snake 変換

以下のような感じかと思います。

import org.apache.commons.lang3.StringUtils;

// camel -> snake 変換
public String toSnakeStr(String camel) {
    String snake =
            StringUtils.join(
                    StringUtils.splitByCharacterTypeCamelCase(camel), "_")
            .toLowerCase();
    //数字の前には「_」不要
    snake = snake.replaceAll("(_)([0-9])", "$2");

    return snake;
}

(改x2) spring boot for java + mybatis で sql動的生成 - WHERE IN句 や OR句

(改) spring boot for java + mybatis で sql動的生成 - end0tknr's kipple - web写経開発

上記entryに対して、更に追記。

org.apache.ibatis.jdbc.SQL を更に試していますが、 WHERE IN句 や OR句 の使用方法が、よく分からなかった為、メモ。

package jp.end0tknr;

import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.jdbc.SQL;

public class UserMstSqlProvider {

    public String findByWhereIn(String userId, Integer[] kyotenIds) {

        SQL sql_1 = new SQL();
        sql_1.SELECT("*").FROM("user_mst").WHERE("user_xmileid LIKE '${userId}%'");

        //sql_1.WHERE("(is_delete=0 OR is_delete=1)");
    // ↑こう書きたくなかった為、↓こう書きましたが、これもちょっとなぁ...
        SQL sql_2 = new SQL();
        sql_2.SELECT().FROM();
        sql_2.WHERE("is_delete=0").OR().WHERE("is_delete=1");
        String sql_2str =
                sql_2.toString().replaceAll("^WHERE ","").replaceAll("\n"," ");
        sql_1.WHERE("("+sql_2str+")");

    // org.apache.ibatis.jdbc.SQL には、WHERE IN句 に特化した methodが
    // ないらしく、以下のように書きましたが、こちらも美しくない感じ
        String[] whereInArgs = new String[ kyotenIds.length ];

        for (int i = 0; i < kyotenIds.length; i++){
            whereInArgs[i] = "#{kyotenIds["+i+"]}";
        }
        sql_1.WHERE( "kyoten_id in ("+ String.join(",",whereInArgs) +")");
        sql_1.LIMIT(1);

        String sqlStr = sql_1.toString();
        System.out.println("SQL:" + sqlStr);

        return sqlStr;

    }

    // camel -> snake 変換
    public String toSnakeStr(String camel) {
        String snake =
                StringUtils.join(
                        StringUtils.splitByCharacterTypeCamelCase(camel), "_")
                .toLowerCase();
        //数字の前には「_」不要
        snake = snake.replaceAll("(_)([0-9])", "$2");

        return snake;
    }
}

上記の findByWhereIn(String userId, Integer[] kyotenIds) により、 以下のsqlが発行されます。

SQL:SELECT *
FROM user_mst
WHERE (user_xmileid LIKE '${userId}%' AND
      ((is_delete=0)  OR (is_delete=1)) AND
      kyoten_id in (#{kyotenIds[0]},#{kyotenIds[1]},#{kyotenIds[2]},#{kyotenIds[3]}) )
LIMIT 1

org.seasar.framework.beans.util.Beans の createAndCopy() を spring for java へ移植

seasar2/Beans.java at master · seasarorg/seasar2 · GitHub を spring 用に移植しようかと思いましたが、 org.apache.commons.beanutils.BeanUtils の copyProperties() で代替できそう。

import org.seasar.framework.beans.util.BeanMap;
import org.seasar.framework.beans.util.Beans;
import jp.co.sekisui.bukkenkoutei.cloneseasar.BeanMap;

BeanMap map = Beans.createAndCopy(BeanMap.class, orgMst).execute();

↑こうだったものを、↓こうします

import org.apache.commons.beanutils.BeanUtils;
import jp.co.sekisui.bukkenkoutei.cloneseasar.BeanMap;

BeanMap map = new BeanMap();
BeanUtils.copyProperties(orgMst, map);

Entity for java に annotation された @Column や @Id 情報を取得

「spring for java や、seasar2 では、 EntityManager.find(~) や、select().id(~) で、Entityを検索できますが、 これらは、idの型や名称と、どうやって把握しているのだろう?」と思い、調べてみた。

当初、javax.persistence.EntityManager.java の method を辿ればよいと、思っていましたが、 MUserEntity.class.getFields() と Field.getAnnotations() で取得できるみたい。

package jp.end0tknr;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.sql.Timestamp;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.data.jpa.repository.support.JpaEntityInformationSupport;
import org.springframework.data.repository.core.EntityInformation;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class MyController {

    @Autowired
    UserMstService userMstService;

    @Autowired
    MessageSource messageSource;
    @Autowired
    ItemNameProp itemNameProp;

    @PersistenceContext
    EntityManager entityManager;

    @RequestMapping(value="/index4")
    public String index4(Model model) {

        System.out.println("START index4");

        // entityクラスのfield一覧から、@Id 情報を取得
        Field[] fields1 =  MUserEntity.class.getFields();
        for (Field field1 : fields1) {
            System.out.println( field1 );

            Annotation[] annots1 = field1.getAnnotations();
            for (Annotation annot1 : annots1) {
                System.out.println( annot1 );
            }
        }

        // 一方、EntityInformation では、@Id の型は取得できるが
        // @Id の名称は取得できない
        EntityInformation entityInformation1 =
                JpaEntityInformationSupport.getEntityInformation(
                        MUserEntity.class,
                        entityManager);
        System.out.println( entityInformation1.getIdType() );
        System.out.println( entityInformation1.getJavaType() );


        // EntityManger による 検索例 1
        MUserEntity mUserEntity = entityManager.find(
                MUserEntity.class,
                "user@co.jp");
        System.out.println(mUserEntity);

        // EntityManger による 検索例 2 - pkeyが複数
        UserMstEntityPkey userMstEntityPkey = new UserMstEntityPkey();
        userMstEntityPkey.setUserSmileid("end0tknr1");
        userMstEntityPkey.setKyotenId(1);

        UserMstEntity userMstEntity = entityManager.find(
                UserMstEntity.class,
                userMstEntityPkey);
        System.out.println(userMstEntity);

        return "index";
    }
}

↑こう書くと、↓こうeclipseのコンソールに表示されます。

START index4
public java.lang.String jp.end0tknr.MUserEntity.userId
@javax.persistence.Id()
@javax.persistence.Column(nullable=true, precision=0, unique=false, name=user_id, length=255, scale=0, updatable=true, columnDefinition=, table=, insertable=true)
public java.lang.String jp.end0tknr.MUserEntity.password
@javax.persistence.Column(nullable=true, precision=0, unique=false, name=password, length=255, scale=0, updatable=true, columnDefinition=, table=, insertable=true)
public java.lang.String jp.end0tknr.MUserEntity.userName
@javax.persistence.Column(nullable=true, precision=0, unique=false, name=user_name, length=255, scale=0, updatable=true, columnDefinition=, table=, insertable=true)
public java.lang.String jp.end0tknr.MUserEntity.birthday
@javax.persistence.Column(nullable=true, precision=0, unique=false, name=birthday, length=255, scale=0, updatable=true, columnDefinition=, table=, insertable=true)
public java.lang.Integer jp.end0tknr.MUserEntity.age
@javax.persistence.Column(nullable=true, precision=0, unique=false, name=age, length=255, scale=0, updatable=true, columnDefinition=, table=, insertable=true)
public java.lang.Integer jp.end0tknr.MUserEntity.gender
@javax.persistence.Column(nullable=true, precision=0, unique=false, name=gender, length=255, scale=0, updatable=true, columnDefinition=, table=, insertable=true)
public java.lang.Integer jp.end0tknr.MUserEntity.departmentId
@javax.persistence.Column(nullable=true, precision=0, unique=false, name=department_id, length=255, scale=0, updatable=true, columnDefinition=, table=, insertable=true)
public java.lang.String jp.end0tknr.MUserEntity.role
@javax.persistence.Column(nullable=true, precision=0, unique=false, name=role, length=255, scale=0, updatable=true, columnDefinition=, table=, insertable=true)
class java.lang.String
class jp.end0tknr.MUserEntity
MUserEntity(userId=user@co.jp, password=$2a$10$rJyapIrvsHARwCNgporWLO6QIKXXezOpRrdb..7X0ea0VwZ5IldSy, userName=ユーザー1, birthday=2000-01-01, age=21, gender=2, departmentId=2, role=ROLE_GENERAL)
UserMstEntity(kyotenId=1, regXmileid=end0tknr1, regName=end0tknr1, regDate=2021-05-12 00:00:00.0, lastUpdXmileid=end0tknr1, lastUpdName=end0tknr1, lastUpdDate=2021-05-12 00:00:00.0, isDelete=0, version=1, userXmileid=end0tknr1, userName=end0tknr, userShozokugroup=jp, shokumuId=freelance, subShokumuId1=null, subShokumuId2=null, subShokumuId3=null, subShokumuId4=null, isBoss=null, isFirstclassArchitect=null, isSecondclassArchitect=null, isMokuzouArchitect=null, architectTourokuNo=null, architectTourokuTodoufuken=null, architectKoushuuDate=null, kouzouArchitectKoufuNo=null, setsubiArchitectKoufuNo=null, sexyKoushuuDate=null, architectJimushoCode=null, architectJimushoCode2=null, architectJimushoCode3=null, architectJimushoCode4=null, architectJimushoCode5=null, architectJimushoCode6=null, architectJimushoCode7=null, architectJimushoCode8=null, architectJimushoCode9=null, architectJimushoCode10=null, smashCode=smash)

ResourceUtil for seasar2 java の spring代替は @Configuration + @PropertySource + Environment

import org.seasar.framework.util.ResourceUtil;
   :
private Properties itemNameProp =
    ResourceUtil.getProperties("item_name.properties");
System.out.println(  itemNameProp.get("msg_key") );

seasar2 では↑このように利用していました。

package jp.end0tknr;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;

@Configuration
// in src/main/resources/*.properties
@PropertySource("classpath:item_name_ascii.properties")
public class ItemNameProp {

    @Autowired
    private Environment env;

    public String get(String key){
        return env.getProperty(key);
    }
}

spring boot では、↑このように定義し、↓このように利用できます。

package jp.end0tknr;

import java.sql.Timestamp;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;


@Controller
public class MyController {

    @Autowired
    UserMstService userMstService;

    @Autowired
    MessageSource messageSource;
    @Autowired
    ItemNameProp itemNameProp;  // ココ

    @RequestMapping(value="/index1")
    public String index1(Model model) {
        System.out.println("START index1");

        List<UserMstEntity> userMstEntities =
                userMstService.selectUserIdList("end0tknr", 1);

        System.out.println(userMstEntities);

        System.out.println(
                itemNameProp.get("suetsuke")    // ココ
                );
        return "index";
    }

その他、Environment 経由で *.properties を参照する場合、 native2ascii の処理が必要です。

(改) spring boot for java + mybatis で sql動的生成

先日、mybatisにおける「select」のsql動的生成を上記entryに記載しましたが、 「insert into」も追加し、以下に記載します。

主なポイントは、以下。

  • UserMstRepository.java における @SelectProvider() + @InsertProvider()
  • UserMstService.java における entityのkey , value の配列変換

その他のポイントは、srcに記載していますので、そちらをご確認下さい。

参考url

class 構成

┌@Controller class ┐
│MyController      │
└─┬───────┘
┌@Service class──┐
│UserMstService    │
└─┬───────┘
┌@Mapper interface ┐
│UserMstRepository │
└─┬───────┘
┌@Entity class ──┐┌複合pkey class  ─┐
│UserMstEntity     ├┤UserMstEntityPkey │
└─┬───────┘└─────────┘
┌mysql ──────┐
│DB TBL user_mst   │
└─────────┘

pom.xml

様々、記載していますが、mybatis-spring-boot-starter により 動的sqlも利用できます。

modelmapper-spring を、entity->dto変換用に記載していますが、 今回は使用しませんでした。

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.4.5</version>
    <relativePath/> <!-- lookup parent from repository -->
  </parent>
  <groupId>jp.end0tknr</groupId>
  <artifactId>MySpring4</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>MySpring4</name>
  <description>seasar2 to spring</description>
  <properties>
    <java.version>1.8</java.version>
  </properties>
  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
      <groupId>org.mybatis.spring.boot</groupId>
      <artifactId>mybatis-spring-boot-starter</artifactId>
      <version>2.1.4</version>
    </dependency>
    
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <optional>true</optional>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency> <!-- 【JSP & タグ利用】 -->
      <groupId>org.apache.tomcat.embed</groupId>
      <artifactId>tomcat-embed-jasper</artifactId>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>jstl</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>
    <dependency>
      <groupId>org.modelmapper.extensions</groupId>
      <artifactId>modelmapper-spring</artifactId>
      <version>2.4.2</version>
    </dependency>
    <dependency>
      <groupId>commons-beanutils</groupId>
      <artifactId>commons-beanutils</artifactId>
      <version>1.9.4</version>
    </dependency>
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>3.5</version>
    </dependency>
  </dependencies>
  
  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <configuration>
          <excludes>
            <exclude>
              <groupId>org.projectlombok</groupId>
              <artifactId>lombok</artifactId>
            </exclude>
          </excludes>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

application.properties

「mybatis.configuration.map-underscore-to-camel-case=true」により mysqlのsnake caseなcolumn名称が、javaのentityで使用するcamel caseに変換。

# JSP利用の為
spring.mvc.view.prefix= /WEB-INF/view/
spring.mvc.view.suffix= .jsp

# DB接続
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://192.168.63.3:3306/bukkenkoutei
spring.datasource.username=xparcadm
spring.datasource.password=ないしょ

# MyBatis
mybatis.mapper-locations:classpath*:/mapper/mysql/*.xml
mybatis.configuration.map-underscore-to-camel-case=true

@Controller と jsp

debug?用に用意しています。

package jp.end0tknr;

import java.sql.Timestamp;
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.RequestMapping;

@Controller
public class MyController {

    @Autowired
    UserMstService userMstService;

    @RequestMapping(value="/index1")
    public String index1(Model model) {
        System.out.println("START index1");

        List<UserMstEntity> userMstEntities =
                userMstService.selectUserIdList("end0tknr", 1);

        System.out.println(userMstEntities);
        return "index";
    }

    @RequestMapping(value="/index2")
    public String index2(Model model) {
        System.out.println("START index2");

        UserMstEntity userMstEntity =
                userMstService.findById(1,"end0tknr1");

        System.out.println(userMstEntity);

        return "index";
    }

    @RequestMapping(value="/index3")
    public String index3(Model model) {
        System.out.println("START index3");

        for (int i=5; i<=7; i++){
            String newUserId = "end0tknr" + Integer.toString(i);

            // insert into 対象の entity作成
            UserMstEntity newEntity = new UserMstEntity();

            newEntity.setKyotenId(1);
            newEntity.setUserXmileid(newUserId);
            newEntity.setRegXmileid(newUserId);
            newEntity.setRegName(newUserId);
            newEntity.setLastUpdXmileid(newUserId);
            newEntity.setLastUpdName(newUserId);
            newEntity.setUserName(newUserId);

            //java.sql.Timestamp で datetime型用の値を作成
            Timestamp date = new Timestamp(System.currentTimeMillis());
            //SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            //String dateStr = sdf.format(date);
            newEntity.setRegDate(date);
            newEntity.setLastUpdDate(date);

            newEntity.setIsDelete(0);
            newEntity.setVersion(1);
            newEntity.setUserShozokugroup("jp");
            newEntity.setShokumuId("freelance");
            newEntity.setXmashCode("xmash");

            userMstService.insertByEntity(newEntity);
        }

        return "index";
    }
}
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>

<a href="/index1">INDEX 1</a><br/>
<a href="/index2">INDEX 2</a><br/>
<a href="/index3">INDEX 3</a> (INSERT INTO)<br/>

</body>
</html>

@Entity

package jp.end0tknr;

import java.io.Serializable;
import java.sql.Timestamp;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.Table;
import javax.persistence.Version;

import lombok.Data;

@Entity
@Table(name = "user_mst")
@IdClass(value=UserMstEntityPkey.class)
@Data
public class UserMstEntity implements Serializable {
    private static final long serialVersionUID = 1L;

    /** kyotenIdプロパティ */
    @Id
    @Column(precision = 10, nullable = false, unique = false)
    public Integer kyotenId;

    /** userXmileidプロパティ */
    @Id
    @Column(length = 50, nullable = false, unique = false)
    public String userXmileid;

    /** isDeleteプロパティ */
    @Column(precision = 10, nullable = false, unique = false)
    public Integer isDelete;

    /** versionプロパティ */
    @Version
    @Column(precision = 10, nullable = false, unique = false)
    public Integer version;

    /** userNameプロパティ */
    @Column(length = 400, nullable = false, unique = false)
    public String userName;

    // 以降、省略
}
package jp.end0tknr;

import java.io.Serializable;

import lombok.Data;

@Data
public class UserMstEntityPkey implements Serializable {
    public Integer kyotenId;
    public String userXmileid;
}

DB TBL user_mst

mysql> desc user_mst;
+------------------------------+--------------+------+-----+---------+-------+
| Field                        | Type         | Null | Key | Default | Extra |
+------------------------------+--------------+------+-----+---------+-------+
| kyoten_id                    | int          | NO   | PRI | NULL    |       |
| user_xmileid                 | varchar(50)  | NO   | PRI | NULL    |       |
| user_name                    | varchar(400) | NO   |     | NULL    |       |
| is_delete                    | int          | NO   |     | 0       |       |
| reg_xmileid                  | varchar(50)  | NO   |     | NULL    |       |
| reg_name                     | varchar(400) | NO   |     | NULL    |       |
| reg_date                     | datetime     | NO   |     | NULL    |       |
   <以降、省略>
+------------------------------+--------------+------+-----+---------+-------+

UserMstRepository

package jp.end0tknr;

import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.annotations.InsertProvider;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.SelectProvider;
import org.apache.ibatis.jdbc.SQL;

@Mapper
public interface UserMstRepository {

    @Select("SELECT * FROM user_mst WHERE kyoten_id=#{kyotenId} and user_xmileid=#{userXmileid}")
    UserMstEntity findById(
            @Param("kyotenId") Integer kyotenId,
            @Param("userXmileid") String userXmileid );

    @SelectProvider(
            type=UserMstRepositorySqlProvider.class,
            method="selectUserIdList" )
    List<UserMstEntity> selectUserIdList(
            @Param("userId") String userId,
            @Param("kyotenId") int kyotenId);

    @InsertProvider(
            type=UserMstRepositorySqlProvider.class,
            method="insertByEntity")
    int insertByEntity(
            @Param("table") String table,
            @Param("atriKeys") String[] atriKeys,
            @Param("atriVals") Object[] atriVals);

    class UserMstRepositorySqlProvider{

        public String insertByEntity(
                String table, String[] atriKeys, Object[] atriVals) {

            SQL sql = new SQL() {{
                INSERT_INTO( table );

                for(int i=0; i<atriKeys.length; i++){
                    String atriKey = atriKeys[i];
                    String atriKeySnake = toSnakeStr( atriKey );
                    VALUES(atriKeySnake, "#{atriVals["+i+"]}");
                }
            }};

            String strSql =sql.toString();
            System.out.println(strSql);

            return strSql;
        }

        public String selectUserIdList(String userId, int kyotenId) {
            SQL sql = new SQL() {{
                SELECT("*");
                FROM("user_mst");
                WHERE("user_xmileid LIKE '${userId}%'");
                WHERE("kyoten_id=${kyotenId}");
               WHERE("is_delete=0");
            }};
            return sql.toString();
        }

        // camel -> snake 変換
        public String toSnakeStr(String camel) {
            String snake =
                    StringUtils.join(
                            StringUtils.splitByCharacterTypeCamelCase(camel), "_")
                    .toLowerCase();
            //数字の前には「_」不要
            snake = snake.replaceAll("(_)([0-9])", "$2");

            return snake;
        }

    }
}

UserMstService

package jp.end0tknr;

import java.lang.reflect.InvocationTargetException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.beanutils.PropertyUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserMstService {

    @Autowired
    UserMstRepository userMstRepository;

    public UserMstEntity findById(Integer kyotenId, String userXmileid) {
        return userMstRepository.findById(kyotenId, userXmileid);
    }

    public List<UserMstEntity> selectUserIdList(String userXmileid, Integer kyotenId) {
        return userMstRepository.selectUserIdList(userXmileid, kyotenId);
    }

    // entityのkey , value を配列へ変換し、insert
    public int insertByEntity(UserMstEntity newUserEntity) {

        Iterator iterator = null;
        Map describeEntity = null;

        try {
            describeEntity = PropertyUtils.describe(newUserEntity);
            iterator = describeEntity.keySet().iterator();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

        String[] atriKeys = new String[describeEntity.size()];
        Object[] atriVals = new Object[describeEntity.size()];

        int i = 0;
        while (iterator.hasNext()) {
            System.out.println(i);
            String atriKey = (String)iterator.next();
            System.out.println(atriKey);
            Object atriVal = describeEntity.get(atriKey);
            System.out.println(atriVal);
            atriKeys[i] = atriKey;
            atriVals[i] = atriVal;
            i++;
        }

        return userMstRepository.insertByEntity("user_mst", atriKeys, atriVals);
    }

}