    001/*
    002 * Copyright (c) 2009 The openGion Project.
    003 *
    004 * Licensed under the Apache License, Version 2.0 (the "License");
    005 * you may not use this file except in compliance with the License.
    006 * You may obtain a copy of the License at
    007 *
    008 *     http://www.apache.org/licenses/LICENSE-2.0
    009 *
    010 * Unless required by applicable law or agreed to in writing, software
    011 * distributed under the License is distributed on an "AS IS" BASIS,
    012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
    013 * either express or implied. See the License for the specific language
    014 * governing permissions and limitations under the License.
    015 */
    016package org.opengion.fukurou.process;
    017
    018import org.opengion.fukurou.system.OgRuntimeException ;         // 6.4.2.0 (2016/01/29)
    019import org.opengion.fukurou.util.Argument;
    020import org.opengion.fukurou.util.SystemParameter;
    021import org.opengion.fukurou.util.FileUtil;
    022import org.opengion.fukurou.util.HybsDateUtil;
    023import org.opengion.fukurou.system.LogWriter;
    024import org.opengion.fukurou.util.HybsEntry ;
    025import org.opengion.fukurou.system.Closer;
    026import org.opengion.fukurou.model.Formatter;
    027import org.opengion.fukurou.db.DBUtil ;
    028import org.opengion.fukurou.db.ConnectionFactory;
    029
    030import java.io.File ;
    031import java.io.PrintWriter ;
    032import java.util.Map ;
    033import java.util.LinkedHashMap ;
    034import java.util.Calendar ;
    035
    036import java.sql.Connection;
    037import java.sql.ResultSet;
    038import java.sql.PreparedStatement;
    039import java.sql.SQLException;
    040
    041/**
    042 * Process_DBFileout は、SELECT文 を指定し データベースの値を抜き出して、
    043 * 個々のファイルにセーブする、ChainProcess インターフェースの実装クラスです。
    044 * 上流(プロセスチェインのデータは上流から下流へと渡されます。)から
    045 * 受け取った LineModel を元に、１行単位に、SELECT文を実行します。
    046 *
    047 * 上流のカラムを、[カラム]変数で使用できます。
    048 * また、セーブするファイル名、更新日付等も、都度、更新可能です。
    049 *
    050 * データベース接続先等は、ParamProcess のサブクラス(Process_DBParam)に
    051 * 設定された接続(Connection)を使用します。
    052 *
    053 * 引数文字列中にスペースを含む場合は、ダブルコーテーション("") で括って下さい。
    054 * 引数文字列の 『=』の前後には、スペースは挟めません。必ず、-key=value の様に
    055 * 繋げてください。
    056 *
    057 * SQL文には、{&#064;DATE.YMDH}等のシステム変数が使用できます。
    058 *
    059 * @og.formSample
    060 *  Process_DBFileout -dbid=DBGE -insertTable=GE41
    061 *
    062 *   [ -dbid=DB接続ID           ] ： -dbid=DBGE (例: Process_DBParam の -configFile で指定する DBConfig.xml ファイルで規定)
    063 *   [ -select=検索SQL文        ] ： -select="SELECT * FROM GE41 WHERE SYSTEM_ID = [SYSTEM_ID] AND CLM = [CLM]"
    064 *   [ -selectFile=登録SQLﾌｧｲﾙ  ] ： -selectFile=select.sql
    065 *                                ：   -select や -selectFile が指定されない場合は、エラーです。
    066 *   [ -select_XXXX=固定値      ] ： -select_SYSTEM_ID=GE
    067 *                                     SQL文中の{&#064;XXXX}文字列を指定の固定値で置き換えます。
    068 *                                     WHERE SYSTEM_ID='{&#064;SYSTEM_ID}' ⇒ WHERE SYSTEM_ID='GE'
    069 *   [ -const_XXXX=固定値       ] ： -const_FGJ=1
    070 *                                     LineModel のキー(const_ に続く文字列)の値に、固定値を設定します。
    071 *                                     キーが異なれば、複数のカラム名を指定できます。
    072 *   [ -addHeader=ヘッダー      ] ： -addHeader="CREATE OR REPLACE "
    073 *   [ -addFooter=フッター      ] ： -addFooter="/\nSHOW ERROR;"
    074 *   [ -outFile=出力ファイル名  ] ： -outFile=[NAME].sql
    075 *   [ -append=[false/true]     ] ： 出力ﾌｧｲﾙを、追記する(true)か新規作成する(false)か。
    076 *   [ -sep=セパレータ文字      ] ： 各カラムを区切る文字列(初期値:TAB)
    077 *   [ -useLineCR=[false/true]  ] ： 各行の最後に、改行文字をつかるかどうか(初期値:true[付ける])
    078 *   [ -timestamp=更新日付      ] ： -timestamp="LAST_DDL_TIME"
    079 *   [ -fetchSize=1000          ] ：フェッチする行数(初期値:1000)  6.9.4.1 (2018/04/09)
    080 *   [ -display=[false/true]    ] ： 結果を標準出力に表示する(true)かしない(false)か(初期値:false[表示しない])
    081 *   [ -debug=[false/true]      ] ： デバッグ情報を標準出力に表示する(true)かしない(false)か(初期値:false[表示しない])
    082 *
    083 * @og.rev 6.4.8.3 (2016/07/15) 新規作成。
    084 *
    085 * @version  4.0
    086 * @author   Kazuhiko Hasegawa
    087 * @since    JDK5.0,
    088 */
    089public class Process_DBFileout extends AbstractProcess implements ChainProcess {
    090        private static final String SELECT_KEY  = "select_" ;
    091        private static final String CNST_KEY    = "const_" ;
    092
    093        private static final String ENCODE = "UTF-8" ;
    094
    095//      /** 6.9.3.0 (2018/03/26) データ検索時のフェッチサイズ  {@value} */
    096//      private static final int DB_FETCH_SIZE = 1001 ;
    097
    098        private Connection      connection              ;
    099        private PreparedStatement selPstmt      ;
    100
    101        private String          dbid            ;
    102        private String          select          ;
    103        private int[]           selClmNos       ;                       // select 時のファイルのヘッダーのカラム番号
    104        private String          outFilename     ;                       // 出力ファイル名
    105        private boolean         append          ;                       // ファイル追加(true:追加/false:通常)
    106        private String          timestamp       ;                       // 出力ファイルの更新日時
    107        private int                     tmstmpClm       = -1;           // 出力ファイルの更新日時のカラム番号
    108        private String          separator       = "\t";         // 各カラムを区切る文字列(初期値:TAB)
    109        private String          addHeader       ;                       // ヘッダー
    110        private String          addFooter       ;                       // フッター
    111        private boolean         useLineCR       = true;         // 各行の最後に、改行文字をつかるかどうか(初期値:true[付ける])
    112        private int                     fetchSize       = 1000;         // 6.9.4.1 (2018/04/09) 初期値を 1000 に設定
    113        private boolean         display         ;                       // false:表示しない
    114        private boolean         debug           ;                       // 5.7.3.0 (2014/02/07) デバッグ情報
    115
    116        private String[]        cnstClm         ;                       // 固定値を設定するカラム名
    117        private int[]           cnstClmNos      ;                       // 固定値を設定するカラム番号
    118        private String[]        constVal        ;                       // カラム番号に対応した固定値
    119
    120        private boolean         firstRow        = true;         // 最初の一行目
    121        private int                     count           ;
    122
    123        /** staticイニシャライザ後、読み取り専用にするので、ConcurrentHashMap を使用しません。 */
    124        private static final Map<String,String> MUST_PROPARTY   ;               // ［プロパティ］必須チェック用 Map
    125        /** staticイニシャライザ後、読み取り専用にするので、ConcurrentHashMap を使用しません。 */
    126        private static final Map<String,String> USABLE_PROPARTY ;               // ［プロパティ］整合性チェック Map
    127
    128        static {
    129                MUST_PROPARTY = new LinkedHashMap<>();
    130
    131                USABLE_PROPARTY = new LinkedHashMap<>();
    132                USABLE_PROPARTY.put( "dbid",    "Process_DBParam の -configFile で指定する DBConfig.xml ファイルで規定" );
    133                USABLE_PROPARTY.put( "select",  "検索SQL文(select or selectFile 必須)" +
    134                                                                        CR + "例: \"SELECT * FROM GE41 WHERE SYSTEM_ID = [SYSTEM_ID] AND CLM = [CLM]\"" );
    135                USABLE_PROPARTY.put( "selectFile",      "検索SQLファイル(select or selectFile 必須)例: select.sql" );
    136                USABLE_PROPARTY.put( "select_",         "SQL文中の{&#064;XXXX}文字列を指定の固定値で置き換えます。" +
    137                                                                        CR + "WHERE SYSTEM_ID='{&#064;SYSTEM_ID}' ⇒ WHERE SYSTEM_ID='GE'" );
    138                USABLE_PROPARTY.put( "const_",  "LineModel のキー(const_ に続く文字列)の値に、固定値を" +
    139                                                                        CR + "設定します。キーが異なれば、複数のカラム名を指定できます。" +
    140                                                                        CR + "例: -sql_SYSTEM_ID=GE" );
    141                USABLE_PROPARTY.put( "addHeader" ,      "ヘッダー" );
    142                USABLE_PROPARTY.put( "addFooter" ,      "フッター" );
    143                USABLE_PROPARTY.put( "outFile"  ,       "出力ファイル名 例: [NAME].sql" );
    144                USABLE_PROPARTY.put( "append"   ,       "出力ﾌｧｲﾙを、追記する(true)か新規作成する(false)か。" );
    145                USABLE_PROPARTY.put( "sep"              ,       "各カラムを区切る文字列(初期値:TAB)" );
    146                USABLE_PROPARTY.put( "useLineCR",       "各行の最後に、改行文字をつかるかどうか(初期値:true[付ける])" );
    147                USABLE_PROPARTY.put( "timestamp",       "出力ファイルの更新日付例: [LAST_DDL_TIME]" );
    148                USABLE_PROPARTY.put( "fetchSize","フェッチする行数 (初期値:1000)" );                                       // 6.9.4.1 (2018/04/09) 初期値を 1000 に設定
    149                USABLE_PROPARTY.put( "display", "結果を標準出力に表示する(true)かしない(false)か" +
    150                                                                                CR + "(初期値:false:表示しない)" );
    151                USABLE_PROPARTY.put( "debug",   "デバッグ情報を標準出力に表示する(true)かしない(false)か" +
    152                                                                                CR + "(初期値:false:表示しない)" );             // 5.7.3.0 (2014/02/07) デバッグ情報
    153        }
    154
    155        /**
    156         * デフォルトコンストラクター。
    157         * このクラスは、動的作成されます。デフォルトコンストラクターで、
    158         * super クラスに対して、必要な初期化を行っておきます。
    159         *
    160         */
    161        public Process_DBFileout() {
    162                super( "org.opengion.fukurou.process.Process_DBFileout",MUST_PROPARTY,USABLE_PROPARTY );
    163        }
    164
    165        /**
    166         * プロセスの初期化を行います。初めに一度だけ、呼び出されます。
    167         * 初期処理(ファイルオープン、ＤＢオープン等)に使用します。
    168         *
    169         * @og.rev 6.4.8.3 (2016/07/15) 新規作成。
    170         * @og.rev 6.9.4.1 (2018/04/09) fetchSize 指定を行います。
    171         *
    172         * @param   paramProcess データベースの接続先情報などを持っているオブジェクト
    173         */
    174        public void init( final ParamProcess paramProcess ) {
    175                final Argument arg = getArgument();
    176
    177                select          = arg.getFileProparty( "select","selectFile",false );
    178                separator       = arg.getProparty( "sep"                , separator             );
    179                outFilename     = arg.getProparty( "outFile"    , outFilename   );
    180                append          = arg.getProparty( "append"             , append                );
    181                addHeader       = arg.getProparty( "addHeader"  , addHeader             );
    182                addFooter       = arg.getProparty( "addFooter"  , addFooter             );
    183                useLineCR       = arg.getProparty( "useLineCR"  , useLineCR             );
    184                timestamp       = arg.getProparty( "timestamp"  , timestamp             );
    185                fetchSize       = arg.getProparty( "fetchSize"  , fetchSize             );              // 6.9.4.1 (2018/04/09) fetchSize 指定
    186                display         = arg.getProparty( "display"    , display               );
    187                debug           = arg.getProparty( "debug"              , debug                 );
    188
    189                addHeader = addHeader.replaceAll( "\\\\t" , "\t" ).replaceAll( "\\\\n" , "\n" );        // ｢\t｣と、｢\n｣の文字列を、タブと改行に変換します。
    190                addFooter = addFooter.replaceAll( "\\\\t" , "\t" ).replaceAll( "\\\\n" , "\n" );        // ｢\t｣と、｢\n｣の文字列を、タブと改行に変換します。
    191
    192                dbid            = arg.getProparty( "dbid" );
    193                connection      = paramProcess.getConnection( dbid );
    194
    195                if( select == null ) {
    196                        final String errMsg = "select または、selectFile は必ず指定してください。";
    197                        throw new OgRuntimeException( errMsg );
    198                }
    199
    200                // 3.8.0.1 (2005/06/17) {@DATE.XXXX} 変換処理の追加
    201                // {@DATE.YMDH} などの文字列を、yyyyMMddHHmmss 型の日付に置き換えます。
    202                // SQL文の {@XXXX} 文字列の固定値への置き換え
    203                final HybsEntry[] entry =arg.getEntrys(SELECT_KEY);                             // 配列
    204                final SystemParameter sysParam = new SystemParameter( select );
    205                select = sysParam.replace( entry );
    206
    207                final HybsEntry[] cnstKey = arg.getEntrys( CNST_KEY );          // 配列
    208                final int csize = cnstKey.length;
    209                cnstClm         = new String[csize];
    210                constVal        = new String[csize];
    211                for( int i=0; i<csize; i++ ) {
    212                        cnstClm[i]  = cnstKey[i].getKey();
    213                        constVal[i] = cnstKey[i].getValue();
    214                }
    215        }
    216
    217        /**
    218         * プロセスの終了を行います。最後に一度だけ、呼び出されます。
    219         * 終了処理(ファイルクローズ、ＤＢクローズ等)に使用します。
    220         *
    221         * @og.rev 6.4.8.3 (2016/07/15) 新規作成。
    222         *
    223         * @param   isOK トータルで、OKだったかどうか[true:成功/false:失敗]
    224         */
    225        public void end( final boolean isOK ) {
    226                final boolean flag1 = Closer.stmtClose( selPstmt );
    227                selPstmt = null;
    228
    229                // close に失敗しているのに commit しても良いのか？
    230                if( isOK ) {
    231                        Closer.commit( connection );
    232                }
    233                else {
    234                        Closer.rollback( connection );
    235                }
    236                ConnectionFactory.remove( connection,dbid );
    237
    238                if( ! flag1 ) {
    239                        final String errMsg = "select ステートメントをクローズ出来ません。" + CR
    240                                                                + " select=[" + select + "] , commit=[" + isOK + "]" ;
    241                        System.err.println( errMsg );
    242                }
    243        }
    244
    245        /**
    246         * 引数の LineModel を処理するメソッドです。
    247         * 変換処理後の LineModel を返します。
    248         * 後続処理を行わない場合(データのフィルタリングを行う場合)は、
    249         * null データを返します。つまり、null データは、後続処理を行わない
    250         * フラグの代わりにも使用しています。
    251         * なお、変換処理後の LineModel と、オリジナルの LineModel が、
    252         * 同一か、コピー(クローン)かは、各処理メソッド内で決めています。
    253         * ドキュメントに明記されていない場合は、副作用が問題になる場合は、
    254         * 各処理ごとに自分でコピー(クローン)して下さい。
    255         *
    256         * @og.rev 6.4.8.3 (2016/07/15) 新規作成。
    257         * @og.rev 6.9.4.1 (2018/04/09) fetchSize 指定を行います。
    258         * @og.rev 6.9.8.0 (2018/05/28) FindBugs:例外的戻り値を無視しているメソッド(mkdirs)
    259         *
    260         * @param       data オリジナルのLineModel
    261         *
    262         * @return      処理変換後のLineModel
    263         */
    264        public LineModel action( final LineModel data ) {
    265                count++ ;
    266                try {
    267                        if( firstRow ) {
    268                                makePrepareStatement( data );
    269
    270                                final int size   = cnstClm.length;
    271                                cnstClmNos = new int[size];
    272                                for( int i=0; i<size; i++ ) {
    273                                        cnstClmNos[i] = data.getColumnNo( cnstClm[i] );
    274                                }
    275
    276                                if( display ) { println( data.nameLine() ); }           // 5.7.3.0 (2014/02/07) デバッグ情報
    277
    278                                if( timestamp != null ) {
    279                                        tmstmpClm = data.getColumnNo( timestamp );
    280                                }
    281                                firstRow = false;
    282                        }
    283
    284                        // 固定値置き換え処理
    285                        for( int j=0; j<cnstClmNos.length; j++ ) {
    286                                data.setValue( cnstClmNos[j],constVal[j] );
    287                        }
    288
    289                        if( selClmNos != null ) {
    290                                for( int i=0; i<selClmNos.length; i++ ) {
    291                                        selPstmt.setObject( i+1,data.getValue(selClmNos[i]) );
    292                                }
    293                        }
    294
    295                        final Formatter fileFmt = new Formatter( data,outFilename );
    296                        final File outFile = new File( fileFmt.getFormatString(0) );
    297                        // 6.9.8.0 (2018/05/28) FindBugs:例外的戻り値を無視しているメソッド
    298//                      if( !outFile.getParentFile().exists() ) {
    299//                              outFile.getParentFile().mkdirs();
    300//                      }
    301                        final File parent = outFile.getParentFile();    // 親フォルダを取得。nullもありえる
    302                        if( parent == null || !parent.exists() && !parent.mkdirs() ) {
    303                                final String errMsg = "親フォルダを作成できませんでした。[" + data.getRowNo() + "]件目"    + CR
    304                                                                        + " outFile=[" + fileFmt.getFormatString(0) + "]"       + CR ;
    305                                throw new OgRuntimeException( errMsg );
    306                        }
    307
    308                        final String[][] rtn ;
    309                        try( ResultSet resultSet = selPstmt.executeQuery() ) {
    310                                rtn = DBUtil.resultToArray( resultSet,false );          // useHeader = false
    311                        }
    312
    313                        // ０件の場合は、ヘッダーもフッターも出力しません。
    314                        if( rtn.length > 0 ) {
    315                                try( PrintWriter writer = FileUtil.getPrintWriter( outFile,ENCODE,append ) ) {
    316                                        if( addHeader != null ) {
    317                                                final Formatter headerFmt = new Formatter( data,addHeader );
    318                                                final String header = headerFmt.getFormatString(0);
    319                                                writer.print( header );
    320                                        }
    321                                        for( int i=0; i<rtn.length; i++ ) {
    322                                                for( int j=0; j<rtn[i].length; j++ ) {
    323                                                        writer.print( rtn[i][j] );
    324                                                        writer.print( separator );
    325                                                }
    326                                                if( useLineCR ) { writer.println(); }
    327                                        }
    328                                        if( addFooter != null ) {
    329                                                final Formatter footerFmt = new Formatter( data,addFooter );
    330                                                final String footer = footerFmt.getFormatString(0);
    331                                                writer.print( footer );
    332                                        }
    333                                }
    334                        }
    335
    336                        if( tmstmpClm >= 0 ) {
    337                                final String tmStmp = String.valueOf( data.getValue( tmstmpClm ) );
    338                                final Calendar cal = HybsDateUtil.getCalendar( tmStmp );
    339                                // 6.9.8.0 (2018/05/28) FindBugs:例外的戻り値を無視しているメソッド
    340//                              outFile.setLastModified( cal.getTimeInMillis() );
    341                                if( !outFile.setLastModified( cal.getTimeInMillis() ) ) {
    342                                        final String errMsg = "タイムスタンプの更新が出来ませんでした。[" + data.getRowNo() + "]件目" + CR
    343                                                                                        + " outFile= [" + outFile + "]" + CR ;
    344                                        System.err.println( errMsg );
    345                                }
    346                        }
    347
    348                        if( display ) { println( data.dataLine() ); }
    349                }
    350                catch( final SQLException ex) {
    351                        final String errMsg = "検索処理でエラーが発生しました。[" + data.getRowNo() + "]件目"     + CR
    352                                                                + " select=[" + select + "]"                                                                            + CR
    353                                                                + " errCode=[" + ex.getErrorCode() + "] State=[" + ex.getSQLState() + "]" + CR
    354                                                                + " data=[" + data.dataLine() + "]" + CR ;
    355                        throw new OgRuntimeException( errMsg,ex );
    356                }
    357                return data;
    358        }
    359
    360        /**
    361         * 内部で使用する PreparedStatement を作成します。
    362         * 引数指定の SQL または、LineModel から作成した SQL より構築します。
    363         *
    364         * @og.rev 6.4.8.3 (2016/07/15) 新規作成。
    365         * @og.rev 6.9.3.0 (2018/03/26) データ検索時のフェッチサイズを設定。
    366         * @og.rev 6.9.4.1 (2018/04/09) fetchSize 指定を行います。
    367         *
    368         * @param       data    処理対象のLineModel
    369         */
    370        private void makePrepareStatement( final LineModel data ) {
    371
    372                final Formatter format = new Formatter( data,select );          // 6.4.3.4 (2016/03/11)
    373                select = format.getQueryFormatString();
    374                selClmNos = format.getClmNos();
    375
    376                for( int i=0; i<selClmNos.length; i++ ) {
    377                        // 指定のカラムが存在しない場合は、エラーにします。
    378                        if( selClmNos[i] < 0 ) {
    379                                final String errMsg = "フォーマットに対応したカラムが存在しません。" + CR
    380                                                                        + "select=[" + select + "]" + CR
    381                                                                        + "ClmKey=[" + format.getClmKeys()[i] + "]" + CR
    382                                                                        + "nameLine=[" + data.nameLine() + "]" + CR
    383                                                                        + "data=[" + data.dataLine() + "]" + CR ;
    384                                throw new OgRuntimeException( errMsg );
    385                        }
    386                }
    387
    388                try {
    389                        selPstmt = connection.prepareStatement( select );
    390//                      selPstmt.setFetchSize( DB_FETCH_SIZE );                         // 6.9.3.0 (2018/03/26) データ検索時のフェッチサイズ
    391                        selPstmt.setFetchSize( fetchSize );                                     // 6.9.4.1 (2018/04/09) fetchSize 指定
    392                }
    393                catch( final SQLException ex) {
    394                        // 5.7.2.2 (2014/01/24) SQL実行エラーを少し詳細に出力します。
    395                        final String errMsg = "PreparedStatement を取得できませんでした。" + CR
    396                                                                + "errMsg=[" + ex.getMessage() + "]" + CR
    397                                                                + "errCode=[" + ex.getErrorCode() + "] State=[" + ex.getSQLState() + "]" + CR
    398                                                                + "select=[" + select + "]" + CR
    399                                                                + "nameLine=[" + data.nameLine() + "]" + CR
    400                                                                + "data=[" + data.dataLine() + "]" + CR ;
    401                        throw new OgRuntimeException( errMsg,ex );
    402                }
    403        }
    404
    405        /**
    406         * プロセスの処理結果のレポート表現を返します。
    407         * 処理プログラム名、入力件数、出力件数などの情報です。
    408         * この文字列をそのまま、標準出力に出すことで、結果レポートと出来るような
    409         * 形式で出してください。
    410         *
    411         * @return   処理結果のレポート
    412         */
    413        public String report() {
    414                // 7.2.9.5 (2020/11/28) PMD:Consider simply returning the value vs storing it in local variable 'XXXX'
    415                return "[" + getClass().getName() + "]" + CR
    416//              final String report = "[" + getClass().getName() + "]" + CR
    417                                                        + TAB + "DBID         : " + dbid + CR
    418                                                        + TAB + "Input  Count : " + count ;
    419
    420//              return report ;
    421        }
    422
    423        /**
    424         * このクラスの使用方法を返します。
    425         *
    426         * @return      このクラスの使用方法
    427         * @og.rtnNotNull
    428         */
    429        public String usage() {
    430                final StringBuilder buf = new StringBuilder( BUFFER_LARGE )
    431                        .append( "Process_DBFileout は、SELECT文 を指定し データベースの値を抜き出して、"             ).append( CR )
    432                        .append( "個々のファイルにセーブする、ChainProcess インターフェースの実装クラスです。" ).append( CR )
    433                        .append( "上流(プロセスチェインのデータは上流から下流へと渡されます。)から"                            ).append( CR )
    434                        .append( "受け取った LineModel を元に、１行単位に、SELECT文を実行します。"                             ).append( CR )
    435                        .append( CR )
    436                        .append( "上流のカラムを、[カラム]変数で使用できます。"                                                                      ).append( CR )
    437                        .append( "また、セーブするファイル名、更新日付等も、都度、更新可能です。"                              ).append( CR )
    438                        .append( CR )
    439                        .append( "データベース接続先等は、ParamProcess のサブクラス(Process_DBParam)に"                    ).append( CR )
    440                        .append( "設定された接続(Connection)を使用します。"                                                                   ).append( CR )
    441                        .append( CR )
    442                        .append( "引数文字列中にスペースを含む場合は、ダブルコーテーション(\"\") で括って下さい。").append( CR )
    443                        .append( "引数文字列の 『=』の前後には、スペースは挟めません。必ず、-key=value の様に" ).append( CR )
    444                        .append( "繋げてください。"                                                                                                                             ).append( CR )
    445                        .append( CR )
    446                        .append( "SQL文には、{&#064;DATE.YMDH}等のシステム変数が使用できます。"                                     ).append( CR )
    447                        .append( CR ).append( CR )
    448                        .append( getArgument().usage() ).append( CR );
    449
    450                return buf.toString();
    451        }
    452
    453        /**
    454         * このクラスは、main メソッドから実行できません。
    455         *
    456         * @param       args    コマンド引数配列
    457         */
    458        public static void main( final String[] args ) {
    459                LogWriter.log( new Process_DBFileout().usage() );
    460        }
    461}