0
0

More than 3 years have passed since last update.

JavaFXで動的にテーブル列を設定する

Last updated at Posted at 2021-01-17

この記事について

将来的に書く予定の「JavaFX で DynamoDB Viewer作ってみた」記事の1ステップ。
結構大きな話になると思うので、少しずつ技術ポイント毎に記事を書いて、ある一定程度の要件を満たせた段階で前述まとめ記事書く予定。

前回記事:DynamoDBの情報を読み込んでJavaFXで表示してみる

今回は、前回時点での課題になっていた、JavaFXのTableViewのカラム情報の動的設定。
テーブル名を指定してDynamoDBから情報を読み込んで、その情報に従って列を設定する。
各データ型に応じて変更したい部分もあるが、今回はすべて文字列扱いにする。

※前回の記事が基本になってます。メソッドなど再説明していない部分があります。
 今回終了時のソースはこちら。Github

今回の進捗

テーブル部分に変更はないけど、カラムも動的に作ってます。
image.png

画面レイアウト変更

  • 既存列を削除
  • テーブル名、パーティションキー名、指定値入力用に TextField3つ追加
  • これらのTextFieldに fx:id を指定

java側修正

TableViewのカラムを動的にする。

  • TableViewのクラス指定をObservableList<String>にする。
  • TableViewのカラムを読み込む度にクリア&再作成
  • カラム再作成時には、setCellValueFactoryを使用してデータマッピングを指定する。
DynamoDbToolController.java
package com.silverboxsoft.controller;

import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;

import com.silverboxsoft.classes.DynamoDbCondition;
import com.silverboxsoft.classes.DynamoDbConditionJoinType;
import com.silverboxsoft.classes.DynamoDbConditionType;
import com.silverboxsoft.classes.DynamoDbResult;
import com.silverboxsoft.dao.DynamicDao;

import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;

public class DynamoDbToolController implements Initializable {

    // テーブルの表示データクラスをObservableList<String>にする
    @FXML
    TableView<ObservableList<String>> tableResultList;

    @FXML
    TextField txtFldTableName;

    @FXML
    TextField txtFldColumnName;

    @FXML
    TextField txtFldCondValue;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
    }

    @FXML
    protected void actLoad(ActionEvent ev) {
        String tableName = txtFldTableName.getText();
        DynamicDao dao = new DynamicDao();
        List<DynamoDbCondition> conditionList = new ArrayList<>();
        DynamoDbCondition cond = new DynamoDbCondition();
        cond.setColumnName(txtFldColumnName.getText());
        cond.setConditionType(DynamoDbConditionType.EQUAL);
        cond.setValue(txtFldCondValue.getText());
        conditionList.add(cond);

        // DynamoDBの結果を取得してテーブルにセットする
        DynamoDbResult result = dao.getResult(tableName, DynamoDbConditionJoinType.AND, conditionList);
        setTable(result);
    }

    private void setTable(DynamoDbResult result) {
        tableResultList.getItems().clear();

        tableResultList.getColumns().clear();
        for (int colIdx = 0; colIdx < result.getColumnCount(); colIdx++) {
            // 後述するDynamoDbResult からデータ名取得して設定
            String columnName = result.getDynamoDbColumn(colIdx).getColumnName();
            TableColumn<ObservableList<String>, String> dataCol = new TableColumn<>(columnName);
            final int finalColIdx = colIdx;
            //データマッピングを指定する
            dataCol.setCellValueFactory(param -> new ReadOnlyObjectWrapper<>(param.getValue().get(finalColIdx)));
            tableResultList.getColumns().add(dataCol);
        }

        tableResultList.getItems().addAll(result.getResultItems());
    }
}

DynamoDBの結果を取得するクラス作成

後々の為に、検索条件を指定できるようにしておく。

DynamicDao.java
package com.silverboxsoft.dao;

import java.util.HashMap;
import java.util.List;

import com.silverboxsoft.classes.DynamoDbCondition;
import com.silverboxsoft.classes.DynamoDbConditionJoinType;
import com.silverboxsoft.classes.DynamoDbResult;

import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.services.dynamodb.model.QueryRequest;
import software.amazon.awssdk.services.dynamodb.model.QueryResponse;

public class DynamicDao {

    private DynamoDbClient ddb;

    public DynamicDao() {
        Region region = Region.AP_NORTHEAST_1;
        this.ddb = DynamoDbClient.builder().region(region).build();
    }

