Talend入門 (14)　～tJava～

##全体の目的
Talendの超簡単なサンプルジョブを作成して，ETLジョブ開発に必要なスキルを習得することが目的です。

##対象者
ETL / EAI技術者

##環境
|使用環境|バージョン|
|:----:|:------------------|
|OS|Windows10|
|Talend|7.1.1|	

##サンプル一覧
以下の順番で習得していきます

|#|内容|
|:----:|:------------------|
|1|[はじめてのTalend](https://qiita.com/airkoda/items/a837d3c5d851d98d2612)|
|2|[ファイルI/O](https://qiita.com/airkoda/items/1cd12c05c6136add7178)|
|3|[データベースI/O](https://qiita.com/airkoda/items/bc807c920c3adebad837)|
|4|[tMap](https://qiita.com/airkoda/items/fa4a9c022b5d688698e0)|
|5|[イテレータ](https://qiita.com/airkoda/items/404d2d65eff535b2126c)|
|6|[メタデータ](https://qiita.com/airkoda/items/4b42cf2a86f34abb6c87)|
|7|[Context変数](https://qiita.com/airkoda/items/42d294fca40b81aa20fb)|
|8|[Global Map](https://qiita.com/airkoda/items/1aba0d36905c755deeae)|
|9|[tHashInput/tHashOutput](https://qiita.com/airkoda/items/d9347865190f0f3ab180)|
|10|[エラーハンドリング](https://qiita.com/airkoda/items/85e7c49a9bf6a21db6d5)|
|11|[ロギング](https://qiita.com/y-fujioka-yoshiharu/items/e0280f34cff2e3d00bda)|
|12|[子ジョブ（ジョブのネスト）](https://qiita.com/y-fujioka-yoshiharu/items/04ea4bd74ded382cf03c)|
|13-1|[設定ファイル読み込みオリジナル版](https://qiita.com/airaotaku/items/92f114795108f25e6d0f)|
|13-2|[設定ファイル読み込みコンポ版](https://qiita.com/airyama/items/83c2bf9b2d51a79c3714)|
|**14**|[**tJava（今回）**](https://qiita.com/airkoda/items/4d81d76c36b6fcbd4d89)|

[サンプルコード（Talendプロジェクトデータ）](https://github.com/kd2air/qiita)

##今回の目的
tJavaについて理解する。

##tJavaとは
Javaのコードをそのまま書くことのできるコンポーネントです。
利用用途によって3種類のtJava系コンポーネントが用意されています。

- tJava
    - 一度だけ実行される場所に配置して使います
    - 主な使いどころとして
        - Context変数やGlobal Mapの初期化や更新
        - サポートされていない（コンポーネントのない）外部I/Fの実装
- tJavaRow
    - ロウ処理においてtmap等、標準コンポーネントでできない独自処理を加えたい場合に使います
- tJavaFlex
    - 開始処理、メイン処理、終了処理を記載したい場合に使用します（awkっぽい使い方ができます）
    - ロウを複製してどうこうしたい場合にも使えます


##サンプル作成
それぞれサンプルプログラムを動かしながら使い方を見ていきましょう。

###tJava
リポジトリの[ジョブ]を右クリックし[ジョブの作成]を選択します。
[名前]に**tjava**と入力し[Finish]ボタンをクリックします。

![GS000230.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364565/9bea6264-25d2-4f80-925c-e172fcc3676a.png)

[tRowGenerator]と[tLogRow]を配置します。
![GS000231.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364565/5e4ed142-5043-a33d-d721-c1940ffe98d0.png)

[tRowGenerator]のスキーマ編集画面で + を2回クリックし２カラム定義します。

| 列      | カラム |
|:-----------------|:-----------------|
| 1列目    | newColumn  |
| 2列目    | newColumn1  |


| 項目名              | 設定内容 |    説明  |
|:-----------------|:------------------|:------------------|
| 生成する行数    | 3  | テストデータを3行だけ作成します |
![GS000232.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364565/9adcb320-96d1-84bc-4d97-2a4231f1cd0d.png)

[tRowGenerator]と[tLogRow]をロウでつなぎます。
[tLogRow]のモードはテーブル形式にしておきます。
![GS000234.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364565/cad251aa-0786-72fa-88af-7071acd605a0.png)

[tJava]を配置します。
![GS000235.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364565/7462b936-d851-4bb5-2ede-5f0d007c2fdd.png)

[tRowGenerator]で右クリックし**サブジョブOK**でつなぎます。
![GS000236.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364565/6f3a4f89-beac-0d50-dc26-2112e8f2ca77.png)

[tJava]のコードを記載します。
この例ではtLogRowの持っているグローバルマップの値を出力しています。

```java:
System.out.println( ((Integer)globalMap.get("tLogRow_1_NB_LINE")) + "件処理しました");

```
![GS000237.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364565/84169579-cc53-e7fd-b118-312390230f25.png)

実行してみましょう。
tJavaで記載した「3件処理しました」というメッセージが出力できました。
![GS000238.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364565/74f1e414-0c67-e56c-2755-e9b66b6fb52f.png)

###tJavaRow
次にロウ処理に便利な**tJavaRow**を見ていきます。

[tJavaRow]コンポーネントを配置し、ロウでつなぎます。
![GS000239.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364565/473dbefa-fc31-d1a6-1682-b780da0364ed.png)

[tJavaRow]の設定画面で「同期カラム」、「コードの生成」の順にクリックすることでロウ処理用のJavaコードが自動生成されます。
![GS000241b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364565/16498eed-6227-265c-c6cd-db84117c03af.png)

確認ダイアログでは「Yes」をクリックします。
![GS000240.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364565/dc053a06-bcda-0489-76f5-464ed0019b56.png)

[tLogRow]を配置しロウでつなぎます。
モードもテーブル形式にしておきましょう。
![GS000242b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364565/eea96df9-1f75-03c2-8b5f-d327377b801b.png)

今回は1列目の値を大文字化してみましょう。
コードを修正します。

```java
output_row.newColumn = StringHandling.UPCASE(input_row.newColumn);
output_row.newColumn1 = input_row.newColumn1;
```
![GS000243b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364565/e7488414-5e29-2d27-83c3-d951bbcb0c1d.png)

実行してみましょう。
tJavaRowで記載したとおり、1列目の結果が大文字になりました。
![GS000244b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364565/a2b9075f-bfa2-9b03-d815-7bbe2d8ebe60.png)

##tJavaFlexサンプル
tJavaFLexでは2つのサンプルを作成してみます。

###tJavaFlex（ロウの複製）

リポジトリの[ジョブ]を右クリックし[ジョブの作成]を選択します。
[名前]に**tjavaflex**と入力し[Finish]ボタンをクリックします。
![GS000245.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364565/dee9de4b-a8af-d530-580f-4c9b606fba91.png)

[tFixedFlowInput]を配置し設定を行います。

![GS000255.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364565/53cebc1b-2378-ab46-26bf-ebaab23055bf.png)

スキーマの編集は以下のとおり。

| 列      | カラム |  タイプ |
|:-----------------|:------------------|:------------------|
| 1列目    | ID  | String|
| 2列目    | NAME  | String|
| 3列目    | GENDER  | String|

![GS000254.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364565/61982af1-44a7-ac17-c77d-cf532ea7760a.png)

[tFixedFlowInput]の[基本設定]のモード設定で[インラインコンテンツの使用]を選び以下設定します。

| 項目名              | 設定内容 |    説明  |
|:-----------------|:------------------|:------------------|
| フィールド区切り  | , | 入力データはcsv定義 |
| コンテンツ  | 下記コンテンツを記載 | csvファイルの代わりをここで定義する |

コンテンツ

```
1,kouda,男
2,mori,男
3,kubo,男
4,shimizu,男
5,aya,女
```

[tJavaFlex]と[tLogRow]を配置します。
[tLogRow]のモードは今回もテーブル形式にしておきます。
![GS000256.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364565/38add683-e08d-c3b8-bdbe-4bc9b36417cc.png)

それぞれをロウでつなぎます。
![GS000257.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364565/61dc5b7e-5b9e-ebe2-c4af-8ae7d8c5b48f.png)

[tJavaFlex]のスキーマの編集を開き全てのカラムを出力側にも複製します。
![GS000258b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364565/20769c2c-f964-fb25-ee4f-2c86487cb528.png)

![GS000259.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364565/80a729e0-b62e-ba3e-a19e-fbe9e8caaf1b.png)

出力側の[+]ボタンをクリックし**num**というカラムを追加します。
タイプは Integer にしておきます。
![GS000260b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364565/beca8456-de88-c238-450a-d3426a6adbf1.png)

[tJavaFlex]にコードを記載します。
これでロウ処理をまるごと2回ループさせていることになります。

開始コード

```java
for (int i=1; i<=2; i++) {
```

メインコード

```java
	row2.num = i;
```

終了コード

```java
}
```

![GS000261.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364565/e30677b9-abab-838e-fa0e-80894898ed80.png)

実行してみましょう。
5行のロウ処理が2回繰り返すことでロウを複製できました。
![GS000262.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364565/ae73168f-2378-6963-2fa5-c6e52308ac90.png)


###tJavaFLex（ログ解析サンプル）
最後にちょっとしたログ分析を行ってみます。
関数の処理ログを入力にしたもので「処理が**成功した**関数だけ**関数単位**で件数を集計する」というものです。
実は[awkでログ分析](https://qiita.com/airkoda/items/9ffbf0accfa7a3001bd5)を書いたのはここでtJavaFlexで実現するためでした。
awkでよくやるBEGIN,メイン(行処理),END処理と同じ考え方で実装ができるところを見ていただければと思います。

入力用ログファイルを準備します。

```sample.log
debug funcX start
debug reccnt 100
debug funcX error
--
debug funcA start
debug reccnt 10
debug funcA success
--
debug funcA start
debug reccnt 8
debug funcA success
--
debug funcA start
debug reccnt 12
debug funcA success
--
debug funcB start
debug reccnt 10
debug funcB success
--
debug funcB start
debug reccnt 8
debug funcB error
--
debug funcB start
debug reccnt 12
debug funcB success
```

リポジトリの[ジョブ]を右クリックし[ジョブの作成]を選択します。
[名前]に**tjavaflex2**と入力し[Finish]ボタンをクリックします。

![GS000263.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364565/da73bf32-3fd1-4c9f-b1e5-ca3a124977be.png)

3つのコンポーネント[tFileInputDelimited]と[tLogRow]と[tJavaFlex]を配置します。
![GS000265.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364565/1ad05cb3-e20c-35fa-6f0b-4b26cd9e2f2e.png)

[tFileInputDelimited]の設定を行います。

| 項目名              | 設定内容 |    説明  |
|:-----------------|:------------------|:------------------|
| ファイル名  | "C:/work/tjava/sample.log" | 適宜環境に合わせる |
| フィールド区切り  | " " | 半角スペースを定義 |

スキーマの編集は以下のとおり。

| 列      | カラム |  タイプ |
|:-----------------|:------------------|:------------------|
| 1列目    | col1  | String|
| 2列目    | col2  | String|
| 3列目    | col3  | String|
![GS000267.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364565/82ca04e5-78ff-f5b0-e64d-d088d61b9c72.png)

全てのコンポーネントをロウでつなぎます。
![GS000266.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364565/f6155be5-054f-a6f7-1f4c-9cb812d6de5b.png)

一度実行確認して読み込み部分の動作確認をしておきましょう。
![GS000268.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364565/88d87a47-2957-ed1c-4dae-aeae6a301615.png)

それでは本題のtJavaFlexにコードを書いていきます。
まず今回はHashMapを使うので詳細設定を開きインポート定義を記載します。

```java
import java.util.HashMap;
```
![GS000271.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364565/30902cee-cb0d-8346-944a-a1e349a3cbe9.png)

次に基本設定を開き以下コードを記載します。
それぞれの処理解説については[awkでログ分析](https://qiita.com/airkoda/items/9ffbf0accfa7a3001bd5)にあるので省略します。

開始コード

```java
// 変数初期化
String tmp = ""; // 関数名を保持
HashMap<String, Integer> cnt = new HashMap<String, Integer>(); // 関数毎の件数（一時保持）
HashMap<String, Integer> out = new HashMap<String, Integer>(); // 関数毎の件数（合計値）

```

メインコード

```java
// start判定
if (row7.col3.equals("start")) {
	tmp = row7.col2; // 関数名をセット
}
// reccnt判定
if (row7.col2.equals("reccnt")) {
	cnt.put(tmp, Integer.parseInt(row7.col3)); //件数をセット
}
// success判定
if (row7.col3.equals("success")) {
	
	out.put(row7.col2, cnt.getOrDefault(row7.col2,0) + out.getOrDefault(row7.col2,0)); //件数をセット
}
```

終了コード

```java
// 結果を出力
System.out.println("function,count");
for (String key : out.keySet()) {
        System.out.println(key + "," + out.get(key));
}
```

![GS000270.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364565/b6b4d0c5-6c33-cd50-b71d-deac0d68eec6.png)

それでは実行してみましょう。
以下のようにsuccessの関数を集計できていれば成功です。

```
function,count
funcA,30
funcB,22
```

![GS000272.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/364565/0d5bb56d-e42d-1b5e-ac89-96f64e4f2bdb.png)

##まとめ
今回はtJava系コンポーネントの使い方を駆け足で紹介しました。
Javaコードがそのまま記述できるため、ある意味なんでもできるコンポーネントだということはわかっていただけたかと思います。

あらたに利用したコンポーネントと用途をおさらいしておきます。

- **tJava**
    - 一度だけのJavaコードが実行できる
- **tJavaRow**
    - ロウ処理ができる
- **tJavaFlex**
    - サブジョブ内で開始処理、メイン処理、終了処理を記載できる


###tJava系コンポーネントの注意点
最後に、これらtJava系を使う場合、ちょっとした注意点をお伝えします。
下記2点を意識しておいてもらえたらと思います。

- **保守性が低くなるよ**
    - tJava系を使うとコンポーネントを開いてコードを読まないとどんな処理をしているのかがわからない
    - →Talendの良さの一つである保守性の高さが失われる可能性があります

- **META情報を参照するソリューションとの連携が出来なくなるよ**
    - 最近耳にすることも増えてきた「[データカタログ](https://jp.talend.com/products/data-catalog/)」といった仕組みは基本的にはDBであればシステムテーブルやDDL、
  TalendならTalend Administratoin Center(通称TAC)が管理しているMETA情報を利用してデータの流れを可視化する仕組みです。
  こういったものを利用しようとした場合、さすがに**tJavaで書かれたデータの流れまでは追うことができません。**


毎度のことですが、ほんのさわりの部分だけを解説していますのでぜひここから深掘りしてみてください。

##次回以降
Talend入門編、全14回はこれで終了です。
少しでも業務に役立てば幸いです。
お付き合いくださりありがとうございました！