LoginSignup
0
1

More than 5 years have passed since last update.

Java6でResultSetとBeanのORMappingを自作する。

Last updated at Posted at 2018-07-28

※内容を改変しました。(2018/7/30)

Java6で、ResultSetをbeanにマッピングする処理を自作します。
Frameworkを使えないので、自作してみることにしました。

レスポンスも気になるので、beanにマッピングする場合と、Stringの配列にマッピングする場合との
2つのクラスを作成して、処理速度を計測します。

1.テストするテーブル

30項目くらいの列数をもったテーブルをテストに使います。

sampledatabase.testtable
create table public.testtable (
  col1 character(8)
  , col2 character varying(8)
  , col3 numeric(10, 2)
  , col4 numeric(10, 2)
  , col5 bit(1)
  , col6 integer
  , col7 bigint
  , col9 date
  , col10 timestamp without time zone
  , col21 character(8)
  , col22 character varying(8)
  , col23 numeric(10, 2)
  , col24 numeric(10, 2)
  , col25 bit(1)
  , col26 integer
  , col27 bigint
  , col29 date
  , col30 timestamp without time zone
  , col31 character(8)
  , col32 character varying(8)
  , col33 numeric(10, 2)
  , col34 numeric(10, 2)
  , col35 bit(1)
  , col36 integer
  , col37 bigint
  , col39 date
  , col40 timestamp without time zone
);

2.マッピングする場合

(1)Beanの配列にセットするクラスを作成します

GenericBeanArray.java
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.sql.Date;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class GenericBeanArray {

    /**
     * 項目数
     */
    private int columnCount=0;

    /**
     * 行数
     */
    private long rowCount=0;

    /**
     * ResultSetMetaData.getColumnTypeに対応したjavaのクラス名をmapに格納する。
     */
    @SuppressWarnings("rawtypes")
    private HashMap<Integer, Class> _setterArg=null;

    /**
     * ResultSetのRowsを配列化
     */
    @SuppressWarnings("rawtypes")
    private List list = new ArrayList();

    @SuppressWarnings({ "rawtypes", "unchecked" })
    public GenericBeanArray(ResultSet rs, Class clazz) throws Exception {

        /** * columnTypeと型のmap */
        _setterArg = new HashMap<Integer, Class>();
        _setterArg.put(2, BigDecimal.class);
        _setterArg.put(-7, boolean.class);
        _setterArg.put(4, int.class);
        _setterArg.put(-5, long.class);
        _setterArg.put(91, Date.class);
        _setterArg.put(93, Timestamp.class);
        _setterArg.put(1, String.class);
        _setterArg.put(12, String.class);

        //Beanのクラス名
        String beanClassName=clazz.getName();
        //MetaData
        ResultSetMetaData rsmd = rs.getMetaData();
        this.setColumnCount(rsmd.getColumnCount());
        this.rowCount=0;
        while (rs.next()) {
            //Beanインスタンスを生成
            Class c = Class.forName(beanClassName);
            Object row = (Object)c.newInstance();
            this.rowCount++;
            //全ての列情報を取得して、setterメソッドを動的に実行する。
            for (int i = 1; i <= rsmd.getColumnCount(); i++) {
                //setterメソッド名を取得する。
                String methodName = getMethodName(rsmd.getColumnName(i));
                //setterメソッドの引数
                Class[] classArgs = getMethodArgs(rsmd.getColumnType(i));
                //メソッドオブジェクトを生成
                Method mthod =
                        Class.forName(beanClassName).getMethod(methodName, classArgs);
                //メソッドを実行する。
                mthod.invoke(row, new Object[]{rs.getObject(i)});
            }
            list.add(row);
        }
    }
    /**
     * setterメソッド名のsetXXXの先頭文字列を大文字にします。
     * @param columnName 列名
     * @return 引数の先頭の文字を大文字に変換した文字列
     */
    private String upperFirstChar(String columnName) {
        if (columnName==null || columnName.isEmpty()) {
            return columnName;
        }
        return String.format("%S", columnName.substring(0, 1))
                + columnName.substring(1);
    }
    /**
     * (beanの)setterメソッド名取得
     * @param columnName 項目名
     * @return setterメソッド名
     */
    private String getMethodName(String columnName) {
        return "set" + upperFirstChar(columnName);
    }

    /**
     * setterメソッドの引数を取得する。
     * @param columnType 項目名
     * @return setterメソッド引数オブジェクト
     */
    @SuppressWarnings("rawtypes")
    private Class[] getMethodArgs(int columnType) {
        //型に対応する各クラスはMapから取得する。
        return new Class[]{_setterArg.get(columnType)};
    }
    public int getColumnCount() {
        return columnCount;
    }
    public void setColumnCount(int columnCount) {
        this.columnCount = columnCount;
    }
    public long getRowCount() {
        return rowCount;
    }
    public void setRowCount(long rowCount) {
        this.rowCount = rowCount;
    }
    @SuppressWarnings("rawtypes")
    public HashMap<Integer, Class> get_setterArg() {
        return _setterArg;
    }
    @SuppressWarnings("rawtypes")
    public void set_setterArg(HashMap<Integer, Class> _setterArg) {
        this._setterArg = _setterArg;
    }
    @SuppressWarnings("rawtypes")
    public List getList() {
        return list;
    }
    @SuppressWarnings("rawtypes")
    public void setList(List list) {
        this.list = list;
    }
}