    public DynamoDbResult getResult(String tableName, DynamoDbConditionJoinType conditionJoinType,
            List<DynamoDbCondition> conditionList) {
        HashMap<String, String> attrNameAlias = new HashMap<String, String>();
        HashMap<String, AttributeValue> attrValues = new HashMap<String, AttributeValue>();
        StringBuilder conditionExpression = new StringBuilder();

        for (DynamoDbCondition dbCond : conditionList) {
            attrNameAlias.put(dbCond.getAlias(), dbCond.getColumnName());
            attrValues.put(":" + dbCond.getColumnName(), AttributeValue.builder().s(dbCond.getValue()).build());

            if (conditionExpression.length() > 0) {
                conditionExpression.append(conditionJoinType.getJoinStr());
            }
            conditionExpression.append(dbCond.getConditionExpression());
        }

        System.out.println(conditionExpression.toString());
        QueryRequest queryReq = QueryRequest.builder()
                .tableName(tableName)
                .keyConditionExpression(conditionExpression.toString())
                .expressionAttributeNames(attrNameAlias)
                .expressionAttributeValues(attrValues)
                .build();

        QueryResponse response = ddb.query(queryReq);
        return new DynamoDbResult(response);
    }
}

DynamoDBの結果を保持するクラス作成

QueryResponse(JavaSDKにおける、DynamoDBの結果クラス)からTableViewに表示する形式に変更する。
AttributeVal が参考になる。

DynamoDbResult.java
package com.silverboxsoft.classes;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.services.dynamodb.model.QueryResponse;

public class DynamoDbResult {

    private List<DynamoDbColumn> columnList = new ArrayList<>();
    private Map<String, Integer> colNameIndex = new HashMap<>();
    private List<ObservableList<String>> resItems = new ArrayList<>();

    public DynamoDbResult(QueryResponse response) {
        for (Map<String, AttributeValue> resItem : response.items()) {
            analyzeOneItem(resItem);

            ObservableList<String> record = FXCollections.observableArrayList();
            for (int colIdx = 0; colIdx < columnList.size(); colIdx++) {
                DynamoDbColumn dbCol = getDynamoDbColumn(colIdx);
                String columnName = dbCol.getColumnName();
                record.add(getAttrString(dbCol, resItem.get(columnName)));
            }
            this.resItems.add(record);
        }
    }

    public int getColumnCount() {
        return columnList.size();
    }

    public int getRecordCount() {
        return resItems.size();
    }

    public List<ObservableList<String>> getResultItems() {
        return resItems;
    }

    public DynamoDbColumn getDynamoDbColumn(int index) {
        return columnList.get(index);
    }

    // DynamoDBの結果からカラム名とデータ形式抽出して保存
    private void analyzeOneItem(Map<String, AttributeValue> resItem) {
        for (String colName : resItem.keySet()) {
            AttributeValue attrVal = resItem.get(colName);
            if (!colNameIndex.containsKey(colName)) {
                DynamoDbColumn dbCol = new DynamoDbColumn();
                dbCol.setColumnName(colName);
                dbCol.setColumnType(getDynamoDbColumnType(attrVal));
                colNameIndex.put(colName, columnList.size());
                columnList.add(dbCol);
            }
        }
    }

    // https://sdk.amazonaws.com/java/api/2.0.0/software/amazon/awssdk/services/dynamodb/model/AttributeValue.html
    private DynamoDbColumnType getDynamoDbColumnType(AttributeValue attrVal) {
        if (attrVal.hasSs()) {
            return DynamoDbColumnType.STRING_SET;
        } else if (attrVal.hasNs()) {
            return DynamoDbColumnType.NUMBER_SET;
        } else if (attrVal.hasBs()) {
            return DynamoDbColumnType.BINARY_SET;
        } else if (attrVal.hasM()) {
            return DynamoDbColumnType.MAP;
        } else if (attrVal.hasL()) {
            return DynamoDbColumnType.LIST;
        }
        // StringとNumberを判断するメソッドが無い様なので、AttributeValのString出力から判断
        String attrValStr = attrVal.toString();
        if (attrValStr.startsWith("AttributeValue(N=")) {
            return DynamoDbColumnType.NUMBER;
        }
        System.out.println(attrValStr);
        return DynamoDbColumnType.STRING;
    }

    // 今はString型とNumberだけ対応
    private String getAttrString(DynamoDbColumn dbCol, AttributeValue attrVal) {
        if (dbCol.getColumnType() == DynamoDbColumnType.STRING) {
            return attrVal.s();
        } else if (dbCol.getColumnType() == DynamoDbColumnType.NUMBER) {
            return attrVal.n();
        }
        return attrVal.toString();
    }
}

DynamoDbColumnType というenumクラスも作成。

DynamoDbColumnType.java
package com.silverboxsoft.classes;

public enum DynamoDbColumnType {
    STRING, NUMBER, BOOLEAN, STRING_SET, NUMBER_SET, MAP, LIST, BINARY, BINARY_SET, NULL;
}

あとがき

今回はソースコードばかりになってしまった。基本技術は、後述の参考ページで書かれている感じ。
あと、今回提示したソース以外にも細かいソースは色々あるので、興味のある方は Github で。
次回は開発時などに使うlocalDynamoDbに対応とか、テーブルリスト表示とかしてみる予定。

参考にさせて頂いたページ

公式ページ

AttributeVal

皆さんの良記事

java fxmlのTableViewに動的な列と行を追加する方法

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0