Ch11. JDBC

JDBCのクラス

    Connection con = null;
    Statement stmt = null;
    ResultSet rs = null;

      String url = "jdbc:mysql://localhost/golddb";

      con = DriverManager.getConnection(url,"root", "*****");

      stmt = con.createStatement();

      String sql = "SELECT dept_code, dept_name FROM department";
      rs = stmt.executeQuery(sql);

      while (rs.next()) {
        System.out.println("dept_code : " + rs.getInt(1));
        System.out.println("dept_name : " + rs.getString("dept_name"));
      }

JDBCでのDBコネクションでは3つのクラスが登場する。

クラス 取得方法 説明
Connection DriverManager.getConnection DBとのコネクションを補完する
Statement connectionObj.createStatement() statementObj.executeQuery("SQL") で利用する
ResultSet statementObj.executeQuery("SQL") で取得 next()でレコードを選択、getString(1), getInt(カラム名)でカラム選択

カラムごとの取得方法

取得するデータ型ごとに異なるメソッドを利用する。 また、ResultSetで取得できる日付・時刻データ型はjava.timeのそれと異なるため、クラスキャストが必要になる。

        java.sql.Date sqlDate = rs.getDate(1);
        java.time.LocalDate localDate= sqlDate.toLocalDate();
        System.out.println("localDate : " + localDate + " sqlDate : " + sqlDate);

        java.sql.Time sqlTime = rs.getTime(2);
        java.time.LocalTime localTime= sqlTime.toLocalTime();
        System.out.println("localTime : " + localTime + " sqlTime : " + sqlTime);

//localDate : 2020-11-12 sqlDate : 2020-11-12
//localTime : 12:05:01 sqlTime : 12:05:01

通常のデータだけでなく、SQLのエラーコード等も取得できる。

 catch (SQLException e) {

        System.out.println(e.getErrorCode());//'1146'
        System.out.println(e.getSQLState());//'42S02'
    } 

executeQuery / executeUpdate

executeQueryで取得した値はresultSetというオブジェクトで返却される。executeQueryの結果が0件だったとしても空っぽのresultSetが返却されるのでnullが返却されることは無い(Optionalみたいな仕組み)。

      String sql = "SELECT field1, field2, filed3 FROM mytableA WHERE field1 = 100";
      rs = stmt.executeQuery(sql);
      System.out.println(rs == null); //false

レコードの挿入処理・削除処理であってもexecuteUpdateを利用する。

executeUpdateの戻り値は「データベースへの変更が発生したかどうか」が0または1のint型で返却される。【訂正】変更が発生した行数が返却される。

    String sql = "UPDATE department set " +
                 "dept_address='Tokyo', pilot_number='03-6666-xxxx' "+
                 "where dept_code = 2" ;

    String sql2 = "UPDATE department set " +
            "dept_address='Melbourne', pilot_number='03-6666-xxxx' "+
            "where dept_code = 1" ;
    try(Connection con = DbConnector.getConnect();
        Statement stmt = con.createStatement()

            ) {
      int col = stmt.executeUpdate(sql);
      int col2 = stmt.executeUpdate(sql2);

      System.out.println("更新対象無し : " + col + "更新対象アリ : " + col2);
      //'更新対象無し : 0更新対象アリ : 1'

一方、例えば主キー制約で更新に失敗したりした場合は、executeUpdateは返却されず、例外となる。

    String sql = "INSERT INTO department VALUES " +
                 "(6,'Finance','Sydney', '112233')";
    try(Connection con = DbConnector.getConnect();
        Statement stmt = con.createStatement()) {
      int col = stmt.executeUpdate(sql);
      System.out.println("col : " + col);
    } catch (SQLException e) {
      System.out.println(e.getMessage()); //'Duplicate entry '6' for key 'department.PRIMARY''
    }

execute

executeStatementは戻り値としてResultSetが、executeupdateは戻り値としてintが返却されていた。 updateやselect両方で利用できるexecuteクラスは戻り値にbooleanを返す。

    String sql2 = "UPDATE department set " +
            "dept_address='Sydney', pilot_number='03-6456-xxxx' "+
            "where dept_code = 1" ;
    try(Connection con = DbConnector.getConnect();
        Statement stmt = con.createStatement()

            ) {
      boolean col = stmt.execute(sql2);
      ResultSet result = stmt.getResultSet();
      System.out.println(result);


      System.out.println( "更新対象アリ : " + col);
      //更新対象アリ : false'
    }

executetrueを返却するのはResultSetに値が挿入されたときだけ。 たとえば、更新に成功してもResultSetに値は入らないのでfalseが返却される。

逆にSELECT文で値一件も取得できなくとも、ResultSetには空のオブジェクトが挿入されるため、trueとなる。

    String sql2 = "SELECT * FROM golddb.department WHERE dept_code = 3" ;
    try(Connection con = DbConnector.getConnect();
        Statement stmt = con.createStatement()

            ) {
      boolean col = stmt.execute(sql2);
      ResultSet result = stmt.getResultSet();
      System.out.println(result);

      while(result.next()) {
          result.getString("dept_name");
      }


      System.out.println( "更新対象アリ : " + col);
      //更新対象アリ : false'
    } catch (SQLException e) {
      System.out.println(e.getMessage());
    }

ResultSetの拡張

ここまではResultSetのメソッドに対してnextしか利用していなかったが、他にも様々なメソッドを利用できる。 ただし、ResultSet間のカーソルを扱えるのはTYPE_SCROLL_INSENSETIVEをサポートしている場合のみで、ドライバの種類によっては利用できない。

クラス 取得方法 説明
previous nextの反対
beforeFirst() 取得行の最初の行の前に移動
afterLast() 取得行の最後の行の後ろに移動
absolute(int x) 1で最初の行、-1で最後の行に移動できる

ResultSetで得られた結果を通して、そのままレコードの更新を実施することが出来る。 レコードの挿入も行えるが、それには専用の(Insert)行まで移動する必要がある。

    String sql2 = "SELECT * FROM golddb.department" ;
    try(
        Connection con = DbConnector.getConnect();
        Statement stmt = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE)
    ) {
        boolean col = stmt.execute(sql2);
        ResultSet result = stmt.getResultSet();
        System.out.println(result);

        while(result.next()) {
            result.getString("dept_name");
        }

        result.absolute(-1);
        result.updateString(2, "HR");

        result.updateRow();//Commit;
        result.moveToInsertRow();

        result.updateInt(1,4);
        result.updateString(2, "Reserch");
        result.updateString(3, "Yokohama");
        result.updateString(4, "03-6666-XXXX");

        result.insertRow();//Commit

        result.beforeFirst();
        while(result.next()) {
            result.getString("dept_name");
        }

      System.out.println( "更新対象アリ : " + col);
      //更新対象アリ : true'
    } catch (SQLException e) {
      System.out.println(e.getMessage());
    }

ResultSetは一度更新がかかってしまうと、閉じられてしまいnextで開くことが出来なくなっていまう。

ResultSet (Java Platform SE 8)