JDBCを使ったテキストファイル→テーブルのローダ

 タブ区切りのテキストファイルを読み、1行を1レコードと扱って、 INSERT文で指定したテーブルに行をロードするユーティリティです。

/**
 * テキストファイルのロード中にエラーが発生した行について、
 * エラー行番号とエラー内容を記憶します。
 * @see Loader
 */
public class ErrorLine {
  int lineno;
  String errorMessage;

  /**
   * コンストラクタ
   * @param lineno 行番号(先頭が1)
   * @param errorMessage エラーメッセージ。典型的には、SQLExceptionの文字列。
   */
  public ErrorLine(int lineno, String errorMessage){
    this.lineno = lineno;
    this.errorMessage = errorMessage;
  }

  public final int getLineno(){ return lineno; }
  public final String getErrorMessage(){ return errorMessage; }
}
/* end. */

import java.sql.*;
import java.io.*;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.Iterator;

/**
 * 指定したテキストファイル、SQL文(INSERT文)でテーブルにデータを
 * ロードします。
 * 2003/03/20
 * @see ErrorLine
 */
public class Loader {
  String insertSQL;
  String fileName;
  String user     = "urano";
  String password = "urano398";
  String database = "udb";

  String separator = "\t"; /* テキストファイルのフィールド区切り子 */
  int maxErrorCount = 50; /* これよりエラー行数が多いと処理を中止 */
  Vector errorLines; /* エラーを起こした行の情報を格納するベクター */
  int totalLineCount; /* 読んだ総行数 */

  Exception exception;

  public Loader(String insertSQL, String fileName){
    this.insertSQL = insertSQL;
    this.fileName = fileName;
  }

  public final Exception getException(){
    return exception;
  }

  /**
   * テーブルに行をロードします。<br>
   * エラーというのは、続行不可能なエラーを指します。
   * 例えば、「データベースに接続できない」「ファイルが開けない」
   * 「I/Oエラー」などです。
   * テキストファイルの特定の行がINSERT文発行でエラーになった場合は、
   * 処理としては正常終了になります。エラー行があったかどうかは、
   * getErrorLineCount()で調べ、その情報はgetErrorLines()で
   * 取得するようにします。
   *
   * @return 正常終了なら真、エラーなら偽
   */
  public boolean load(){
    exception = null;
    errorLines = new Vector();
    totalLineCount = 0;

    /* JDBCドライバのロード */
    try {
      Class.forName("org.gjt.mm.mysql.Driver");
    } catch(ClassNotFoundException ex){
      this.exception = ex;
      return false;
    }

    /* テキストファイルのオープン */
    BufferedReader reader;
    try {
      reader = new BufferedReader(
        new InputStreamReader(new FileInputStream(fileName), "SJIS") );
    } catch(IOException ex){
      this.exception = ex;
      return false;
    }

    /* データベースへ接続 */
    Connection conn;
    PreparedStatement ps;
    try {
      conn =
        DriverManager.getConnection ("jdbc:mysql://localhost:3306/" +
             database + "?useUnicode=true&characterEncoding=SJIS",
             user, password);
      conn.setAutoCommit(false);
      ps = conn.prepareStatement(insertSQL);
    } catch(SQLException ex){
      this.exception = ex;
      return false;
    }

    /* 1行ずつ読みながらロード */
    boolean result = true;
    int lineno = 0;
    int errorCount = 0;
    try {
      while(true){
        String buf = reader.readLine();
        if(buf == null) break;
        lineno++;
        StringTokenizer stok = new StringTokenizer(buf.trim(), separator);
        try {
          for(int index = 1; stok.hasMoreTokens(); index++){
            String token = stok.nextToken();
            /* 先頭と最後が"なら、先頭と最後の"を削除します。*/
            if(token.charAt(0) == '"' &&
               token.endsWith("\"")){
              token = token.substring(1, token.length()-1);
              //System.out.println("削ります:" + token);
            }
            ps.setString(index, token);
          }
          int rows = ps.executeUpdate();
          if(rows == 0) throw new SQLException("更新行数が0です。");
        } catch(SQLException ex){
          //System.out.println("行 " + String.valueOf(lineno) +
          //  " でエラーが発生しました:" + ex.toString());
          errorLines.addElement(new ErrorLine(lineno, ex.toString()));
          errorCount++;
          if(errorCount > maxErrorCount){
            throw new RuntimeException("エラー行数が制限を越えたので、処理を中止しました。");
          }
        }
      }
    } catch(Exception ex){
      /* ここでエラーを起こしたらロールバックします。*/
      try{ conn.rollback(); } catch(SQLException ex2){ }
      this.exception = ex;
      result = false;
    }

    try{ reader.close(); } catch(Exception ex){ }
    try{ ps.close(); } catch(Exception ex){ }
    try{ conn.close(); } catch(Exception ex){ }
    //System.out.println("読んだ行数=" + String.valueOf(lineno));
    //System.out.println("エラー行数=" + String.valueOf(errorCount));
    totalLineCount = lineno;
    return result;
  }

  /**
   * load()を実行した後に呼ばれ、読み込んだ行の数
   * (ロードできた行数+エラー行数)を返します。
   */
  public final int getTotalLineCount(){
    return totalLineCount;
  }

  /**
   * load()を実行した後に呼ばれ、エラーを起こした行の数を返します。
   */
  public final int getErrorLineCount(){
    return (errorLines == null) ? 0 : errorLines.size();
  }

  /**
   * load()を実行した後に呼ばれ、エラーを起こした行の情報を格納した
   * ErrorLineオブジェクトのベクターを返します。
   */
  public final Vector getErrorLines(){
    return errorLines;
  }

  public static void main(String[] args) {
    Loader loader = new Loader("INSERT INTO SHITEN VALUES (?, ?, ?, ?, ?)",
      "shiten-add.dat");
    boolean result = loader.load();

    System.out.println("処理行数=" + loader.getTotalLineCount());
    System.out.println("エラー行数=" + loader.getErrorLineCount());
    if(loader.getErrorLineCount() > 0){
      System.out.println("エラー情報:");
      Iterator iter = loader.getErrorLines().iterator();
      while(iter.hasNext()){
        ErrorLine el = (ErrorLine) iter.next();
        System.out.println("[" + el.getLineno() + "]:" + el.getErrorMessage());
      }
    }
    if(result){
      System.out.println("正常終了");
    }
    else{
      Exception ex = loader.getException();
      System.out.println("おおっと!エラーが発生しました。:" + ex.toString());
      ex.printStackTrace();
    }
  }
}
// end.

JDBC and Swing Top

(first uploaded 2003/03/22 last updated (not ever), URANO398)

楽天モバイル[UNLIMITが今なら1円] ECナビでポインと Yahoo 楽天 LINEがデータ消費ゼロで月額500円〜!


無料ホームページ 無料のクレジットカード 海外格安航空券 解約手数料0円【あしたでんき】 海外旅行保険が無料! 海外ホテル