Db2:レコードが登録されたらスマートフォンに通知したい

# 背景と状況
データベースにデータが登録されたらすぐに知りたいと思ったことはありませんか。ユースケースとしては電子カルテに検査結果が登録されたらスマートフォンに通知するなどがあります。
# やりたいこと
レコードが登録されたらスマートフォンに通知したい。具体的にはレコードのインサート、アップデートが発生したら、通知プログラムを実行したい。今回はLINEを使いますがSlackなどほかのアプリにも応用可能です。

# やり方
Db2の **ユーザー定義関数(User Defined Functions 以下UDF)** を使って、LINEにメッセージを送ります。LINEへのメッセージはLINE Notifyを使います。また、インサート、アップデートなどの **イベントを検知するのにDb2のトリガー** を使います。

1. LINE Notifyのトークン取得
1. LINE Notifyの稼働確認
1. JavaでLINE Notifyを実装
1. UDFの作成
1. トリガーの定義
1. レコードのインサートによる稼働確認

# 環境

- Windows 11
- IBM Db2 V11.5 Windows

# LINE Notifyのトークン取得
[こちらの記事](https://qiita.com/iitenkida7/items/576a8226ba6584864d95 "Qiita")を参考にLINE Notifyのサイトからトークンを取得します。

https://qiita.com/iitenkida7/items/576a8226ba6584864d95

# LINE Notifyの稼働確認
[こちらの記事](https://qiita.com/akira6592/items/45f927b25f00d7441c1b "Qitta")を参考に取得したトークンでLINEにメッセージが送付できるかを確認してください。

https://qiita.com/akira6592/items/45f927b25f00d7441c1b

# JavaでLINE Notifyを実装
[こちらのGitHub](https://gist.github.com/chemp7/548fccd3d2509811843e42f1198c040c "GitHub")にあるJavaのソースを参考にLineNotify.javaを用意します。

```java:LineNotify.java
import java.io.*;
import java.net.*;
import java.util.regex.Pattern;

public class LineNotify {
	public static String callEvent(String token, String message) {
		String result = "false";
   		try {
			String strUrl = "https://notify-api.line.me/api/notify";  // 通知用エンドポイント
			message = URLEncoder.encode(message, "UTF-8");
			URL url = new URL( strUrl );
			HttpURLConnection connection = (HttpURLConnection)url.openConnection();
			connection.addRequestProperty("Authorization",  "Bearer " + token);
			connection.setRequestMethod( "POST" );
			connection.addRequestProperty( "Content-Type", "application/x-www-form-urlencoded" );
			connection.setDoOutput( true );
			String parameterString = new String("message=" + message);
			PrintWriter printWriter = new PrintWriter(connection.getOutputStream());
			printWriter.print(parameterString);
			printWriter.close();
			connection.connect();
            
			int statusCode = connection.getResponseCode();
		        if ( statusCode == 200 ) {
		        	result = "true";
		        } else {
		        	throw new Exception( "Error:(StatusCode)" + statusCode + ", " + connection.getResponseMessage() );
		        }
		        connection.disconnect();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}
	private String replaceProcess(String txt){
    		txt = replaceAllRegex(txt, "\\\\", "￥");		// \
		return txt;
	}
	private String replaceAllRegex(String value, String regex, String replacement) {
		if ( value == null || value.length() == 0 || regex == null || regex.length() == 0 || replacement == null )
			return "";
		return Pattern.compile(regex).matcher(value).replaceAll(replacement);
	}
}
```

稼働確認用にmain.javaも用意します。

```java:Main.java
public class Main {

    public static void main(String[] args) {

        String result ;

        LineNotify objetct1 = new LineNotify();
        result = objetct1.callEvent(args[0],args[1]);

        System.out.print(result);
    }
}
```

Db2にインストールされているJDKを使ってコンパイルします。
`D:\SQLLIB\java\jdk\bin\javac Main.java`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698263/0f2a4656-7b58-bcc1-7b73-4f5c7e80813e.png)

作成したクラスの稼働確認をします。
`D:\SQLLIB\java\jdk\bin\java Main 取得したトークン "JavaからLINE Notify"` 

jarファイルを作成します。
`D:\SQLLIB\java\jdk\bin\jar -cvf LineNotify.jar LineNotify.class` 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698263/d6d62989-3801-0e8e-5c13-4b1b9e3ec789.png)

jarファイルを格納するフォルダーにコピー、Db2にインストールします。
`copy LineNotify.jar D:\myapp_jar`
`db2 connect to データベース名 user ユーザー名`
`db2 "CALL SQLJ.INSTALL_JAR('file:D:/myapp_jar/LineNotify.jar', 'LINENOTIFY')"`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698263/609cbec3-2b86-b0a2-ddfb-aace714b4984.png)

