/* * Copyright 2004-2015 the Seasar Foundation and the Others. * * 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 org.seasar.extension.jdbc.gen.internal.data; import java.io.File; import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.List; import org.seasar.extension.jdbc.gen.data.Loader; import org.seasar.extension.jdbc.gen.desc.ColumnDesc; import org.seasar.extension.jdbc.gen.desc.DatabaseDesc; import org.seasar.extension.jdbc.gen.desc.TableDesc; import org.seasar.extension.jdbc.gen.dialect.GenDialect; import org.seasar.extension.jdbc.gen.exception.LoadFailedRuntimeException; import org.seasar.extension.jdbc.gen.internal.exception.DumpFileEmptyRuntimeException; import org.seasar.extension.jdbc.gen.sql.SqlExecutionContext; import org.seasar.extension.jdbc.gen.sqltype.SqlType; import org.seasar.framework.log.Logger; import org.seasar.framework.util.StringUtil; /** * {@link Loader}の実装クラスです。 * * @author taedium */ public class LoaderImpl implements Loader { /** ロガー */ protected static Logger logger = Logger.getLogger(LoaderImpl.class); /** 方言 */ protected GenDialect dialect; /** ダンプファイルのエンコーディング */ protected String dumpFileEncoding; /** 区切り文字 */ protected char delimiter = ','; /** 拡張子 */ protected String extension = ".csv"; /** バッチサイズ */ protected int batchSize; /** ロードの前に存在するデータを削除する場合{@code true}、削除しない場合{@code false}を設定します。 */ protected boolean delete; /** * インスタンスを構築します。 * * @param dialect * 方言 * @param dumpFileEncoding * ダンプファイルのエンコーディング * @param batchSize * バッチサイズ * @param delete * ロードの前に存在するデータを削除する場合{@code true}、削除しない場合{@code false}を設定します。 */ public LoaderImpl(GenDialect dialect, String dumpFileEncoding, int batchSize, boolean delete) { if (dialect == null) { throw new NullPointerException("dialect"); } if (dumpFileEncoding == null) { throw new NullPointerException("dumpFileEncoding"); } if (batchSize < 0) { throw new IllegalArgumentException("batchSize"); } this.dialect = dialect; this.dumpFileEncoding = dumpFileEncoding; this.batchSize = batchSize; this.delete = delete; } public void load(SqlExecutionContext sqlExecutionContext, DatabaseDesc databaseDesc, File dumpFile) { TableDesc tableDesc = getTableDesc(databaseDesc, dumpFile); if (tableDesc == null) { return; } logger.log("DS2JDBCGen0013", new Object[] { dumpFile.getPath(), tableDesc.getFullName() }); DumpFileReader reader = createDumpFileReader(dumpFile); try { List<String> columnNameList = reader.readLine(); if (columnNameList == null) { throw new DumpFileEmptyRuntimeException(dumpFile.getPath()); } List<SqlType> sqlTypeList = getSqlTypeList(tableDesc, columnNameList); String sql = buildSql(tableDesc, columnNameList); sqlExecutionContext.begin(); try { if (delete) { deleteData(sqlExecutionContext, tableDesc); } preLoadData(sqlExecutionContext, tableDesc); loadData(sqlExecutionContext, reader, sqlTypeList, sql); postLoadData(sqlExecutionContext, tableDesc); } catch (Exception e) { logger.log("DS2JDBCGen0021", new Object[] { dumpFile.getPath() }); if (dialect.isTableNotFound(e)) { logger.log("DS2JDBCGen0012", new Object[] { tableDesc .getFullName() }); sqlExecutionContext.notifyException(); } else { LoadFailedRuntimeException ex = new LoadFailedRuntimeException( e, dumpFile.getPath(), reader.getLineNumber()); sqlExecutionContext.addException(ex); } return; } finally { sqlExecutionContext.end(); } logger.log("DS2JDBCGen0014", new Object[] { dumpFile.getPath(), tableDesc.getFullName(), reader.getLineNumber() - 1 }); } finally { reader.close(); } } public boolean isTarget(DatabaseDesc databaseDesc, File file) { if (databaseDesc == null || file == null) { return false; } if (!file.getName().endsWith(extension)) { return false; } if (getTableDesc(databaseDesc, file) == null) { return false; } return true; } /** * データを削除します。 * * @param sqlExecutionContext * SQL実行コンテキスト * @param tableDesc * テーブル記述 * @throws SQLException * SQL例外が発生した場合 */ protected void deleteData(SqlExecutionContext sqlExecutionContext, TableDesc tableDesc) throws SQLException { Statement statement = sqlExecutionContext.getStatement(); String sql = "delete from " + tableDesc.getFullName(); logger.debug(sql); statement.execute(sql); } /** * データのロード前に処理します。 * * @param sqlExecutionContext * SQL実行コンテキスト * @param tableDesc * テーブル記述 * @throws SQLException * SQL例外が発生した場合 */ protected void preLoadData(SqlExecutionContext sqlExecutionContext, TableDesc tableDesc) throws SQLException { if (tableDesc.hasIdentityColumn() && dialect.supportsIdentityInsertControlStatement()) { Statement statement = sqlExecutionContext.getStatement(); String sql = dialect.getIdentityInsertEnableStatement(tableDesc .getFullName()); logger.debug(sql); statement.execute(sql); } } /** * データをロードします。 * * @param sqlExecutionContext * SQL実行コンテキスト * @param reader * リーダ * @param sqlTypeList * {@link SqlType}のリスト * @param sql * SQL * @throws SQLException * SQL例外が発生した場合 */ protected void loadData(SqlExecutionContext sqlExecutionContext, DumpFileReader reader, List<SqlType> sqlTypeList, String sql) throws SQLException { PreparedStatement ps = sqlExecutionContext.getPreparedStatement(sql); List<String> valueList = null; boolean remaining = false; for (int i = 0; (valueList = reader.readLine()) != null; i++) { bindArgs(ps, sqlTypeList, valueList); ps.addBatch(); if (batchSize > 0 && (i + 1) % batchSize == 0) { ps.executeBatch(); sqlExecutionContext.commitLocalTx(); remaining = false; } else { remaining = true; } } if (remaining) { ps.executeBatch(); } } /** * データのロード後に処理します。 * * @param sqlExecutionContext * SQL実行コンテキスト * @param tableDesc * テーブル記述 * @throws SQLException * SQL例外が発生した場合 */ protected void postLoadData(SqlExecutionContext sqlExecutionContext, TableDesc tableDesc) throws SQLException { if (tableDesc.hasIdentityColumn() && dialect.supportsIdentityInsertControlStatement()) { Statement statement = sqlExecutionContext.getStatement(); String sql = dialect.getIdentityInsertDisableStatement(tableDesc .getFullName()); logger.debug(sql); statement.execute(sql); } } /** * 引数をバインドします。 * * @param ps * 準備されたステートメント * @param sqlTypeList * {@link SqlType}のリスト * @param valueList * 値のリスト * @throws SQLException * SQL例外が発生した場合 */ protected void bindArgs(PreparedStatement ps, List<SqlType> sqlTypeList, List<String> valueList) throws SQLException { for (int i = 0; i < sqlTypeList.size(); i++) { SqlType sqlType = sqlTypeList.get(i); String value = valueList.get(i); sqlType.bindValue(ps, i + 1, value); } } /** * SQLを組み立てます。 * * @param tableDesc * テーブル記述 * @param columnNameList * カラム名のリスト * @return SQL */ protected String buildSql(TableDesc tableDesc, List<String> columnNameList) { StringBuilder buf = new StringBuilder(); buf.append("insert into "); buf.append(tableDesc.getFullName()); buf.append(" ("); for (String columnName : columnNameList) { buf.append(columnName); buf.append(", "); } buf.setLength(buf.length() - 2); buf.append(") values ("); for (int i = 0; i < columnNameList.size(); i++) { buf.append("?, "); } buf.setLength(buf.length() - 2); buf.append(")"); return buf.toString(); } /** * {@link SqlType}のリストを返します。 * * @param tableDesc * テーブル記述 * @param columnNameList * カラム名のリスト * @return {@link SqlType}のリストを返します。 */ protected List<SqlType> getSqlTypeList(TableDesc tableDesc, List<String> columnNameList) { List<SqlType> sqlTypeList = new ArrayList<SqlType>(); for (int i = 0; i < columnNameList.size(); i++) { String columnName = columnNameList.get(i); ColumnDesc columnDesc = tableDesc.getColumnDesc(columnName); sqlTypeList.add(columnDesc.getSqlType()); } return sqlTypeList; } /** * ダンプファイルのトークナイザを作成します。 * * @return ダンプファイルのトークナイザ */ protected DumpFileTokenizer createDumpFileTokenizer() { return new DumpFileTokenizer(delimiter); } /** * ダンプファイルのリーダを作成します。 * * @param dumpFile * ダンプファイル * @return ダンプファイルのリーダ */ protected DumpFileReader createDumpFileReader(File dumpFile) { return new DumpFileReader(dumpFile, dumpFileEncoding, createDumpFileTokenizer()); } /** * テーブル記述を返します。 * * @param databaseDesc * データベース記述 * @param dumpFile * ダンプファイル * @return テーブル記述、存在しない場合{@code null} */ protected TableDesc getTableDesc(DatabaseDesc databaseDesc, File dumpFile) { String name = StringUtil.trimSuffix(dumpFile.getName(), extension); return databaseDesc.getTableDesc(name); } }