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     */
016    package org.opengion.fukurou.xml;
017    
018    import org.opengion.fukurou.util.Closer;
019    import org.opengion.fukurou.util.LogWriter;
020    import org.opengion.fukurou.db.DBUtil;
021    
022    import java.io.Reader;
023    import java.io.BufferedReader;
024    import java.io.InputStreamReader;
025    import java.io.FileInputStream;
026    import java.util.Map;
027    import java.util.List;
028    import java.util.ArrayList;
029    import java.util.regex.Pattern;
030    import java.util.regex.Matcher;
031    import java.util.Arrays;
032    import java.util.Locale;
033    
034    import java.sql.DriverManager;
035    import java.sql.Connection;
036    import java.sql.Statement;
037    import java.sql.PreparedStatement;
038    import java.sql.ParameterMetaData;
039    import java.sql.DatabaseMetaData;
040    import java.sql.SQLException;
041    
042    /**
043     * こã?クラスは、オラクル XDKの oracle.xml.sql.dml.OracleXMLSave クラスと
044     * ほぼ同様ã?目çš?�§使用できるクラスですã?
045     * 拡張XDK形式ã?XMLファイルを読み込み、データベã?スに INSERT しますã?
046     *
047     * 拡張XDK形式ã?å…?�¨なã‚?オラクル XDK(Oracle XML Developer's Kit)につã�?�¦は、以下ã?
048     * リンクを参照願いますã?
049     * <a href="http://otn.oracle.co.jp/software/tech/xml/xdk/index.html" target="_blank" >
050     * XDK(Oracle XML Developer's Kit)</a>
051     *
052     * こã?クラスでは、MAP を登録する[ setDefaultMap( Map ) ]ことによりã€?
053     * XMLファイルに存在しなã�?‚«ラãƒ?‚’初期値として設定することが可能になりますã?
054     * 例えば、登録日ã‚??登録è€??またã?、テンプレートより各シスãƒ?ƒ ID毎に
055     * 登録するなどですã?
056     * 同様に、読み取っã�?XMLファイルのæƒ??を書き換える機è?[ setAfterMap( Map ) ]メソãƒ?ƒ‰
057     * により、カラãƒ??値の置き換えも可能ですã?
058     *
059     * 拡張XDK形式ã?å…?�¨なã‚?オラクル XDK(Oracle XML Developer's Kit)につã�?�¦は、以下ã?
060     * リンクを参照願いますã?
061     * <a href="http://otn.oracle.co.jp/software/tech/xml/xdk/index.html" target="_blank" >
062     * XDK(Oracle XML Developer's Kit)</a>
063     *
064     * 拡張XDK形式とは、ROW 以外に、SQL処ç�?”¨タグ(EXEC_SQL)を持つ XML ファイルですã?
065     * またã?登録するãƒ??ブル(table)ã‚?ROWSETタグの属æ?æƒ??として付与することができますã?
066     * (大æ–?­—小文字に注æ„?
067     * これは、オラクルXDKで処ç�?�™るå?合ã?無視されますã?で、同様に扱ã�?�“とがå?来ますã?
068     * こã?、EXEC_SQL は、それそれã? XMLãƒ??タをデータベã?スに登録する際にã€?
069     * SQL処ç�?‚’自動的に流す為の、SQLæ–?‚’記載しますã?
070     * こã?処ç�??、イベント毎に実行される為、その配置é ??重要ですã?
071     * こã?タグは、è¤?•°記述することもå?来ますが、BODY部にはã€?¼‘つのSQLæ–??み記述しますã?
072     *
073     *   &lt;ROWSET tableName="XX" &gt;
074     *       &lt;EXEC_SQL&gt;                    æœ??に記載して、å?期å?ç�?ãƒ??タクリアç­?を実行させるã€?
075     *           delete from GEXX where YYYYY
076     *       &lt;/EXEC_SQL&gt;
077     *       &lt;MERGE_SQL&gt;                   こã?SQLæ–?�§ UPDATEして、結果が0件ならINSERTを行いますã?
078     *           update GEXX set AA=[AA] , BB=[BB] where CC=[CC]
079     *       &lt;/MERGE_SQL&gt;
080     *       &lt;ROW num="1"&gt;
081     *           &lt;カラãƒ?&gt;値1&lt;/カラãƒ?&gt;
082     *             ?¥?¥?¥
083     *           &lt;カラãƒ?&gt;値n&lt;/カラãƒ?&gt;
084     *       &lt;/ROW&gt;
085     *        ?¥?¥?¥
086     *       &lt;ROW num="n"&gt;
087     *          ?¥?¥?¥
088     *       &lt;/ROW&gt;
089     *       &lt;EXEC_SQL&gt;                    æœ?¾Œに記載してã€??目の設å®?整合æ?登録)を行うã€?
090     *           update GEXX set AA='XX' , BB='XX' where YYYYY
091     *       &lt;/EXEC_SQL&gt;
092     *   &lt;ROWSET&gt;
093     *
094     * @version  4.0
095     * @author   Kazuhiko Hasegawa
096     * @since    JDK5.0,
097     */
098    public class HybsXMLSave implements TagElementListener {
099            /** シスãƒ?ƒ 依存ã?改行記号をセãƒ?ƒˆしますã?     */
100            private static final String CR = System.getProperty("line.separator");
101    
102            private String tableName                = null;
103            private String[] keyColumns             = null;
104            private Connection connection   = null;
105            private PreparedStatement insPstmt      = null;         // INSERT用の PreparedStatement
106            private PreparedStatement updPstmt      = null;         // UPDATE用の PreparedStatement
107            private ParameterMetaData insMeta       = null;
108            private ParameterMetaData updMeta       = null;
109            private int insCnt              = 0;
110            private int updCnt              = 0;
111            private int delCnt              = 0;
112            private int ddlCnt              = 0;                                    // 5.6.7.0 (2013/07/27) DDLæ–??カウンター
113            private Map<String,String>        defaultMap      = null;
114            private Map<String,String>        afterMap        = null;
115            private List<String>              updClms         = null;
116            private String[]                        insClms         = null;
117            private String                          lastSQL         = null;         // 5.6.6.1 (2013/07/12) ãƒ?ƒ�ãƒ?‚°用。最後に使用したSQLæ–?
118    
119            private final boolean useParamMetaData ;        // 4.0.0.0 (2007/09/25)
120    
121            // UPDATE時ã? [XXX] を取りå?しますã?\w は、単語構æ?æ–?­? [a-zA-Z_0-9]と同じ
122            private static final Pattern pattern = Pattern.compile( "\\[\\w*\\]" );
123    
124            // 5.6.9.2 (2013/10/18) EXEC_SQL のエラーを無視するかどã�?�‹を指定できますã?
125            private boolean isExecErrException = true;                      // true は、エラー時に Exception を発行しますã?
126    
127            /**
128             * コネクションを指定して、オブジェクトを構築しますã?
129             * ãƒ??ブル名ã?、拡張XDK形式ã?ROWSETタグのtableName属æ?に
130             * 記述しておくå¿?¦�がありますã?
131             *
132             * @param       conn    ãƒ??タベã?ス接ç¶?
133             */
134            public HybsXMLSave( final Connection conn ) {
135                    this( conn,null );
136            }
137    
138            /**
139             * コネクションとãƒ??ブル名をæŒ?®šして、オブジェクトを構築しますã?
140             * ここでæŒ?®šするテーブル名ã?、デフォルトテーブルとã�?�†扱ã�?�§すã?
141             * 拡張XDK形式ã?ROWSETタグのtableName属æ?にãƒ??ブル名が記述されてã�?‚‹場合ã?ã€?
142             * そちらが優先されますã?
143             *
144             * @og.rev 4.0.0.0 (2007/09/25) ParameterMetaData を使用したパラメータ設定追åŠ??
145             * @og.rev 5.3.8.0 (2011/08/01) useParamMetaData ã‚?こã?クラスで直接取得するã?(PostgreSQL対å¿?
146             *
147             * @param       conn    ãƒ??タベã?ス接ç¶?
148             * @param       table   ãƒ??ブルå�?ROWSETタグのtable属æ?が未設定時に使用)
149             */
150            public HybsXMLSave( final Connection conn,final String table ) {
151                    connection = conn;
152                    tableName  = table;
153    //              useParamMetaData = ApplicationInfo.useParameterMetaData( conn );
154                    useParamMetaData = useParameterMetaData( connection );          // 5.3.8.0 (2011/08/01)
155            }
156    
157            /**
158             * EXEC_SQL のエラー時に Exception を発行するかどã�?�‹を指定できまã�?初期値:true)ã€?
159             * true を指定すると、エラー時にはã€?RuntimeException ã‚?throw しますã?
160             * false にすると、標準エラー出力にのみ、å?力しますã?
161             * こã?フラグは、EXEC_SQL のみ有効ですã?それ以外ã?タブã?処ç�?�§は、エラーが発生すると
162             * そã?時点で、Exception を発行して、å?ç�?‚’終äº?�—ますã?
163             * 初期値は、true(Exception を発行すã‚? ですã?
164             *
165             * @og.rev 5.6.9.2 (2013/10/18) 新規追åŠ?
166             *
167             * @param flag true:Exception を発行すã‚?false:標準エラー出力に出力すã‚?
168             */
169            public void onExecErrException( final boolean flag ) {
170                    isExecErrException = flag;
171            }
172    
173            /**
174             * &lt;ROWSET&gt; タグのä¸?•ªæœ??に呼び出されますã?
175             * ROWSET の属æ?である、table 属æ?と、dbid 属æ? をã?TagElement の
176             * get メソãƒ?ƒ‰で取得できますã?
177             * 取得時のキーは、それぞれã?"TABLE" と "DBID" ですã?
178             *
179             * @param tag タグエレメンãƒ?
180             * @see org.opengion.fukurou.xml.TagElement
181             * @see HybsXMLHandler#setTagElementListener( TagElementListener )
182             */
183            public void actionInit( final TagElement tag ) {
184                    String table = tag.get( "tableName" );
185                    if( table != null ) { tableName = table; }
186            }
187    
188            /**
189             * &lt;ROW&gt; タグの endElement 処ç�?¯Žに呼び出されますã?
190             * こã? Listener をセãƒ?ƒˆすることにより、行データを取得é?度ã€?
191             * TagElement オブジェクトを作æ?しã?こã?メソãƒ?ƒ‰が呼び出されますã?
192             *
193             * @og.rev 4.0.0.0 (2007/05/09) ParameterMetaData を使用したパラメータ設定追åŠ??
194             * @og.rev 4.0.0.0 (2007/09/25) isOracle から useParamMetaData に変更
195             * @og.rev 4.3.7.0 (2009/06/01) HSQLDB対å¿?
196             * @og.rev 5.3.8.0 (2011/08/01) useParamMetaData  setNull 対å¿?PostgreSQL対å¿?
197             * @og.rev 5.6.6.1 (2013/07/12) lastSQL 対応ã?ãƒ?ƒ�ãƒ?‚°用に、最後に使用したSQLæ–?‚’残しますã?
198             *
199             * @param tag タグエレメンãƒ?
200             * @see org.opengion.fukurou.xml.TagElement
201             * @see HybsXMLHandler#setTagElementListener( TagElementListener )
202             */
203            public void actionRow( final TagElement tag ) {
204                    tag.setAfterMap( afterMap );
205    
206                    String[] vals = null;                   // 5.6.6.1 (2013/07/12) ãƒ?ƒ�ãƒ?‚°用
207                    try {
208                            // 更新SQL(MERGE_SQLタグ)が存在する場合ã?処ç�?
209                            int tempCnt = 0;
210                            if( updPstmt != null ) {
211    //                              String[] vals = tag.getValues( updClms );
212                                    vals = tag.getValues( updClms );                                                // 5.6.6.1 (2013/07/12) ãƒ?ƒ�ãƒ?‚°用
213                                    for( int j=0; j<vals.length; j++ ) {
214                                            // 4.3.7.0 (2009/06/01) HSQLDB対応ã?空æ–?­—ã?場å�?ullに置換え
215                                            if( vals[j] != null && vals[j].length() == 0 ){
216                                                    vals[j] = null;
217                                            }
218    
219                                            // 4.0.0.0 (2007/09/25) ParameterMetaData を使用したパラメータ設定追åŠ?
220                                            if( useParamMetaData ) {
221                                                    int type = updMeta.getParameterType( j+1 );
222                                                    // 5.3.8.0 (2011/08/01) setNull 対å¿?
223    //                                              updPstmt.setObject( j+1,vals[j],type );
224                                                    String val = vals[j];
225                                                    if( val == null || val.isEmpty() ) {
226                                                            updPstmt.setNull( j+1, type );
227                                                    }
228                                                    else {
229                                                            updPstmt.setObject( j+1, val, type );
230                                                    }
231                                            }
232                                            else {
233                                                    updPstmt.setObject( j+1,vals[j] );
234                                            }
235    
236                                    }
237                                    tempCnt = updPstmt.executeUpdate();
238                                    if( tempCnt > 1 ) {
239                                            String errMsg = "Update キーが重è¤?�—てã�?�¾すã?"
240                                                            + "TABLE=[" + tableName + "] ROW=["
241                                                            + tag.getRowNo() + "]" + CR
242                                                            + " SQL=[" + lastSQL + "]" + CR                         // 5.6.6.1 (2013/07/12) ãƒ?ƒ�ãƒ?‚°用
243                                                            + tag.toString() + CR
244                                                            + Arrays.toString( vals ) + CR ;                        // 5.6.6.1 (2013/07/12) ãƒ?ƒ�ãƒ?‚°用
245                                            throw new RuntimeException( errMsg );
246                                    }
247                                    updCnt += tempCnt;
248                            }
249                            // 更新ã�?0件の場合ã?、INSERT処ç�?‚’行いますã?
250                            if( tempCnt == 0 ) {
251                                    // 初回INSERT時ã?タグより、DB登録SQLæ–?‚’構築しますã?
252                                    if( insPstmt == null ) {
253                                            insClms = tag.getKeys();
254    //                                      String sql = insertSQL( insClms,tableName );
255    //                                      insPstmt = connection.prepareStatement( sql );
256                                            lastSQL    = insertSQL( insClms,tableName );            // 5.6.6.1 (2013/07/12) ãƒ?ƒ�ãƒ?‚°用
257                                            insPstmt = connection.prepareStatement( lastSQL );
258                                            // 4.0.0.0 (2007/09/25) ParameterMetaData を使用したパラメータ設定追åŠ?
259                                            if( useParamMetaData ) { insMeta = insPstmt.getParameterMetaData(); }
260                                    }
261    //                              String[] vals = tag.getValues( insClms );
262                                    vals = tag.getValues( insClms );                                                // 5.6.6.1 (2013/07/12) ãƒ?ƒ�ãƒ?‚°用
263                                    for( int j=0; j<vals.length; j++ ) {
264                                            // 4.3.7.0 (2009/06/01) HSQLDB対応ã?空æ–?­—ã?場å�?ullに置換え
265                                            if( vals[j] != null && vals[j].length() == 0 ){
266                                                    vals[j] = null;
267                                            }
268    
269                                            // 4.0.0.0 (2007/09/25) ParameterMetaData を使用したパラメータ設定追åŠ?
270                                            if( useParamMetaData ) {
271                                                    int type = insMeta.getParameterType( j+1 );
272                                                    // 5.3.8.0 (2011/08/01) setNull 対å¿?
273    //                                              insPstmt.setObject( j+1,vals[j],type );
274                                                    String val = vals[j];
275                                                    if( val == null || val.isEmpty() ) {
276                                                            insPstmt.setNull( j+1, type );
277                                                    }
278                                                    else {
279                                                            insPstmt.setObject( j+1, val, type );
280                                                    }
281                                            }
282                                            else {
283                                                    insPstmt.setObject( j+1,vals[j] );
284                                            }
285                                    }
286                                    insCnt += insPstmt.executeUpdate();
287                            }
288                    }
289                    catch( SQLException ex ) {
290                            String errMsg = "DB登録エラーが発生しましたã€?
291                                                    + "TABLE=[" + tableName + "] ROW=["
292                                                    + tag.getRowNo() + "]" + CR
293                                                    + " SQL=[" + lastSQL + "]" + CR                         // 5.6.6.1 (2013/07/12) ãƒ?ƒ�ãƒ?‚°用
294                                                    + tag.toString() + CR
295                                                    + Arrays.toString( vals ) + CR                          // 5.6.6.1 (2013/07/12) ãƒ?ƒ�ãƒ?‚°用
296                                                    + ex.getMessage() + ":" + ex.getSQLState() + CR ;
297                            throw new RuntimeException( errMsg,ex );
298                    }
299            }
300    
301            /**
302             * &lt;EXEC_SQL&gt; タグの endElement 処ç�?¯Žに呼び出されますã?
303             * getBody メソãƒ?ƒ‰を使用して、このタグのBODY部のæ–?­—å?を取得しますã?
304             * こã? Listener をセãƒ?ƒˆすることにより、EXEC_SQL ãƒ??タを取得é?度ã€?
305             * TagElement オブジェクトを作æ?しã?こã?メソãƒ?ƒ‰が呼び出されますã?
306             * EXEC_SQL タグでは、deleteæ–?‚„updateæ–?�ªど、特殊な前å?ç�?‚„後å?ç�?”¨の SQLと
307             * DDL?ˆデータ定義è¨?ªž:Data Definition Language?‰ã?処ç�?�ªども記述できますã?
308             * ここでは簡易的に、何か実行された場合ã?、delete 処ç�?�¨è€?�ˆ、削除カウントをåŠ?®—しã€?
309             * 0件で帰ってきた場合に、DDLが実行されたとè€?�ˆ、DDLカウントを?‹1しますã?
310             * ただしã?0件 delete もè?えられるため、SQLæ–??先é?æ–?­—によるチェãƒ?‚¯は入れておきますã?
311             *
312             * @og.rev 5.6.6.1 (2013/07/12) lastSQL 対応ã?ãƒ?ƒ�ãƒ?‚°用に、最後に使用したSQLæ–?‚’残しますã?
313             * @og.rev 5.6.7.0 (2013/07/27) DDL?ˆデータ定義è¨?ªž:Data Definition Language?‰ã?処ç�?»¶数追åŠ?
314             * @og.rev 5.6.9.2 (2013/10/18) EXEC_SQL のエラー時に Exception を発行するかどã�?�‹を指å®?
315             *
316             * @param tag タグエレメンãƒ?
317             * @see org.opengion.fukurou.xml.TagElement
318             * @see HybsXMLHandler#setTagElementListener( TagElementListener )
319             */
320            public void actionExecSQL( final TagElement tag ) {
321                    Statement execSQL = null ;
322                    try {
323    //                      String sql = tag.getBody();
324                            lastSQL = tag.getBody();                // 5.6.6.1 (2013/07/12) ãƒ?ƒ�ãƒ?‚°用
325                            execSQL = connection.createStatement();
326    //                      delCnt += execSQL.executeUpdate( sql ) ;
327    
328                            // 5.6.7.0 (2013/07/27) DDL?ˆデータ定義è¨?ªž:Data Definition Language?‰ã?処ç�?»¶数追åŠ?
329    //                      delCnt += execSQL.executeUpdate( lastSQL ) ;
330                            int cnt = execSQL.executeUpdate( lastSQL ) ;
331                            if( cnt > 0 ) { delCnt += cnt; }                             // 件数が返れば、DDLでなã�?�Ÿめã?削除数を加ç®?
332                            else {
333                                    String sql = lastSQL.trim().toUpperCase( Locale.JAPAN );
334                                    if( !sql.startsWith( "DELETE" ) && !sql.startsWith( "INSERT" ) && !sql.startsWith( "UPDATE" ) ) {
335                                            ddlCnt ++ ;
336                                    }
337                            }
338                    }
339                    catch( SQLException ex ) {
340                            String errMsg = "DB登録エラーが発生しましたã€?
341                                                    + "TABLE=[" + tableName + "] ROW=["
342                                                    + tag.getRowNo() + "]" + CR
343                                                    + " SQL=[" + lastSQL + "]" + CR                         // 5.6.6.1 (2013/07/12) ãƒ?ƒ�ãƒ?‚°用
344                                                    + tag.toString() + CR
345                                                    + ex.getMessage() + ":" + ex.getSQLState() + CR ;
346    
347                            // 5.6.9.2 (2013/10/18) EXEC_SQL のエラー時に Exception を発行するかどã�?�‹を指å®?
348    
349                            if( isExecErrException ) {
350                                    throw new RuntimeException( errMsg,ex );
351                            }
352                            else {
353                                    System.err.println( errMsg );
354                            }
355                    }
356                    finally {
357                            Closer.stmtClose( execSQL );
358                    }
359            }
360    
361            /**
362             * &lt;MERGE_SQL&gt; タグの endElement 処ç�?™‚に呼び出されますã?
363             * getBody メソãƒ?ƒ‰を使用して、このタグのBODY部のæ–?­—å?を取得しますã?
364             * MERGE_SQLタグは、ã?ージ処ç�?�—たいãƒ??タ部よりも上位に記述しておく
365             * å¿?¦�がありますが、中間部にè¤?•°回記述しても構いませんã€?
366             * こã?タグが現れるまでは、INSERT のみ実行されますã?こã?タグ以降ã?ã€?
367             * ä¸?—¦ UPDATE しã?結果ã�??�件の場合ã?、INSERTする流れになりますã?
368             * 完å?に INSERT のみであるãƒ??タを前半に、UPDATE/INSERTを行う
369             * ãƒ??タを後半に、その間に、MERGE_SQL タグをå?れることで、無意味な
370             * UPDATE を避けることが可能ですã?
371             * こã? Listener をセãƒ?ƒˆすることにより、MERGE_SQL ãƒ??タを取得é?度ã€?
372             * TagElement オブジェクトを作æ?しã?こã?メソãƒ?ƒ‰が呼び出されますã?
373             *
374             * @og.rev 4.0.0.0 (2007/05/09) ParameterMetaData を使用したパラメータ設定追åŠ??
375             * @og.rev 4.0.0.0 (2007/09/25) isOracle から useParamMetaData に変更
376             * @og.rev 5.6.6.1 (2013/07/12) lastSQL 対応ã?ãƒ?ƒ�ãƒ?‚°用に、最後に使用したSQLæ–?‚’残しますã?
377             *
378             * @param tag タグエレメンãƒ?
379             * @see org.opengion.fukurou.xml.TagElement
380             * @see HybsXMLHandler#setTagElementListener( TagElementListener )
381             */
382            public void actionMergeSQL( final TagElement tag ) {
383                    if( updPstmt != null ) {
384                            String errMsg = "MERGE_SQLタグがã?è¤?•°回記述されてã�?�¾すã?"
385                                                    + "TABLE=[" + tableName + "] ROW=["
386                                                    + tag.getRowNo() + "]" + CR
387                                                    + " SQL=[" + lastSQL + "]" + CR                         // 5.6.6.1 (2013/07/12) ãƒ?ƒ�ãƒ?‚°用
388                                                    + tag.toString() + CR;
389                            throw new RuntimeException( errMsg );
390                    }
391    
392                    String orgSql = tag.getBody();
393                    Matcher matcher = pattern.matcher( orgSql );
394                    updClms = new ArrayList<String>();
395                    while( matcher.find() ) {
396                            // ここでは、[XXX]にマッチする為、前後ã?[]を取り除きますã?
397                            updClms.add( orgSql.substring( matcher.start()+1,matcher.end()-1 ) );
398                    }
399    //              String sql = matcher.replaceAll( "?" );
400                    lastSQL = matcher.replaceAll( "?" );            // 5.6.6.1 (2013/07/12) ãƒ?ƒ�ãƒ?‚°用
401    
402                    try {
403    //                      updPstmt = connection.prepareStatement( sql );
404                            updPstmt = connection.prepareStatement( lastSQL );
405                            // 4.0.0.0 (2007/09/25) ParameterMetaData を使用したパラメータ設定追åŠ?
406                            if( useParamMetaData ) { updMeta = updPstmt.getParameterMetaData(); }
407                    }
408                    catch( SQLException ex ) {
409                            String errMsg = "Statement作æ?時にエラーが発生しましたã€?
410                                                    + "TABLE=[" + tableName + "] ROW=["
411                                                    + tag.getRowNo() + "]" + CR
412                                                    + " SQL=[" + lastSQL + "]" + CR                         // 5.6.6.1 (2013/07/12) ãƒ?ƒ�ãƒ?‚°用
413                                                    + tag.toString() + CR
414                                                    + ex.getMessage() + ":" + ex.getSQLState() + CR ;
415                            throw new RuntimeException( errMsg,ex );
416                    }
417            }
418    
419            /**
420             * UPDATE,DELETE を行う場合ã? WHERE 条件になるキー配å?
421             * こã?キーの AND 条件でカラãƒ?‚’特定し、UPDATE,DELETE などの処ç�?‚’
422             * 行いますã?
423             *
424             * @param       keyCols WHERE条件になるキー配å?
425             */
426            public void setKeyColumns( final String[] keyCols ) {
427                    keyColumns = new String[keyCols.length];
428                    System.arraycopy( keyCols,0,keyColumns,0,keyColumns.length );
429            }
430    
431            /**
432             * XMLファイルを読み取る前にæŒ?®šするカラãƒ?�¨値のペア(マッãƒ?æƒ??をセãƒ?ƒˆしますã?
433             *
434             * こã?カラãƒ?�¨値のペアのマップã?、オブジェクト構築前に設定される為ã€?
435             * XMLファイルにキーが存在してã�?‚‹場合ã?、å?が書き変わりますã?(XML優å…?
436             * XMLファイルにキーが存在してã�?�ªã�??合ã?、ここでæŒ?®šするMapの値ã�?
437             * 初期設定å?として使用されますã?
438             * ここでæŒ?®šすã‚?Map に LinkedHashMap を使用する場合ã?カラãƒ??ã‚?
439             * æŒ?®šすることがå?来ますã?
440             *
441             * @param       map     初期設定するカラãƒ?ƒ‡ータマッãƒ?
442             * @see #setAfterMap( Map )
443             */
444            public void setDefaultMap( final Map<String,String> map ) { defaultMap = map; }
445    
446            /**
447             * XMLファイルを読み取った後でæŒ?®šするカラãƒ?�¨値のペア(マッãƒ?æƒ??をセãƒ?ƒˆしますã?
448             *
449             * こã?カラãƒ?�¨値のペアのマップã?、オブジェクト構築後に設定される為ã€?
450             * XMLファイルのキーの存在に関係なくã?Mapのキーと値が使用されますã?(Map優å…?
451             * null を設定したå?合ã?、なにもå?ç�?�•れませんã€?
452             *
453             * @param map   後設定するカラãƒ?ƒ‡ータマッãƒ?
454             * @see #setDefaultMap( Map )
455             */
456            public void setAfterMap( final Map<String,String> map ) { afterMap = map; }
457    
458            /**
459             * ãƒ??タベã?スに追åŠ??ç�?INSERT)を行いますã?
460             *
461             * 先にæŒ?®šされたコネクションを用ã�?�¦、指定ã?ãƒ??ブルに INSERT しますã?
462             * 引数には、XMLファイルを指定したリーãƒ??をセãƒ?ƒˆしますã?
463             * コネクションは、終äº?¾Œã?コミットされますã?(close されませんã€?
464             * リーãƒ??のクローズは、ここでは行ってã�?�¾せんã€?
465             *
466             * @og.rev 5.1.1.0 (2009/11/11) insMeta , updMeta のクリア(気休め)
467             *
468             * @param       reader  XMLファイルを指定するリーãƒ??
469             */
470            public void insertXML( final Reader reader ) {
471                    try {
472                            HybsXMLHandler handler = new HybsXMLHandler();
473                            handler.setTagElementListener( this );
474                            handler.setDefaultMap( defaultMap );
475    
476                            handler.parse( reader );
477                    }
478                    finally {
479                            Closer.stmtClose( insPstmt );
480                            Closer.stmtClose( updPstmt );
481                            insPstmt = null;
482                            updPstmt = null;
483                            insMeta = null;         // 5.1.1.0 (2009/11/11)
484                            updMeta = null;         // 5.1.1.0 (2009/11/11)
485                    }
486            }
487    
488            /**
489             * インサート用のSQLæ–?‚’作æ?しますã?
490             *
491             * @param       columns インサートするカラãƒ?��
492             * @param       tableName       インサートするテーブルå�?
493             *
494             * @return      インサート用のSQLæ–?
495             */
496            private String insertSQL( final String[] columns,final String tableName ) {
497                    if( tableName == null ) {
498                            String errMsg = "tableName がセãƒ?ƒˆされてã�?�¾せんã€? + CR
499                                                    + "tableName は、コンストラクタでæŒ?®šするか、ROWSETのtableName属æ?で"
500                                                    + "æŒ?®šしておくå¿?¦�がありまã�? + CR ;
501                            throw new RuntimeException( errMsg );
502                    }
503    
504                    StringBuilder sql = new StringBuilder();
505                    sql.append( "INSERT INTO " ).append( tableName );
506                    sql.append( " ( " );
507                    sql.append( columns[0] );
508                    for( int i=1; i<columns.length; i++ ) {
509                            sql.append( "," ).append( columns[i] );
510                    }
511                    sql.append( " ) VALUES ( " );
512                    sql.append( "?" );
513                    for( int i=1; i<columns.length; i++ ) {
514                            sql.append( "," ).append( "?" );
515                    }
516                    sql.append( " )" );
517    
518                    return sql.toString();
519            }
520    
521            /**
522             * ãƒ??タベã?スに追åŠ?�—た件数を返しますã?
523             *
524             * @return 登録件数
525             */
526            public int getInsertCount() { return insCnt; }
527    
528            /**
529             * ãƒ??タベã?スを更新した件数を返しますã?
530             * これは、拡張XDK形式で、MERGE_SQL タグを使用した場合ã?更新処ç�?»¶数ã‚?
531             * 合計したå?を返しますã?
532             *
533             * @return 更新件数
534             */
535            public int getUpdateCount() { return updCnt; }
536    
537            /**
538             * ãƒ??タベã?スに変更(更新、削除を含ã‚?した件数を返しますã?
539             * これは、拡張XDK形式で、EXEC_SQL タグを使用した場合ã?実行件数を合計しã�?
540             * 値を返しますã?
541             * よって、更新かã?追åŠ?�‹、削除かã?、判りませんがã?通常 登録前に削除する
542             * ケースで使われることから、deleteCount としてã�?�¾すã?
543             *
544             * @return 変更件数(主に、削除件数)
545             */
546            public int getDeleteCount() { return delCnt; }
547    
548            /**
549             * ãƒ??タベã?スにDDL?ˆデータ定義è¨?ªž:Data Definition Language?‰å?ç�?�—た件数を返しますã?
550             * これは、拡張XDK形式で、EXEC_SQL タグを使用した場合ã?実行件数を合計しã�?
551             * 値を返しますã?
552             * EXEC_SQL では、登録前に削除する delete 処ç�?‚‚、EXEC_SQL タグを使用して実行しますが
553             * そã?処ç�?�¨åˆ?�‘てカウントしますã?
554             *
555             * @og.rev 5.6.7.0 (2013/07/27) DDL?ˆデータ定義è¨?ªž:Data Definition Language?‰ã?処ç�?»¶数追åŠ?
556             *
557             * @return DDL?ˆデータ定義è¨?ªž:Data Definition Language?‰å?ç�?�—た件数
558             */
559            public int getDDLCount() { return ddlCnt; }
560    
561            /**
562             * 実際に登録されã�?ãƒ??ブル名を返しますã?
563             *
564             * ãƒ??ブル名ã?、拡張XDK形式ã?ROWSETタグのtableName属æ?に
565             * 記述しておくかã?コンストラクターで引数として渡しますã?
566             * 両方æŒ?®šされた場合ã?、ROWSETタグのtableName属æ?が優先されますã?
567             * ここでの返り値は、実際に使用されã�?ãƒ??ブル名ですã?
568             *
569             * @return 変更件数(主に、削除件数)
570             */
571            public String getTableName() { return tableName; }
572    
573            /**
574             * こã?接続が、PreparedStatement#getParameterMetaData() を使用するかどã�?�‹を判定しますã?
575             * 本来は、ConnectionFactory#useParameterMetaData(String)を使ã�?�¹きだがã?dbid が無ã�?�Ÿめã?直接取得しますã?
576             *
577             * @og.rev 5.3.8.0 (2011/08/01) 新規作æ? ( ApplicationInfo#useParameterMetaData(Connection) からコピã? )
578             * @og.rev 5.6.7.0 (2013/07/27) ProductName は、DBUtil 経由で取得するã?
579             *
580             * @param   conn 接続å?(コネクション)
581             *
582             * @return      使用する場合:true / そã?ä»?false
583             */
584            private static boolean useParameterMetaData( final Connection conn ) {
585    
586                    String dbProductName = DBUtil.getProductName( conn );
587    
588                    return "PostgreSQL".equalsIgnoreCase( dbProductName ) ;
589    
590    //              try {
591    //                      DatabaseMetaData meta = conn.getMetaData();
592    //                      String dbProductName = meta.getDatabaseProductName();
593    //
594    //                      if( "PostgreSQL".equalsIgnoreCase( dbProductName ) ) { return true; }
595    //
596    //                      return false ;
597    //              }
598    //              catch( SQLException ex ) {
599    //                      String errMsg = "DatabaseMetaData から、getDatabaseProductName を取得できませんでしたã€?
600    //                                      + ex.getMessage() + ":" + ex.getSQLState() ;
601    //                      throw new RuntimeException( errMsg,ex );
602    //              }
603            }
604    
605            /**
606             * ãƒ?‚¹ト用のメインメソãƒ?ƒ‰
607             *
608             * Usage: java org.opengion.fukurou.xml.HybsXMLSave USER PASSWD URL TABLE FILE [ENCODE] [DRIVER]
609             *    USER    : DB接続ユーザー(GE)
610             *    PASSWD  : DB接続パスワーãƒ?GE)
611             *    URL     : DB接続JDBCドライバURL(jdbc:oracle:thin:@localhost:1521:HYBS
612             *    TABLE   : 登録するãƒ??ブルID(GE21)
613             *    FILE    : 登録するORACLE XDK 形å¼?XMLファイル(GE21.xml)
614             *    [ENCODE]: ファイルのエンコーãƒ?初期値:UTF-8
615             *    [DRIVER]: JDBCドライバã? 初期値:oracle.jdbc.OracleDriver
616             *
617             * ※ ファイルが存在しなかったå?合ã?FileNotFoundException ã‚?RuntimeException に変換して、throw しますã?
618             * ※ æŒ?®šã?エンコードが存在しなかったå?合ã?UnsupportedEncodingException ã‚?RuntimeException に変換して、throw しますã?
619             *
620             * @og.rev 5.1.1.0 (2009/12/01) MySQL対å¿?明示çš?�«、TRANSACTION_READ_COMMITTED を指定するã?
621             * @og.rev 5.6.7.0 (2013/07/27) DDL?ˆデータ定義è¨?ªž:Data Definition Language?‰ã?処ç�?»¶数追åŠ?
622             *
623             * @param       args    コマンド引数配å?
624             * @throws ClassNotFoundException クラスを見つけることができなかったå?合ã?
625             * @throws SQLException ãƒ??タベã?ス接続エラーが発生したå?合ã?
626             */
627            public static void main( final String[] args )
628                            throws ClassNotFoundException , SQLException {
629                    if( args.length < 5 ) {
630                            LogWriter.log( "Usage: java org.opengion.fukurou.xml.HybsXMLSave USER PASSWD URL TABLE FILE [ENCODE] [DRIVER]" );
631                            LogWriter.log( "   USER  : DB接続ユーザー(GE)" );
632                            LogWriter.log( "   PASSWD: DB接続パスワーãƒ?GE)" );
633                            LogWriter.log( "   URL   : DB接続JDBCドライバURL(jdbc:oracle:thin:@localhost:1521:HYBS)" );
634                            LogWriter.log( "   TABLE : 登録するãƒ??ブルID(GE21)" );
635                            LogWriter.log( "   FILE  : 登録するORACLE XDK 形å¼?XMLファイル(GE21.xml)" );
636                            LogWriter.log( " [ ENCODE: ファイルのエンコーãƒ?初期値:UTF-8 ]" );
637                            LogWriter.log( " [ DRIVER: JDBCドライバã? 初期値:oracle.jdbc.OracleDriver ]" );
638                            return ;
639                    }
640    
641                    String user   = args[0] ;
642                    String passwd = args[1] ;
643                    String url    = args[2] ;
644                    String table  = args[3] ;
645                    String file   = args[4] ;
646                    String encode = ( args.length == 6 ) ? args[5] : "UTF-8"  ;
647                    String driver = ( args.length == 7 ) ? args[6] : "oracle.jdbc.OracleDriver"  ;
648    
649                    Class.forName(driver);
650    
651                    Connection conn = DriverManager.getConnection( url,user,passwd );
652                    Reader reader = null;
653                    int insCnt;
654                    int updCnt;
655                    int delCnt;
656                    int ddlCnt;                     // 5.6.7.0 (2013/07/27) DDL処ç�?»¶数追åŠ?
657                    try {
658                            conn.setAutoCommit( false );
659                            conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);  // 5.1.1.0 (2009/12/01)
660                            HybsXMLSave save = new HybsXMLSave( conn,table );
661    
662                            reader = new BufferedReader(new InputStreamReader(
663                                                                    new FileInputStream( file ) ,encode ));
664                            save.insertXML( reader );
665                            insCnt = save.getInsertCount();
666                            updCnt = save.getUpdateCount();
667                            delCnt = save.getDeleteCount();
668                            ddlCnt = save.getDDLCount();            // 5.6.7.0 (2013/07/27) DDL処ç�?»¶数追åŠ?
669    
670                            Closer.commit( conn );
671                    }
672                    // FileNotFoundException , UnsupportedEncodingException
673                    catch( java.io.FileNotFoundException ex ) {
674                            String errMsg = "ファイルが存在しませんã€? + ex.getMessage()
675                                                            + CR + "Table=[" + table + "] File =[" + file + "]" ;
676                            throw new RuntimeException( errMsg,ex );
677                    }
678                    catch( java.io.UnsupportedEncodingException ex ) {
679                            String errMsg = "æŒ?®šã?エンコードが存在しませんã€? + ex.getMessage()
680                                                            + CR + "Table=[" + table + "] Encode =[" + encode + "]" ;
681                            throw new RuntimeException( errMsg,ex );
682                    }
683                    finally {
684                            Closer.ioClose( reader );
685                            Closer.connClose( conn );
686                    }
687    
688                    System.out.println( "XML File[" + file + "] Into [" + table + "] Table" );
689                    System.out.println( "   Insert Count : [" + insCnt + "]" );
690                    System.out.println( "   Update Count : [" + updCnt + "]" );
691                    System.out.println( "   Delete Count : [" + delCnt + "]" );
692                    System.out.println( "   DDL    Count : [" + ddlCnt + "]" );
693            }
694    }