jarは以下のフォルダーに格納されます。
`D:\DB2PROFS\IBM\DB2\DB2COPY1\function\jar\DB2ADMIN`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698263/13f41867-0757-c42a-13cb-b783477ee095.png)

格納したjarを更新、削除したい場合は以下のコマンドを使用します。

置換
  `db2 "CALL SQLJ.REPLACE_JAR('file:D:/myapp_jar/LineNotify.jar', 'LINENOTIFY')"`
  `db2 "CALL SQLJ.REFRESH_CLASSES()"`
除去
  `db2 "CALL SQLJ.REMOVE_JAR('LINENOTIFY')"`


# UDFの作成

UDF定義用のDDLを準備、以下のコマンドでUDFを定義します。
`db2 -tvf LineNotifyUDF.ddl`

```sql:LineNotifyUDF.ddl
DROP   PROCEDURE LINENOTIFY ;
DROP   FUNCTION LINENOTIFY ;

CREATE FUNCTION LINENOTIFY (IN TOKEN CHAR(100) , IN MESSAGE CHAR(200)) 
RETURNS CHAR(200)
EXTERNAL NAME 'LineNotify.callEvent' 
LANGUAGE JAVA 
PARAMETER STYLE JAVA 
SPECIFIC LINENOTIFY 
CALLED ON NULL INPUT
NO SQL
;                                                                                                                                                                                         
```
プロシージャーとファンクションが事前定義されていない場合はエラーになりますが問題ありません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698263/e0325d97-3f3b-9150-9a59-021b49464801.png)

UDFを稼働確認します。
`db2 "CALL SQLJ.REFRESH_CLASSES()"`
`db2 select LINENOTIFY('取得したトークン','DB2からLINE Notify') FROM SYSIBM.SYSDUMMY1`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698263/47d39bd7-8dad-c3f9-aaf9-fc33cc1eb383.png)

うまくいくと以下のようにLINEに通知が行きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698263/7d6b48a6-a57a-5358-d3ff-a29c7888692e.png)

UDFを確認、除去したい場合は以下のコマンドを使用します。

確認
    `db2 SELECT ROUTINESCHEMA, ROUTINENAME, FENCED, LANGUAGE FROM SYSCAT.ROUTINES WHERE ROUTINESCHEMA='DB2ADMIN'`
除去
    `db2 drop FUNCTION LINENOTIFY`

# トリガーの定義
トリガーのレコードがインサートされたらUDFが動くトリガーを用意、以下のコマンドで定義します。テーブル名は各自の環境に合わせて変更してください。
`db2 -tvf TriggerLineNotifyUDF.ddl`
```sql:TriggerLineNotifyUDF.ddl
CREATE TRIGGER LINENOTIFY
　　AFTER INSERT ON TKNTIDX
　　REFERENCING NEW AS N_ROW
　　FOR EACH ROW MODE DB2SQL
　　
    select LINENOTIFY('取得したトークン', 
                       rtrim(KJ_KAN_NM) || 'さんの検査結果が出ました。')  
      from tjptkhn where pt_id = N_ROW.PT_ID
;
```

# レコードのインサートによる稼働確認
レコードをインサートするとLINEにメッセージが送られます。
```sql:insert.sql
insert into tkntidx select 
'000001', 
'00000001', 
current date, 
current time, 
'999999', 
current date ,
'',
'',
'',
'',
'',
'',
'',
current date, 
current time, 
'',
'',
'',
'',
'',
'',
current time, 
'',
''
from sysibm.sysdummy1 ;
```

正しく設定ができると、以下のようなメッセージがLINEに届きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698263/a3ba86b4-d93c-2209-6edc-1347a266b114.png)

Javaでアプリに通知するロジックを記述すれば、UDFに定義できます。UDFに定義後、select文で簡単にプログラムが起動します。

、