(2)Bean
マッピングするbean

TestTable.java
public class TestTable {

    private String col1;
    private String col2;
    private java.math.BigDecimal  col3;
    private java.math.BigDecimal  col4;
    private boolean col5;
    private int col6;
    private long col7;
    private java.sql.Date col9;
    private java.sql.Timestamp col10;
    private String col21;
    private String col22;
    private java.math.BigDecimal  col23;
    private java.math.BigDecimal  col24;
    private boolean col25;
    private int col26;
    private long col27;
    private java.sql.Date col29;
    private java.sql.Timestamp col30;

    private String col31;
    private String col32;
    private java.math.BigDecimal  col33;
    private java.math.BigDecimal  col34;
    private boolean col35;
    private int col36;
    private long col37;
    private java.sql.Date col39;
    private java.sql.Timestamp col40;

    @Override
    public String toString() {
        return
                this.getCol1() + "," +
                this.getCol2() + "," +
                this.getCol3() + "," +
                this.getCol4() + "," +
                this.isCol5() + "," +
                this.getCol6() + "," +
                this.getCol7() + "," +
                this.getCol9() + "," +
                this.getCol10() + "," +
                this.getCol21() + "," +
                this.getCol22() + "," +
                this.getCol23() + "," +
                this.getCol24() + "," +
                this.isCol25() + "," +
                this.getCol26() + "," +
                this.getCol27() + "," +
                this.getCol29() + "," +
                this.getCol30() + "," +
                this.getCol31() + "," +
                this.getCol32() + "," +
                this.getCol33() + "," +
                this.getCol34() + "," +
                this.isCol35() + "," +
                this.getCol36() + "," +
                this.getCol37() + "," +
                this.getCol39();
    }
    //setter / getterは省略
}

3.Stringの配列にマッピングする場合

(1)Stringの配列にマッピングするクラス

QuerySet.java
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * データを管理するクラス
 * @author ycnp4
 *
 */
public class QuerySet {

    /**
     * 項目数
     */
    private int columnCount=0;

    /**
     * 行数
     */
    private long rowCount=0;

    /*** ResultSetMetaDataの項目名を管理する。
     * key:列番号
     * val:getColumnTypeName
     * */
    private HashMap<Integer, String> columnNameMap
        =new HashMap<Integer, String>();

    /*** ResultSetMetaDataの各項目の型名を管理する。
     * key:列番号
     * val:getColumnTypeName
     * */
    private HashMap<Integer, String> columnTypenameMap
        =new HashMap<Integer, String>();

    /**
     * 検索結果のレコードの配列
     */
    private List<String[]> rows=new ArrayList<String[]>();

    /**
     * コンストラクタ
     * @param rs ResultSetオブジェクト
     * @throws Exception 例外
     */
    public QuerySet(ResultSet rs) throws Exception {
        //metadataを取得する。
        ResultSetMetaData meta = rs.getMetaData();
        this.columnCount=meta.getColumnCount();
        for (int i = 1; i <= meta.getColumnCount(); i++) {
            columnNameMap.put(i, meta.getColumnName(i));
            columnTypenameMap.put(i, meta.getColumnTypeName(i));
        }
        this.rowCount=0;
        while (rs.next()) {
            this.rowCount++;
            List<String> rowdata=new ArrayList<String>();
            for (int i = 1; i <= meta.getColumnCount(); i++) {
                rowdata.add(rs.getString(i));
            }
            this.addRow(rowdata.toArray(new String[rowdata.size()]));
        }
    }
    /**
     * 行追加
     * @param row
     */
    public void addRow(String[] row) {
        this.rows.add(row);
    }
}
    //setter / getterは省略

(2)テスト用に、Stringの配列を結合して文字列にするクラス

CommonUtils.java
public class CommonUtils {
    public static String join(String delimiter, Object... strarr){   //可変長引数
        String rv = "";
        for(Object sa: strarr){  //拡張for
                 rv += sa + delimiter;
        }
        return rv.substring(0, rv.length() - delimiter.length());   //String切り出し
    }
}

4.DB接続クラス

両方のオブジェクトを返すメソッドを実装しています。

