はじめに
最近JavaのアプリケーションでSQLをJavaに直接書かずに実行する方法を勉強しました。
いわゆる外だしSQLというもののようなんですが、筆者の少ない開発経験では、開発に入ったときにはすでに環境が構築してあったりして、よくわかってないところがあったので、時間を作って環境構築からやってみました。
JavaのSQL実行ツール(ORマッパ)は色々とあるようで、丁寧に比較をしてくれている記事がありましたので、その中から個人的に一番お手軽なものを選んで遊んでみたので、その時の構築方法などご紹介します。
今回は、MirageSQLというライブラリを使った外だしSQLをご紹介します。
その他にもたくさんありますので、こちらもご覧ください。
1. 環境紹介
本記事で使用する環境をまず紹介しておきます。
1.1. 開発環境
開発環境はこちらです。
カテゴリ | モノ | バージョン | 特記事項 |
---|---|---|---|
統合開発環境 | Eclipse 64bit | 4.4 Luna | |
データベース | Oracle Database 64bit | 11g(11.2.0.1.0) |
1.2. テーブル
複雑なSQLは書かないので、単一テーブルとしています。
定義とDDLを記載します。
列名 | データ型 | 桁数 | PK | NULL? |
---|---|---|---|---|
UNO | NUMBER | 4 | 〇 | × |
UNAME | VARCHAR2 | 10 | ||
AGE | NUMBER | 7,2 | ||
BDATE | DATE |
CREATE TABLE SCOTT.USR
(
UNO NUMBER(4) NOT NULL,
UNAME VARCHAR2(10),
AGE NUMBER(7,2),
BDATE DATE,
CONSTRAINT PK_USER PRIMARY KEY (UNO)
);
2. プロジェクトの作成
Eclipseを使用し、サンプル用プロジェクトを作成します。
環境の構築方法は、Mavenを使用する方法と、自力で構築する方法があります。
Mavenは便利ですが、頭に入らないデメリットがあるので(少なくとも筆者は・・・)今回は自力で構築します。
ファイル > 新規 > Javaプロジェクト
の順にクリックし、プロジェクトの作成ウィザードを表示します。
プロジェクト名を入力し、次へ進みます。
プロジェクト名は好きなもので構いませんが、サンプルでは「SampleMirageSQLBasic」としています。
新規ソース・フォルダーの作成をクリックします。
フォルダー名に「resources」と入力し、完了します。
「resources」フォルダーが追加されたことを確認し、完了します。
パッケージ・エクスプローラーに「SampleMirageSQLBasic」が追加できました。
プロジェクト作成直後のツリーです。
次に、依存するライブラリを配置するフォルダを作ります。
プロジェクトを右クリック > 新規 > フォルダーの順でクリックします。
ダイアログにフォルダー名を入力して完了します。
フォルダー名は「lib」としました。
「lib」フォルダーが作成できました。
次に、MirageSQLを使用するのに必要なライブラリを配置します。
Mavenだと6個のjarが依存関係として追加されますが、最小構成で使用する場合は追加する必要はありません。
次の表の必須列に〇のついているもののみ依存するライブラリとして追加します。
また、同時にアプリログで使用するライブラリと、DB接続に使用するJDBCドライバも追加しています。
jar | 必須 | 特記事項 |
---|---|---|
javassist-3.21.0-GA.jar | 〇 | |
miragesql-2.0.0.jar | 〇 | |
slf4j-api-1.7.25.jar | 〇 | |
ognl-3.2.3.jar | 〇 | |
log4j-api-2.9.1.jar | ログ出力に使用します。 | |
log4j-core-2.9.1.jar | ログ出力に使用します。 | |
ojdbc8.jar | DBとの接続に使用します。 | |
junit-4.12.jar | Mavenだと追加されますが、必須ではありません。 | |
hamcrest-core-1.3.jar | Mavenだと追加されますが、必須ではありません。 |
上記のライブラリをlibフォルダに配置し、ビルド・パスに追加します。
追加したライブラリを選択し、右クリック > ビルド・パス > ビルド・パスに追加をクリックします。
参照ライブラリーに配置したjarが全て追加できました。
ここまでで、プロジェクトの作成は完了です。
今回は自力で構築しましたが、参考までにMavenで構築する際のpom.xmlの記述内容を記載しておきます。
<dependency>
<groupId>com.miragesql</groupId>
<artifactId>miragesql</artifactId>
<version>2.1.0</version>
</dependency>
3. 実装
いよいよサンプルプログラムを実装します。
3.1. アプリケーションの設定
MirageSQLやロガーなど、アプリケーションの設定を行います。
3.1.1. 接続先DBの設定
アプリケーションから接続する先のDBの設定を行います。
プロジェクト作成時に追加した「resources」フォルダーに接続先を定義したプロパティ配置し、設定します。
DBへの接続は、MirageSQLの機能を使用しますが、機能の制約上、クラス・パスに登録したフォルダーのルートに定義情報を置く必要があるようです。
また、ファイル名は「jdbc.properties」というファイル名でなければならないようです。
# -----------------------------------------------------------------
# MirageSQLの接続先DB設定
# MirageSQLでは接続先は自動検出なので、
# 必ず「jdbc.properties」のファイル名でクラスパスに
# 登録したフォルダのルートに配置する。
# -----------------------------------------------------------------
# 使用するJDBCドライバ
jdbc.driver=oracle.jdbc.driver.OracleDriver
# 接続先DBの接続文字列
jdbc.url=jdbc:oracle:thin:@localhost:1521/ORCL
# 接続ユーザ
jdbc.user=scott
# パスワード
jdbc.password=tiger
3.1.2. ログ出力設定
アプリケーションログの出力設定を行います。
ファイルにログを出力したい方は、コメントアウトを復活させてご利用ください。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration>
<Configuration status="off">
<!-- Appenderの設定 -->
<Appenders>
<Console name="STDOUT" target="SYSTEM_OUT">
<PatternLayout>
<pattern>%d{yyyy/MM/dd HH:mm:ss.SSS} [%t] %-6p %c{10} %m%n</pattern>
</PatternLayout>
</Console>
<RollingFile name="FILE" fileName="./log/appLog.log" filePattern="appLog-%d{yyyy-MM-dd}-%i.log">
<TimeBasedTriggeringPolicy />
<!-- <SizeBasedTriggeringPolicy size="100MB" /> -->
<DefaultRolloverStrategy max="3"/>
</RollingFile>
</Appenders>
<!-- 出力に使用するロガーの設定 -->
<Loggers>
<Root level="DEBUG">
<AppenderRef ref="STDOUT" level="INFO"/>
<!-- <AppenderRef ref="FILE" /> -->
</Root>
</Loggers>
</Configuration>
3.2. データベースアクセス処理の実装
簡単な検索、挿入、更新処理をしてみましょう。
3.2.1 SQLの実装
検索用SQLを実装しましょう。
SQLはクラス・パスに登録しているフォルダーであればどこでに置いてもいいのですが、サンプルでは「resources」に「sql」というフォルダーを新たに追加し、そこにSQLを配置します。
ファイル名はそれぞれ
- SelectUsr.sql
- InsertUsr.sql
- UpdateUsr.sql
とします。
select
*
from
USR
/*-------------------------------------------
* BEGINからENDでくくったところまでは、
* くくった中の処理がすべてなくなった場合に、
* SQLの文法としておかしくなってしまうので、
* 自動的に消滅する区間です。
--------------------------------------------*/
/*BEGIN*/
where
/*-------------------------------------------------
* パラメータとなるunoがnullでないときのみ
* 行が有効となる制御です。
* MirageSQLでは動的SQLの制御もパラメータの定義も
* コメント形式で記述します。
--------------------------------------------------*/
/*IF uno != null*/
UNO = /*uno*/0
/*END*/
/*IF uname != null*/
and UNAME like /*uname*/''
/*END*/
/*END*/
こちらのSQLは2WaySQL呼ばれるものです。
2WaySQLとは、簡単に言うと、SQLをそのまま貼り付けても実行できるし、アプリケーションでパラメータを可変にしても実行できるというSQLのことです。
また、MirageSQLでは指定するパラメータの内容などにより、不要になる行をその都度有効にしたり無効にしたりしてくれる動的SQLを実装できます。
制御文をコメント形式でSQLに埋め込んでおけば、MirageSQLがよきにはからってくれます。
同様の2WaySQLでINSERT、UPDATEも実装します。
SelectUsr.sqlの並びに配置してください。
-- ユーザーテーブルに新規レコードをINSERT
insert
into USR (
UNO
, UNAME
, AGE
, BDATE
) values (
/*UNO*/1000 -- UNO NUMBER(4) NOT NULL
, 'test02' -- ENAME VARCHAR2(10)
, 0 -- AGE NUMBER(7,2)
, SYSDATE -- BDATE DATE
)
■UpdateUsr.sql
-- ユーザーテーブルを更新する
update USR
set
AGE = /*age*/0
where
UNO = /*uno*/1000
3.2.2. エンティティの作成
検索したデータの入れ物となるエンティティと呼ばれるクラスを実装します。
こちらは単にデータの入れ物なので、取得したレコードを入れるフィールドとそれを取得、設定する処理以外必要ありません。
サンプルでは、取得結果を出力しやすくするために、toStringメソッドをオーバーライドしています。
「src」フォルダーを右クリック > 新規 > クラスから、エンティティクラスを追加します。
パッケージ、名前を入力し、完了します。
パッケージ名やクラス名はサンプルと一致させる必要はありません。
追加したクラスの実装を記載します。
今回は全列検索しますので、すべての列の値を設定するフィールドを用意しています。
実際のテーブルに定義している列と、エンティティクラスのフィールドは、アノテーションを使って対応させ、キーとなる列は加えてアノテーションを付加します。
キーとなる列をアノテーションで定義しておくことで、InsertとUpdateをSQLを使わずに行うことができます。
package jp.co.sample.tarosa.entity;
import com.miragesql.miragesql.annotation.Column;
import com.miragesql.miragesql.annotation.PrimaryKey;
import com.miragesql.miragesql.annotation.PrimaryKey.GenerationType;
/**
* <h3>[ユーザーテーブルエンティティ]</h3><br>
* <br>
* DBの列とフィールドの対応はアノテーションを使って対応させる。
* @author tarosa0001
*/
public class Usr {
/** ユーザー番号 */
@PrimaryKey(generationType = GenerationType.APPLICATION)
@Column(name = "UNO")
private Long uNo;
/** ユーザー名 */
@Column(name = "UNAME")
private String uName;
/** 年齢 */
@Column(name = "AGE")
private Double age;
/** 誕生日 */
@Column(name = "BDATE")
private String bDate;
/**
* <h3>[コンストラクタ]</h3><br>
* <br>
* 引数を使用してユーザーエンティティのフィールドを設定する。
* @param uNo ユーザー番号
* @param uName ユーザー名
* @param age 年齢
* @param bDate 誕生日
*/
public Usr(Long uNo, String uName, Double age, String bDate) {
this.uNo = uNo;
this.uName = uName;
this.age = age;
this.bDate = bDate;
}
/**
* <h3>[ユーザー番号取得処理]</h3><br>
* ユーザー番号を取得します。<br>
* @return uNo ユーザー番号
*/
public Long getuNo() {
return uNo;
}
/**
* <h3>[ユーザー番号設定処理]</h3><br>
* ユーザー番号を設定します。<br>
* @param uNo ユーザー番号
*/
public void setuNo(Long uNo) {
this.uNo = uNo;
}
/**
* <h3>[ユーザー名取得処理]</h3><br>
* ユーザー名を取得します。<br>
* @return uName ユーザー名
*/
public String getuName() {
return uName;
}
/**
* <h3>[ユーザー名設定処理]</h3><br>
* ユーザー名を設定します。<br>
* @param uName ユーザー名
*/
public void setuName(String uName) {
this.uName = uName;
}
/**
* <h3>[年齢取得処理]</h3><br>
* 年齢を取得します。<br>
* @return age 年齢
*/
public Double getAge() {
return age;
}
/**
* <h3>[年齢設定処理]</h3><br>
* 年齢を設定します。<br>
* @param age 年齢
*/
public void setAge(Double age) {
this.age = age;
}
/**
* <h3>[誕生日取得処理]</h3><br>
* 誕生日を取得します。<br>
* @return bDate 誕生日
*/
public String getbDate() {
return bDate;
}
/**
* <h3>[誕生日設定処理]</h3><br>
* 誕生日を設定します。<br>
* @param bDate 誕生日
*/
public void setbDate(String bDate) {
this.bDate = bDate;
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
// 文字列表現組み立て
StringBuilder sb = new StringBuilder();
sb.append(this.uNo);
sb.append(", ");
sb.append(this.uName);
sb.append(", ");
sb.append(this.age);
sb.append(", ");
sb.append(this.bDate);
return new String(sb);
}
}
3.2.3 データベースアクセス処理の実装
データベースアクセス処理を実装します。
エンティティと同じ手順でクラスを追加します。
パッケージ、クラス名は以下としました。
パッケージ名:jp.co.sample.tarosa.dao
クラス名:UsrDao
こちらのクラスに、DBの検索、挿入、更新処理を実装していきます。
package jp.co.sample.tarosa.dao;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import jp.co.sample.tarosa.entity.Usr;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.miragesql.miragesql.ClasspathSqlResource;
import com.miragesql.miragesql.SqlManager;
import com.miragesql.miragesql.SqlResource;
import com.miragesql.miragesql.session.Session;
import com.miragesql.miragesql.session.SessionFactory;
/**
* <h3>[ユーザーテーブルアクセスクラス]</h3><br>
* <br>
* ユーザーテーブルに対する操作を行うクラス。
* @author tarosa0001
*/
public class UsrDao {
/** ロガー */
private Logger logger = LogManager.getLogger(UsrDao.class);
/**
* <h3>[ユーザーテーブル検索処理]</h3><br>
* <br>
* ユーザーテーブルを検索する。
*/
public void selectUsr() {
logger.info("ユーザーテーブル検索処理開始");
// -------------------------------------------------------------
// セッションオブジェクトの生成。
// MirageSQLのクラスメソッドを使ってインスタンスを取得する
// -------------------------------------------------------------
Session session = SessionFactory.getSession();
SqlManager sqlManager = session.getSqlManager();
// -------------------------------------------------------------
// セッションの開始
// Springと連携する方法もある
// -------------------------------------------------------------
session.begin();
try {
// -------------------------------------------------------------
// SQLによるレコード検索の開始
// MirageSQLの機能を使用してSQLのリソースを生成
// -------------------------------------------------------------
SqlResource selecttUsrSql = new ClasspathSqlResource("sql/SelectUsr.sql");
// -------------------------------------------------------------
// 検索条件のパラメータを生成
// ここでは社員番号を指定
// パラメータは数値であろうとも文字列として指定する
// -------------------------------------------------------------
Map<String, Object> params = new HashMap<>();
params.put("uno", "9000");
// -------------------------------------------------------------
// SQLをリソースとして生成し、パラメータはMapとして引き渡す。
// -------------------------------------------------------------
Usr usr = sqlManager.getSingleResult(Usr.class, selecttUsrSql, params);
// --------------------------------------------
// 検索結果を出力
// --------------------------------------------
System.out.println(usr);
System.out.println("--------------------------------------------");
// -------------------------------------------------------------
// 次はパラメータにユーザー名に設定
// -------------------------------------------------------------
params = new HashMap<>();
params.put("uname", "%TARO");
// -------------------------------------------------------------
// ユーザー名はlike検索なので複数の結果を取得
// -------------------------------------------------------------
List<Usr> result = sqlManager.getResultList(Usr.class, selecttUsrSql, params);
// -------------------------------------------------------------
// 検索結果を全件出力
// -------------------------------------------------------------
result.forEach(li -> System.out.println(li));
System.out.println("--------------------------------------------");
// -------------------------------------------------------------
// パラメータなしで全件検索
// -------------------------------------------------------------
result = sqlManager.getResultList(Usr.class, selecttUsrSql);
// -------------------------------------------------------------
// 検索結果を全件出力
// -------------------------------------------------------------
result.forEach(li -> System.out.println(li));
// --------------------------------------------
// セッションをコミット
// --------------------------------------------
session.commit();
} catch(Exception e) {
logger.error("DB系のエラー", e);
// エラーの場合はロールバック
session.rollback();
} finally {
// セッションは必ずリリースする
session.release();
}
logger.info("ユーザーテーブル検索処理終了");
}
/**
* <h3>[ユーザーテーブル挿入処理]</h3><br>
* <br>
* ユーザーテーブルにレコードを挿入する。
*/
public void insertUsr() {
logger.info("ユーザーテーブル挿入処理開始");
Session session = SessionFactory.getSession();
// セッション開始
session.begin();
try {
// ------------------------------------------------------------
// INSERTするレコードのエンティティを生成
// SQLではなくMirageSQLのメソッドを使用してINSERTする。
// ------------------------------------------------------------
Usr usr = new Usr(new Long(1000), "test01", new Double(0),
new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
SqlManager sqlManager = session.getSqlManager();
int result = sqlManager.insertEntity(usr);
System.out.println("エンティティで挿入した件数挿入件数:" + result);
// ------------------------------------------------------------
// SQLを使ってINSERTする
// MirageSQLではINSERTのSQLを発行する機能はなく、
// 更新用のメソッドを使ってINSERTする。
// ------------------------------------------------------------
Map<String, Object> params = new HashMap<>();
// パラメータは検索同様に設定
params.put("uno", "1001");
SqlResource insertUsrSql = new ClasspathSqlResource("sql/insertUsr.sql");
result = session.getSqlManager().executeUpdate(insertUsrSql, params);
System.out.println("SQLで挿入した件数挿入件数:" + result);
session.commit();
} catch(Exception e) {
e.printStackTrace();
session.rollback();
} finally {
session.release();
}
logger.info("ユーザーテーブル挿入処理終了");
}
/**
* <h3>[ユーザーテーブル更新処理]</h3><br>
* <br>
* ユーザーテーブルのレコードを更新する。
*/
public void updateUsr() {
logger.info("ユーザーテーブル更新処理開始");
Session session = SessionFactory.getSession();
// セッション開始
session.begin();
try {
// ------------------------------------------------------------
// UPDATEするレコードのエンティティを生成
// SQLではなくMirageSQLのメソッドを使用してINSERTする。
// ------------------------------------------------------------
Usr usr = new Usr(new Long(1000), "test01", new Double(99),
new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
SqlManager sqlManager = session.getSqlManager();
int result = sqlManager.updateEntity(usr);
System.out.println("更新件数:" + result);
// ------------------------------------------------------------
// SQLを使ってUPDATEする
// MirageSQLではINSERTのSQLを発行する機能はなく、
// 検索用のメソッドを使ってINSERTする。
// ------------------------------------------------------------
Map<String, Object> params = new HashMap<>();
// パラメータは検索同様に設定
params.put("uno", "1001");
params.put("age", "300");
SqlResource updateUsrSql = new ClasspathSqlResource("sql/updateUsr.sql");
result = session.getSqlManager().executeUpdate(updateUsrSql, params);
System.out.println("更新件数:" + result);
session.commit();
} catch(Exception e) {
e.printStackTrace();
session.rollback();
} finally {
session.release();
}
logger.info("社員テーブル更新処理終了");
}
}
3.2.4. main処理の実装
最後に、これまで実装した処理を行うmain処理を実装して完了です。
これまでと同様の手順でクラスを追加しましょう。
パッケージ名:jp.co.sample.tarosa.main
クラス名:SampleMirageSQLBasic
import jp.co.sample.tarosa.dao.UsrDao;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* <h3>[MirageSQLサンプルのメインクラス]</h3><br>
* <br>
* MirageSQLサンプルのMain処理を持つクラス。
*
* @author tarosa0001
*/
public class SampleMirageSQLBasic {
/** ロガー */
private static Logger logger = LogManager.getLogger(SampleMirageSQLBasic.class);
/**
* <h3>[Main処理]</h3><br>
* <br>
* MirageSQLサンプルのMain処理。<br>
* @param args コマンド引数
*/
public static void main(String[] args) {
logger.info("Main処理開始");
// データアクセスオブジェクト生成
UsrDao dao = new UsrDao();
// -------------------------------------------------------------
// 検索処理呼び出し
// -------------------------------------------------------------
dao.selectUsr();
// -------------------------------------------------------------
// 挿入理呼び出し
// -------------------------------------------------------------
dao.insertUsr();
// -------------------------------------------------------------
// 更新処理呼び出し
// -------------------------------------------------------------
dao.updateUsr();
logger.info("Main処理終了");
}
}
3.2.5. 実装の完了
ここまでで、サンプルプログラムの実装は完了です。
最後に、完成したプロジェクトのツリーを記載します。
プロジェクトが以下のツリーになっていることを確認します。
■ツリー(テキスト)
プロジェクト・ルート
├─src
│ └─jp
│ └─co
│ └─sample
│ └─tarosa
│ ├─dao
│ │ UsrDao.java
│ │
│ ├─entity
│ │ Usr.java
│ │
│ └─main
│ SampleMirageSQLBasic.java
│
├─resources
│ │ jdbc.properties
│ │ log4j2.xml
│ │
│ └─sql
│ InsertUsr.sql
│ SelectUsr.sql
│ UpdateUsr.sql
│
├─lib
│ javassist-3.21.0-GA.jar
│ log4j-api-2.9.1.jar
│ log4j-core-2.9.1.jar
│ miragesql-2.0.0.jar
│ ognl-3.2.3.jar
│ ojdbc8.jar
│ slf4j-api-1.7.25.jar
│
└─log ※ログ出力用に追加しています
4. 実行
プロジェクトを右クリック > 実行 > Javaアプリケーションで、サンプルを実行します。
[2018-09-10 21:03:04.829], INFO , main, jp.co.sample.tarosa.main.SampleMirageSQLBasic, Main処理開始
[2018-09-10 21:03:04.833], INFO , main, jp.co.sample.tarosa.dao.UsrDao, ユーザーテーブル検索処理開始
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
9000, ATARO, 20.0, 1990-01-01 00:00:00.0
--------------------------------------------
9003, DTARO, 30.0, 1980-08-01 00:00:00.0
9000, ATARO, 20.0, 1990-01-01 00:00:00.0
--------------------------------------------
9003, DTARO, 30.0, 1980-08-01 00:00:00.0
9000, ATARO, 20.0, 1990-01-01 00:00:00.0
9001, BJIRO, 21.0, 1989-05-25 00:00:00.0
9002, CSABURO, 19.0, 1991-12-31 00:00:00.0
[2018-09-10 21:03:07.043], INFO , main, jp.co.sample.tarosa.dao.UsrDao, ユーザーテーブル検索処理終了
[2018-09-10 21:03:07.043], INFO , main, jp.co.sample.tarosa.dao.UsrDao, ユーザーテーブル挿入処理開始
エンティティで挿入した件数挿入件数:1
SQLで挿入した件数挿入件数:1
[2018-09-10 21:03:07.168], INFO , main, jp.co.sample.tarosa.dao.UsrDao, ユーザーテーブル挿入処理終了
[2018-09-10 21:03:07.168], INFO , main, jp.co.sample.tarosa.dao.UsrDao, ユーザーテーブル更新処理開始
更新件数:1
更新件数:1
[2018-09-10 21:03:07.250], INFO , main, jp.co.sample.tarosa.dao.UsrDao, 社員テーブル更新処理終了
[2018-09-10 21:03:07.250], INFO , main, jp.co.sample.tarosa.main.SampleMirageSQLBasic, Main処理終了
終わりに
ここまでで、MirageSQLを使用した外だしSQLの実現についての基本は終了です。
今回は、あくまで基本の使い方ですので、すべてを自力で実装しましたが、トランザクションの制御については、springと連携する方法もあります。
springを使用すると、わざわざ自分でセッションの開始などを実装しなくてもspringが勝手にやってくれます。
こちらはspringの知識が別に必要になりますので、今回の記事では割愛します。
spring連携の仕方についてはこちらをご覧ください。
EX. 参考記事
筆者がMirageSQLを勉強し、ひいては本記事の執筆に至るまでお世話になった参考記事を紹介します。
参考記事を執筆していただいた筆者様、管理人様に感謝です。