Package com.asakusafw.bulkloader.exporter

Source Code of com.asakusafw.bulkloader.exporter.ExportDataCopy

/**
* Copyright 2011-2014 Asakusa Framework Team.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.asakusafw.bulkloader.exporter;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

import com.asakusafw.bulkloader.bean.ExportTargetTableBean;
import com.asakusafw.bulkloader.bean.ExportTempTableBean;
import com.asakusafw.bulkloader.bean.ExporterBean;
import com.asakusafw.bulkloader.common.ConfigurationLoader;
import com.asakusafw.bulkloader.common.Constants;
import com.asakusafw.bulkloader.common.DBAccessUtil;
import com.asakusafw.bulkloader.common.DBConnection;
import com.asakusafw.bulkloader.common.ExportTempTableStatus;
import com.asakusafw.bulkloader.exception.BulkLoaderSystemException;
import com.asakusafw.bulkloader.log.Log;


/**
* エクスポートテンポラリテーブルからデータをコピーするクラス。
* @author yuta.shirai
*
*/
public class ExportDataCopy {

    static final Log LOG = new Log(ExportDataCopy.class);

    /**
     * 更新レコードのコピーが全て終了したかを表すフラグ。
     */
    private boolean copyEnd = true;

    /**
     * エクスポートテンポラリテーブルからデータをコピーする。
     * @param bean パラメータを保持するBean
     * @return コピー結果(true:成功、false:失敗(UPDATEが全て成功していなくてもtrueを返す。UPDATEの結果はupdateEndを参照する))
     */
    public boolean copyData(ExporterBean bean) {
        long maxRecord = Long.parseLong(ConfigurationLoader.getProperty(Constants.PROP_KEY_EXP_COPY_MAX_RECORD));
        Connection conn = null;
        try {
            // コネクションを取得する
            conn = DBConnection.getConnection();

            // テンポラリ管理テーブルの情報を取得する。
            List<ExportTempTableBean> tempBean = DBAccessUtil.getExportTempTable(bean.getJobflowSid());

            // Export対象テーブル分繰り返す
            List<String> l = bean.getExportTargetTableList();
            for (String tableName : l) {
                ExportTargetTableBean expTableBean = bean.getExportTargetTable(tableName);

                LOG.info("TG-EXPORTER-06002",
                        bean.getJobflowSid(), tableName, expTableBean.getExportTempTableName());

                // TODO 外側からO(N^2)となるのでやや気になる
                if  (isCopyEnd(tempBean, expTableBean, tableName)) {
                    // 当該テーブルのコピーが完了している場合はコピーを行わない。
                    LOG.info("TG-EXPORTER-06004",
                            bean.getJobflowSid(), tableName, expTableBean.getExportTempTableName());
                    continue;
                }
                if (expTableBean.getExportTempTableName() == null) {
                    // エクスポートテンポラリテーブルが存在しない場合はコピーを行わない
                    LOG.info("TG-EXPORTER-06005",
                            bean.getJobflowSid(), tableName, expTableBean.getExportTempTableName());
                    continue;
                }

                // 新規レコードにレコードロックを取得するかを判定
                boolean isGetRecordLock = getRecordLock(bean.getJobflowSid(), tableName, conn);

                // 新規レコードのコピー(重複していないデータ)
                copyNonDuplicateData(expTableBean, tableName, maxRecord, bean.getJobflowSid(), isGetRecordLock, conn);
                if (expTableBean.isDuplicateCheck()) {
                    // 新規レコードのコピー(重複していいるデータ(重複チェックを行う場合のみ))
                    copyDuplicateData(expTableBean, maxRecord, conn);
                }
                // 更新レコードのコピー
                boolean tableCopyEnd = copyUpdateData(expTableBean, tableName, maxRecord, bean.getJobflowSid(), conn);
                if (tableCopyEnd) {
                    // コピー完了を記録
                    copyExit(bean.getJobflowSid(), tableName, conn);
                } else {
                    copyEnd = false;
                }
                LOG.info("TG-EXPORTER-06003",
                        bean.getJobflowSid(), tableName, expTableBean.getExportTempTableName(), tableCopyEnd);
            }
            return true;
        } catch (BulkLoaderSystemException e) {
            try {
                DBConnection.rollback(conn);
            } catch (BulkLoaderSystemException e1) {
                LOG.log(e);
            }
            LOG.log(e);
            return false;
        } finally {
            DBConnection.closeConn(conn);
        }
    }
    /**
     * 当該テーブルのコピーが終了しているかを判断する。
     * @param tempBeans テンポラリ管理テーブルの情報
     * @param tableBean パラメータを保持するBean
     * @param tableName テーブル名
     * @return コピーが完了している場合trueを返す
     */
    private boolean isCopyEnd(
            List<ExportTempTableBean> tempBeans,
            ExportTargetTableBean tableBean,
            String tableName) {
        if (tempBeans == null || tempBeans.size() == 0) {
            return false;
        } else {
            for (ExportTempTableBean tempBean : tempBeans) {
                if (tempBean.getExportTableName().equals(tableName)) {
                    if (tableBean.getExportTempTableName() == null) {
                        // 再実行でLoadを行っていない場合はテンポラリテーブル名をセットする
                        tableBean.setExportTempTableName(tempBean.getTemporaryTableName());
                        tableBean.setDuplicateFlagTableName(tempBean.getDuplicateFlagTableName());
                    }
                    boolean completed = ExportTempTableStatus.COPY_EXIT.equals(
                            tempBean.getTempTableStatus());
                    return completed;
                }
            }
        }
        return false;
    }
    /**
     * テンポラリ管理テーブルにコピー完了を記録する。
     * @param jobflowSid ジョブフローSID
     * @param tableName テーブル名
     * @param conn コネクション
     * @throws BulkLoaderSystemException SQL例外が発生した場合
     */
    private void copyExit(
            String jobflowSid,
            String tableName,
            Connection conn) throws BulkLoaderSystemException {
        // ロード完了を記録するSQL
        String loadExitSql = "UPDATE EXPORT_TEMP_TABLE "
            + "SET TEMP_TABLE_STATUS=? "
            + "WHERE JOBFLOW_SID=? AND TABLE_NAME=?";

        PreparedStatement stmt = null;
        try {
            stmt = conn.prepareStatement(loadExitSql);
            stmt.setString(1, ExportTempTableStatus.COPY_EXIT.getStatus());
            stmt.setString(2, jobflowSid);
            stmt.setString(3, tableName);
            DBConnection.executeUpdate(
                    stmt,
                    loadExitSql,
                    new String[] { ExportTempTableStatus.COPY_EXIT.getStatus(), jobflowSid, tableName });
            DBConnection.commit(conn);
            LOG.info("TG-EXPORTER-06011", jobflowSid, tableName);
        } catch (SQLException e) {
            throw BulkLoaderSystemException.createInstanceCauseBySQLException(
                    e, this.getClass(), loadExitSql,
                    new String[] { ExportTempTableStatus.COPY_EXIT.getStatus(), jobflowSid, tableName });
        } finally {
            DBConnection.closePs(stmt);
        }
    }
    /**
     * 重複データを異常データテーブルにコピー(Insert)する。
     * コピーしたデータはエクスポートテンポラリテーブルから削除する。
     * @param expTableBean Export対象テーブルの設定を保持するBean
     * @param maxRecord コピーの最大レコード件数
     * @param conn コネクション
     * @throws BulkLoaderSystemException SQL例外が発生した場合
     */
    private void copyDuplicateData(
            ExportTargetTableBean expTableBean,
            long maxRecord,
            Connection conn) throws BulkLoaderSystemException {

        // 検索条件を作成
        String selectCondition = createDupSelectCondition(expTableBean, maxRecord);
        // コピーのSQLを作成
        String copySql = createDupInsertSql(expTableBean, selectCondition);
        // 削除のSQLを作成
        String delSql = createDupCopyDelSql(expTableBean, selectCondition);

        PreparedStatement stmt = null;

        while (true) {
            // データをコピー
            int copyCount = 0;
            try {
                stmt = conn.prepareStatement(copySql.toString());
                copyCount = DBConnection.executeUpdate(stmt, copySql.toString());
            } catch (SQLException e) {
                throw BulkLoaderSystemException.createInstanceCauseBySQLException(
                        e,
                        this.getClass(),
                        copySql.toString(),
                        new String[0]);
            } finally {
                DBConnection.closePs(stmt);
            }

            // コピーするレコードが存在しなくなった場合はコミットして終了
            if (copyCount == 0) {
                DBConnection.commit(conn);
                break;
            }

            // コピーしたレコードを削除
            try {
                stmt = conn.prepareStatement(delSql);
                DBConnection.executeUpdate(stmt, delSql, new String[0]);
                DBConnection.commit(conn);
            } catch (SQLException e) {
                throw BulkLoaderSystemException.createInstanceCauseBySQLException(
                        e,
                        this.getClass(),
                        delSql,
                        new String[0]);
            } finally {
                DBConnection.closePs(stmt);
            }

            LOG.info("TG-EXPORTER-06008",
                    expTableBean.getErrorTableName(),
                    expTableBean.getExportTempTableName(),
                    copySql.toString(),
                    delSql);
        }
    }
    /**
     * コピーした重複データを削除のSQLを作成する。
     * @param expTableBean Export対象テーブルの設定を保持する
     * @param selectCondition 検索条件
     * @return 削除のSQL
     */
    private String createDupCopyDelSql(ExportTargetTableBean expTableBean,
            String selectCondition) {
        StringBuilder delSqll = new StringBuilder("DELETE FROM ");
        delSqll.append(expTableBean.getExportTempTableName());
        delSqll.append(selectCondition);
        return delSqll.toString();
    }
    /**
     * 重複データをコピーするSQLを生成する。
     * @param expTableBean Export対象テーブルの設定を保持する
     * @param selectCondition 検索条件
     * @return 重複データをコピーするSQL
     */
    private String createDupInsertSql(ExportTargetTableBean expTableBean,
            String selectCondition) {
        List<String> columnList = DBAccessUtil.delErrorSystemColumn(
                expTableBean.getErrorTableColumns(),
                expTableBean.getErrorCodeColumn());
        String column = DBAccessUtil.joinColumnArray(columnList);

        StringBuilder copySql = new StringBuilder("INSERT INTO ");
        copySql.append(expTableBean.getErrorTableName());
        copySql.append(" (");
        copySql.append(column);
        copySql.append(",");
        copySql.append(Constants.getRegisteredDateTimeColumnName());
        copySql.append(",");
        copySql.append(Constants.getUpdatedDateTimeColumnName());
        copySql.append(",");
        copySql.append(expTableBean.getErrorCodeColumn());
        copySql.append(") SELECT ");
        copySql.append(column);
        copySql.append(",NOW(),NOW(),'");
        copySql.append(expTableBean.getErrorCode());
        copySql.append("' FROM ");
        copySql.append(expTableBean.getExportTempTableName());
        copySql.append(selectCondition);
        return copySql.toString();
    }
    /**
     * 重複データコピーの検索条件を作成する。
     * @param expTableBean Export対象テーブルの設定を保持するBean
     * @param maxRecord コピーの最大レコード件数
     * @return 検索条件
     */
    private String createDupSelectCondition(ExportTargetTableBean expTableBean,
            long maxRecord) {
        StringBuilder selectCondition = new StringBuilder(" WHERE ");
        selectCondition.append(Constants.getSidColumnName());
        selectCondition.append(" IS NULL AND EXISTS (SELECT ");
        selectCondition.append(expTableBean.getDuplicateFlagTableName());
        selectCondition.append(".");
        selectCondition.append(Constants.getTemporarySidColumnName());
        selectCondition.append(" FROM ");
        selectCondition.append(expTableBean.getDuplicateFlagTableName());
        selectCondition.append(" WHERE ");
        selectCondition.append(expTableBean.getDuplicateFlagTableName());
        selectCondition.append(".");
        selectCondition.append(Constants.getTemporarySidColumnName());
        selectCondition.append("=");
        selectCondition.append(expTableBean.getExportTempTableName());
        selectCondition.append(".");
        selectCondition.append(Constants.getTemporarySidColumnName());
        selectCondition.append(")");
        selectCondition.append(" ORDER BY ");
        selectCondition.append(Constants.getTemporarySidColumnName());
        selectCondition.append(" LIMIT ");
        selectCondition.append(maxRecord);
        return selectCondition.toString();
    }
    /**
     * 更新データをExport対象テーブルにコピーする。
     * テーブルロックを取得していない場合、コピーしたデータは行ロック状態に更新する。
     * また、コピーしたデータはエクスポートテンポラリテーブルから削除する。
     * @param tableBean Export対象テーブルの設定を保持するBean
     * @param tableName Export対象テーブル名
     * @param conn コネクション
     * @param maxRecord 毎回のコピーの最大レコード件数
     * @param jobflowSid ジョブフローSID
     * @return 全てのレコードをコピーした場合:true、更新対象レコードが見つからなかった場合:false
     * @throws BulkLoaderSystemException SQL例外が発生した場合
     */
    private boolean copyUpdateData(
            ExportTargetTableBean tableBean,
            String tableName,
            long maxRecord,
            String jobflowSid,
            Connection conn) throws BulkLoaderSystemException {

        String tempTableName = tableBean.getExportTempTableName();

        // テンポラリSIDの最小値を調べるSQL
        String minTempSidSql = createMinTempSidSql(Constants.getTemporarySidColumnName(), tempTableName);
        // テンポラリSIDの最大値を調べるSQL
        String maxTempSidSql = createMaxTempSidSql(Constants.getTemporarySidColumnName(), tempTableName);
        // テンポラリテーブルのレコード件数を調べるSQL
        String countTempSql = createCountTempSql(tempTableName);
        // 検索条件を作成
        String selectCondition = createUpdateSelectCondition(tableName, tempTableName);
        // コピーのSQLを作成
        String copySql = createUpdateCopySql(
                tableName, tempTableName, selectCondition, tableBean.getExportTableColumns());
        // 削除のSQLを作成
        String delSql = createUpdateRecordDelSql(tableName, tempTableName);
        // エラーになったレコードを取得するSQLを作成
        String errTempSql = createSelectErrTempRecordSql(tempTableName);

        PreparedStatement stmt = null;

        // テンポラリSIDの最小値を取得
        long minTempSid = 0;
        ResultSet rs = null;
        try {
            stmt = conn.prepareStatement(minTempSidSql);
            rs = DBConnection.executeQuery(stmt, minTempSidSql, new String[0]);
            rs.next();
            minTempSid =  rs.getLong(1);
        } catch (SQLException e) {
            throw BulkLoaderSystemException.createInstanceCauseBySQLException(
                    e,
                    this.getClass(),
                    minTempSidSql,
                    new String[0]);
        } finally {
            DBConnection.closeRs(rs);
            DBConnection.closePs(stmt);
        }

        // テンポラリSIDの最大値を取得
        long maxTempSid = 0;
        try {
            stmt = conn.prepareStatement(maxTempSidSql);
            rs = DBConnection.executeQuery(stmt, maxTempSidSql, new String[0]);
            rs.next();
            maxTempSid =  rs.getLong(1);
        } catch (SQLException e) {
            throw BulkLoaderSystemException.createInstanceCauseBySQLException(
                    e,
                    this.getClass(),
                    maxTempSidSql,
                    new String[0]);
        } finally {
            DBConnection.closeRs(rs);
            DBConnection.closePs(stmt);
        }

        // 現在のテンポラリSIDの位置を表す変数
        long currentCount = minTempSid;

        while (true) {
            Long maxCount = currentCount + maxRecord;

            // データをコピー
            int copyCount = 0;
            try {
                stmt = conn.prepareStatement(copySql);
                stmt.setLong(1, currentCount);
                stmt.setLong(2, maxCount);
                copyCount = DBConnection.executeUpdate(
                        stmt,
                        copySql,
                        new String[] { String.valueOf(currentCount), String.valueOf(maxCount) });
            } catch (SQLException e) {
                throw BulkLoaderSystemException.createInstanceCauseBySQLException(
                        e,
                        this.getClass(),
                        copySql,
                        new String[] { String.valueOf(currentCount), String.valueOf(maxCount) });
            } finally {
                DBConnection.closePs(stmt);
            }

            if (copyCount > 0) {
                // コピーしたレコードを削除
                try {
                    stmt = conn.prepareStatement(delSql);
                    stmt.setLong(1, currentCount);
                    stmt.setLong(2, maxCount);
                    DBConnection.executeUpdate(
                            stmt,
                            delSql,
                            new String[] { String.valueOf(currentCount), String.valueOf(maxCount) });
                    DBConnection.commit(conn);
                } catch (SQLException e) {
                    throw BulkLoaderSystemException.createInstanceCauseBySQLException(
                            e,
                            this.getClass(),
                            delSql,
                            new String[] { String.valueOf(currentCount), String.valueOf(maxCount) });
                } finally {
                    DBConnection.closePs(stmt);
                }
            }
            LOG.info("TG-EXPORTER-06009",
                    tableName,
                    tableBean.getExportTempTableName(),
                    copySql,
                    delSql,
                    currentCount,
                    maxCount);

            // カレントのテンポラリSIDがテンポラリSIDの最大値を超えた場合終了する。
            currentCount = maxCount + 1;
            if (currentCount > maxTempSid) {
                DBConnection.commit(conn);
                break;
            }
        }

        // テンポラリテーブルの件数を取得
        long tempCount = 0;
        try {
            stmt = conn.prepareStatement(countTempSql);
            rs = DBConnection.executeQuery(stmt, countTempSql, new String[0]);
            rs.next();
            tempCount = rs.getLong(1);
        } catch (SQLException e) {
            throw BulkLoaderSystemException.createInstanceCauseBySQLException(
                    e,
                    this.getClass(),
                    countTempSql,
                    new String[0]);
        } finally {
            DBConnection.closeRs(rs);
            DBConnection.closePs(stmt);
        }

        if (tempCount == 0) {
            LOG.info("TG-EXPORTER-06010",
                    tableName,
                    tableBean.getExportTempTableName());
            return true;
        } else {
            // エラーになったレコードをログに出力する
            StringBuilder errSid = new StringBuilder();
            try {
                stmt = conn.prepareStatement(errTempSql);
                rs = DBConnection.executeQuery(stmt, errTempSql, new String[0]);
                while (rs.next()) {
                    errSid.append(Constants.getSidColumnName());
                    errSid.append("=");
                    errSid.append(rs.getLong(Constants.getSidColumnName()));
                    errSid.append(",");
                    errSid.append(Constants.getTemporarySidColumnName());
                    errSid.append("=");
                    errSid.append(rs.getLong(Constants.getTemporarySidColumnName()));
                    errSid.append(" ");
                }
            } catch (SQLException e) {
                throw BulkLoaderSystemException.createInstanceCauseBySQLException(
                        e,
                        this.getClass(),
                        errTempSql.toString(),
                        new String[0]);
            } finally {
                DBConnection.closeRs(rs);
                DBConnection.closePs(stmt);
            }
            LOG.error("TG-EXPORTER-06001",
                    tableName,
                    tempTableName,
                    errSid.toString());
            return false;
        }
    }
    /**
     * 更新レコードコピーでエラーになったレコードを取得するSQLを作成する。
     * @param tempTableName テンポラリテーブル名
     * @return エラーになったレコードを取得するSQL
     */
    private String createSelectErrTempRecordSql(String tempTableName) {
        StringBuilder errTempSql = new StringBuilder("SELECT ");
        errTempSql.append(Constants.getTemporarySidColumnName());
        errTempSql.append(",");
        errTempSql.append(Constants.getSidColumnName());
        errTempSql.append(" FROM ");
        errTempSql.append(tempTableName);
        return errTempSql.toString();
    }
    /**
     * コピーした更新レコードを削除するSQLを作成する。
     * @param tableName テーブル名
     * @param tempTableName テンポラリテーブル名
     * @return 削除のSQL
     */
    private String createUpdateRecordDelSql(String tableName, String tempTableName) {
        StringBuilder delSql = new StringBuilder("DELETE FROM ");
        delSql.append(tempTableName);
        delSql.append(" WHERE EXISTS (SELECT ");
        delSql.append(Constants.getTemporarySidColumnName());
        delSql.append(" FROM ");
        delSql.append(tableName);
        delSql.append(" WHERE ");
        delSql.append(tableName);
        delSql.append(".");
        delSql.append(Constants.getSidColumnName());
        delSql.append("=");
        delSql.append(tempTableName);
        delSql.append(".");
        delSql.append(Constants.getSidColumnName());
        delSql.append(") AND ");
        delSql.append(Constants.getSidColumnName());
        delSql.append(" IS NOT NULL AND ");
        delSql.append(Constants.getTemporarySidColumnName());
        delSql.append(" BETWEEN ? AND ?");
        return delSql.toString();
    }
    /**
     * 更新レコードをコピーするSQLを作成する。
     * @param tableName テーブル名
     * @param tempTableName テンポラリテーブル名
     * @param selectCondition 検索条件
     * @param exportTableColumns エクスポートするカラム名
     * @return コピーのSQL
     */
    private String createUpdateCopySql(
            String tableName,
            String tempTableName,
            String selectCondition,
            List<String> exportTableColumns) {
        List<String> updateColumnList = DBAccessUtil.delSystemColumn(exportTableColumns);
        StringBuilder copySql = new StringBuilder("UPDATE ");
        copySql.append(tableName);
        copySql.append(",");
        copySql.append(tempTableName);
        copySql.append(" SET ");
        // SIDを設定
        copySql.append(tableName);
        copySql.append(".");
        copySql.append(Constants.getSidColumnName());
        copySql.append("=");
        copySql.append(tempTableName);
        copySql.append(".");
        copySql.append(Constants.getSidColumnName());
        copySql.append(",");
        // バージョン番号を設定
        copySql.append(tableName);
        copySql.append(".");
        copySql.append(Constants.getVersionColumnName());
        copySql.append("=");
        copySql.append(tableName);
        copySql.append(".");
        copySql.append(Constants.getVersionColumnName());
        copySql.append("+");
        copySql.append(Constants.SYS_COLUMN_INCREMENT_VERSION_NO);
        copySql.append(",");
        // 更新日時を設定
        copySql.append(tableName);
        copySql.append(".");
        copySql.append(Constants.getUpdatedDateTimeColumnName());
        copySql.append("=NOW(),");
        // 業務カラムを設定
        int updateColumnSize = updateColumnList.size();
        for (int i = 0; i < updateColumnSize; i++) {
            copySql.append(tableName);
            copySql.append(".");
            copySql.append(updateColumnList.get(i));
            copySql.append("=");
            copySql.append(tempTableName);
            copySql.append(".");
            copySql.append(updateColumnList.get(i));
            if (i + 1 < updateColumnSize) {
                copySql.append(",");
            }
        }
        copySql.append(selectCondition);
        return copySql.toString();
    }
    /**
     * 更新レコードコピーの検索条件を作成する。
     * @param tableName テーブル名
     * @param tempTableName テンポラリテーブル名
     * @return 検索条件
     */
    private String createUpdateSelectCondition(String tableName, String tempTableName) {
        StringBuilder selectCondition = new StringBuilder(" WHERE ");
        selectCondition.append(tableName);
        selectCondition.append(".");
        selectCondition.append(Constants.getSidColumnName());
        selectCondition.append("=");
        selectCondition.append(tempTableName);
        selectCondition.append(".");
        selectCondition.append(Constants.getSidColumnName());
        selectCondition.append(" AND ");
        selectCondition.append(tempTableName);
        selectCondition.append(".");
        selectCondition.append(Constants.getSidColumnName());
        selectCondition.append(" IS NOT NULL AND ");
        selectCondition.append(tempTableName);
        selectCondition.append(".");
        selectCondition.append(Constants.getTemporarySidColumnName());
        selectCondition.append(" BETWEEN ? AND ?");
        return selectCondition.toString();
    }
    /**
     * テンポラリテーブルのレコード件数を調べるSQLを生成する。
     * @param tempTableName テンポラリテーブル名
     * @return テンポラリテーブルのレコード件数を調べるSQL
     */
    private String createCountTempSql(String tempTableName) {
        StringBuilder countTempSql = new StringBuilder("SELECT COUNT(*) FROM ");
        countTempSql.append(tempTableName);
        return countTempSql.toString();
    }
    /**
     * テンポラリSIDの最大値を調べるSQLを生成する。
     * @param temporarySidColumnName テンポラリSIDのカラム名
     * @param tempTableName テンポラリテーブル名
     * @return テンポラリSIDの最大値を調べるSQL
     */
    private String createMaxTempSidSql(String temporarySidColumnName,
            String tempTableName) {
        StringBuilder maxTempSidSql = new StringBuilder("SELECT MAX(");
        maxTempSidSql.append(Constants.getTemporarySidColumnName());
        maxTempSidSql.append(") FROM ");
        maxTempSidSql.append(tempTableName);
        return maxTempSidSql.toString();
    }
    /**
     * テンポラリSIDの最小値を調べるSQLを生成する。
     * @param temporarySidColumnName テンポラリSIDのカラム名
     * @param tempTableName テンポラリテーブル名
     * @return テンポラリSIDの最小値を調べるSQL
     */
    private String createMinTempSidSql(String temporarySidColumnName, String tempTableName) {
        StringBuilder minTempSidSql = new StringBuilder("SELECT MIN(");
        minTempSidSql.append(Constants.getTemporarySidColumnName());
        minTempSidSql.append(") FROM ");
        minTempSidSql.append(tempTableName);
        return minTempSidSql.toString();
    }
    /**
     * 新規データをExport対象テーブルにコピー(Insert)する。
     * テーブルロックを取得していない場合、コピーしたデータは行ロック状態に更新する。
     * また、コピーしたデータはエクスポートテンポラリテーブルから削除する。
     * @param expTableBean Export対象テーブルの設定を保持するBean
     * @param tableName Export対象テーブル名
     * @param conn コネクション
     * @param maxRecord コピーの最大レコード件数
     * @param jobflowSid ジョブフローSID
     * @param isGetRecordLock レコードロックを取得するか
     * @throws BulkLoaderSystemException SQL例外が発生した場合
     */
    private void copyNonDuplicateData(
            ExportTargetTableBean expTableBean,
            String tableName,
            long maxRecord,
            String jobflowSid,
            boolean isGetRecordLock,
            Connection conn) throws BulkLoaderSystemException {

        String recordLockSql = null;
        // 検索条件を作成
        String selectCondition = createInsertselectcondition(expTableBean, maxRecord);
        // コピーのSQLを作成
        String copySql = createInsertCopySql(tableName, expTableBean, selectCondition);

        // 削除のSQLを作成
        String delSql = createInsertDelSql(expTableBean, selectCondition);

        PreparedStatement stmt = null;

        while (true) {
            // データをコピー
            int copyCount = 0;
            try {
                stmt = conn.prepareStatement(copySql.toString());
                copyCount = DBConnection.executeUpdate(stmt, copySql.toString(), new String[0]);
            } catch (SQLException e) {
                throw BulkLoaderSystemException.createInstanceCauseBySQLException(
                        e,
                        this.getClass(),
                        copySql.toString(),
                        new String[0]);
            } finally {
                DBConnection.closePs(stmt);
            }

            // コピーするレコードが存在しなくなった場合はコミットして終了
            if (copyCount == 0) {
                DBConnection.commit(conn);
                break;
            }

            // コピーしたレコードにレコードロックフラグを立てる
            if (isGetRecordLock) {
                // コピーしたデータのSIDを取得するSQL
                String selectSidSql = "SELECT LAST_INSERT_ID()";

                // ユーザー変数を設定するSQL
                StringBuilder userParam = new StringBuilder("@EXPORT_");
                userParam.append(tableName);
                userParam.append("_SID");
                String setUserParamSql = createSetUserParamSql(tableName, userParam);

                // レコードロックフラグを立てるSQL
                recordLockSql = createRecordLockSql(tableName, expTableBean, jobflowSid, userParam, selectCondition);

                // コピーしたレコードのSIDを取得
                ResultSet rs = null;
                String sid = null;
                try {
                    stmt = conn.prepareStatement(selectSidSql);
                    rs = DBConnection.executeQuery(stmt, selectSidSql, new String[0]);
                    rs.next();
                    sid =  rs.getString(1);
                } catch (SQLException e) {
                    throw BulkLoaderSystemException.createInstanceCauseBySQLException(
                            e, this.getClass(), selectSidSql, new String[0]);
                } finally {
                    DBConnection.closeRs(rs);
                    DBConnection.closePs(stmt);
                }

                // ユーザー変数をセットする
                try {
                    stmt = conn.prepareStatement(setUserParamSql);
                    long param = Long.parseLong(sid) - 1L;
                    stmt.setLong(1, param);
                    copyCount = DBConnection.executeUpdate(
                            stmt,
                            setUserParamSql,
                            new String[]{ String.valueOf(param) });
                } catch (SQLException e) {
                    throw BulkLoaderSystemException.createInstanceCauseBySQLException(
                            e, this.getClass(), setUserParamSql, new String[] { sid });
                } finally {
                    DBConnection.closePs(stmt);
                }
                // ロックフラグを立てる
                // Import時にレコードロックを取得している場合のみレコードロックを取得する
                try {
                    stmt = conn.prepareStatement(recordLockSql.toString());
                    copyCount = DBConnection.executeUpdate(
                            stmt,
                            recordLockSql.toString(),
                            new String[0]);
                } catch (SQLException e) {
                    throw BulkLoaderSystemException.createInstanceCauseBySQLException(
                            e, this.getClass(), recordLockSql.toString(), new String[0]);
                } finally {
                    DBConnection.closePs(stmt);
                }
            }

            // コピーしたレコードを削除
            try {
                stmt = conn.prepareStatement(delSql);
                DBConnection.executeUpdate(stmt, delSql, new String[0]);
                DBConnection.commit(conn);
            } catch (SQLException e) {
                throw BulkLoaderSystemException.createInstanceCauseBySQLException(
                        e, this.getClass(), delSql, new String[0]);
            } finally {
                DBConnection.closePs(stmt);
            }
            LOG.info("TG-EXPORTER-06007",
                    tableName,
                    expTableBean.getExportTempTableName(),
                    copySql,
                    recordLockSql,
                    delSql);
        }
    }
    /**
     * レコードロックフラグを立てるSQLを生成する。
     * @param tableName テーブル名
     * @param expTableBean Export対象テーブルの設定を保持するBean
     * @param jobflowSid ジョブフローSID
     * @param userParam ユーザー変数
     * @param selectCondition 検索条件
     * @return レコードロックフラグを立てるSQL
     */
    private String createRecordLockSql(String tableName,
            ExportTargetTableBean expTableBean, String jobflowSid, StringBuilder userParam, String selectCondition) {
        String rlTableName = DBAccessUtil.createRecordLockTableName(tableName);
        StringBuilder recordLockSql = new StringBuilder();
        recordLockSql.append("INSERT INTO ");
        recordLockSql.append(rlTableName);
        recordLockSql.append(" (");
        recordLockSql.append(Constants.getSidColumnName());
        recordLockSql.append(",JOBFLOW_SID) SELECT ");
        recordLockSql.append(userParam);
        recordLockSql.append(":=");
        recordLockSql.append(userParam);
        recordLockSql.append("+1,");
        recordLockSql.append(jobflowSid);
        recordLockSql.append(" FROM ");
        recordLockSql.append(expTableBean.getExportTempTableName());
        recordLockSql.append(selectCondition);
        return recordLockSql.toString();
    }
    /**
     * ユーザー変数を設定するSQLを生成する。
     * @param tableName テーブル名
     * @param userParam ユーザー変数
     * @return ユーザー変数を設定するSQL
     */
    private String createSetUserParamSql(String tableName,
            StringBuilder userParam) {
        StringBuilder setUserParamSql = new StringBuilder("SET ");
        setUserParamSql.append(userParam);
        setUserParamSql.append("=?");
        return setUserParamSql.toString();
    }
    /**
     * コピーした新規データを削除するSQLを作成する。
     * @param expTableBean Export対象テーブルの設定を保持するBean
     * @param selectCondition 検索条件
     * @return コピーした新規データを削除するSQL
     */
    private String createInsertDelSql(ExportTargetTableBean expTableBean,
            String selectCondition) {
        StringBuilder delSql = new StringBuilder("DELETE FROM ");
        delSql.append(expTableBean.getExportTempTableName());
        delSql.append(selectCondition);
        return delSql.toString();
    }
    /**
     * 新規データコピーのSQLを作成する。
     * @param tableName テーブル名
     * @param expTableBean Export対象テーブルの設定を保持するBean
     * @param selectCondition 検索条件
     * @return 新規データコピーのSQL
     */
    private String createInsertCopySql(String tableName,
            ExportTargetTableBean expTableBean, String selectCondition) {
        List<String> columnList = DBAccessUtil.delSystemColumn(expTableBean.getExportTableColumns());
        String column = DBAccessUtil.joinColumnArray(columnList);

        StringBuilder copySql = new StringBuilder("INSERT INTO ");
        copySql.append(tableName);
        copySql.append(" (");
        copySql.append(column);
        copySql.append(",");
        copySql.append(Constants.getRegisteredDateTimeColumnName());
        copySql.append(",");
        copySql.append(Constants.getUpdatedDateTimeColumnName());
        copySql.append(") SELECT ");
        copySql.append(column);
        copySql.append(",NOW(),NOW() FROM ");
        copySql.append(expTableBean.getExportTempTableName());
        copySql.append(selectCondition);
        return copySql.toString();
    }
    /**
     * 新規レコードコピーの検索条件を作成する。
     * @param expTableBean Export対象テーブルの設定を保持するBean
     * @param maxRecord コピー最大件数
     * @return 検索条件
     */
    private String createInsertselectcondition(
            ExportTargetTableBean expTableBean, long maxRecord) {
        StringBuilder selectCondition = new StringBuilder(" WHERE ");
        selectCondition.append(Constants.getSidColumnName());
        selectCondition.append(" IS NULL AND NOT EXISTS (SELECT ");
        selectCondition.append(Constants.getTemporarySidColumnName());
        selectCondition.append(" FROM ");
        selectCondition.append(expTableBean.getDuplicateFlagTableName());
        selectCondition.append(" WHERE ");
        selectCondition.append(expTableBean.getDuplicateFlagTableName());
        selectCondition.append(".");
        selectCondition.append(Constants.getTemporarySidColumnName());
        selectCondition.append("=");
        selectCondition.append(expTableBean.getExportTempTableName());
        selectCondition.append(".");
        selectCondition.append(Constants.getTemporarySidColumnName());
        selectCondition.append(")");
        selectCondition.append(" ORDER BY ");
        selectCondition.append(Constants.getTemporarySidColumnName());
        selectCondition.append(" LIMIT ");
        selectCondition.append(maxRecord);
        return selectCondition.toString();
    }
    /**
     * 新規レコードに対してレコードロックを取得するか判定する。
     *
     * Import時にレコードロックが取得されている場合にのみ、
     * 新規レコードに対してレコードロックを取得する。
     *
     * @param jobflowSid ジョブフローSID
     * @param tableName テーブル名
     * @param conn コネクション
     * @return レコードロック取得要否
     * @throws BulkLoaderSystemException SQL例外が発生した場合
     */
    private boolean getRecordLock(
            String jobflowSid,
            String tableName,
            Connection conn) throws BulkLoaderSystemException {
        String checkRecordLockSql = "SELECT COUNT(*) "
            + "FROM IMPORT_RECORD_LOCK "
            + "WHERE JOBFLOW_SID=? AND TABLE_NAME=?";
        boolean isRecordLock = false;
        PreparedStatement stmt = null;
        ResultSet rs = null;

        // レコードロック有無を確認
        try {
            stmt = conn.prepareStatement(checkRecordLockSql);
            stmt.setString(1, jobflowSid);
            stmt.setString(2, tableName);
            rs = DBConnection.executeQuery(
                    stmt, checkRecordLockSql, new String[] { jobflowSid, tableName });
            rs.next();
            int count = rs.getInt("COUNT(*)");
            if (count > 0) {
                isRecordLock = true;
            }
        } catch (SQLException e) {
            throw BulkLoaderSystemException.createInstanceCauseBySQLException(
                    e, this.getClass(), checkRecordLockSql, new String[] { jobflowSid, tableName });
        } finally {
            DBConnection.closeRs(rs);
            DBConnection.closePs(stmt);
        }

        return isRecordLock;
    }
    /**
     * 更新が終了している場合のみ{@code true}を返す。
     * @return 更新が終了している場合に{@code true}、そうでない場合は{@code false}
     */
    public boolean isUpdateEnd() {
        return copyEnd;
    }
}
TOP

Related Classes of com.asakusafw.bulkloader.exporter.ExportDataCopy

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.