DBUtils.java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class DBUtils {

    private Connection _connection = null;

    /**
     * コンストラクタ(仮)
     * @throws SQLException
     */
    public DBUtils() throws SQLException {
        _connection = DriverManager.getConnection(
                "jdbc:postgresql://localhost:5432/sampledatabase",
                "testuser",
                "XXXXXXXXXXXXXXXX");
    }
    /**
     * クエリを実行してをBeanのListに詰めて返します。
     * @param query クエリ(SQL)
     * @param clazz Beanのクラス
     * @return BeanのList
     * @throws Exception 例外はすべてスロー
     */
    @SuppressWarnings("rawtypes")
    public GenericBeanArray query(String query, Class clazz) throws Exception {

        PreparedStatement stmt = null;
        ResultSet rs = null;

        try {
            //PreparedStatementを生成
            stmt = _connection.prepareStatement(query);
            //ResultSetを取得する。
            rs = stmt.executeQuery();
            return new GenericBeanArray(rs, clazz);
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        } finally {
            close(rs);
            close(stmt);
        }
    }

    /**
     * クエリを実行して、QuerySetオブジェクトを生成します。
     * @param query クエリ
     * @return QuerySetオブジェクト
     * @throws Exception 例外
     */
    public QuerySet getQuerySet(String query) throws Exception {

        PreparedStatement stmt = null;
        ResultSet rs = null;

        try {
            //PreparedStatementを生成
            stmt = _connection.prepareStatement(query);
            //ResultSetを取得する。
            rs = stmt.executeQuery();
            //QuerySetオブジェクトを生成します
            return new QuerySet(rs);
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        } finally {
            close(rs);
            close(stmt);
        }
    }

    /**
     * ResultSetクローズ
     * @param ps PreparedStatement
     */
    private void close(PreparedStatement ps) {
        try {
            if (ps != null && !ps.isClosed()) {
                ps.close();
                System.out.println("PreparedStatement closed.");
            }
        } catch (SQLException e) {
            //どうしようもないので何もしなくていい。
            e.printStackTrace();
        }
    }
    /**
     * ResultSetクローズ
     * @param rs ResultSet
     */
    private void close(ResultSet rs) {
        try {
            if (rs != null && !rs.isClosed()) {
                rs.close();
                System.out.println("ResultSet closed.");
            }
        } catch (SQLException e) {
            //どうしようもないので何もしなくていい。
            e.printStackTrace();
        }
    }
    /**
     * ファイナライザ
     */
    @Override
    protected void finalize() throws Throwable {
        try {
            super.finalize();
        } finally {
            closeConnection();
        }
    }
    /**
     * ファイナライザ
     * コネクションをクローズする。
     */
    public void closeConnection() {
        try {
            _connection.close();
            System.out.println("connection closed.");
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

5.実行

sampleORMapper.java
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.util.List;

public class TestOrMapper {

    @SuppressWarnings("unchecked")
    public static void main(String[] args) {

        FileWriter file = null;
        PrintWriter pw = null;
        FileWriter file2 = null;
        PrintWriter pw2 = null;
        DBUtils db;
        try {

            // FileWriterクラスのオブジェクトを生成する
            file = new FileWriter("C:\\work\\sampleORMapper\\bean.txt");
            // PrintWriterクラスのオブジェクトを生成する
            pw = new PrintWriter(new BufferedWriter(file));

            // FileWriterクラスのオブジェクトを生成する
            file2 = new FileWriter("C:\\work\\sampleORMapper\\queryset.txt");
            // PrintWriterクラスのオブジェクトを生成する
            pw2 = new PrintWriter(new BufferedWriter(file2));

            db = new DBUtils();

            long start = System.currentTimeMillis();

            GenericBeanArray beanarray =
                db.query("select * from testtable", TestTable.class);

            int i=0;

            long end = System.currentTimeMillis();

            long start2 = System.currentTimeMillis();

            QuerySet queryset =
                db.getQuerySet("select * from testtable");

            long end2 = System.currentTimeMillis();

            List<TestTable> list = beanarray.getList();
            for (TestTable row : list) {
                pw.println(row.toString());
            }

            for (String[] row : queryset.getRows()) {
                pw2.println(CommonUtils.join(",", row));
            }
            System.out.println(
                    "case bean 列数=" + beanarray.getColumnCount() +"件数=" + beanarray.getRowCount() + " 処理時間=" + (end - start)  + "ms");
            System.out.println(
                    "case queryset 列数=" + queryset.getColumnCount() + " 件数=" + queryset.getRowCount() + " 処理時間=" + (end2 - start2)  + "ms");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //ファイルを閉じる
            pw.close();
            pw2.close();
        }
    }
}

6.結果

case bean 列数=27件数=1500 処理時間=939ms
case queryset 列数=27 件数=1500 処理時間=26ms

beanにmappingすると、String[]に比較して、かなり時間がかかってしまってますね。
beanが必要ない限り、String[]などで行を処理するほうがいいかな・・。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1