DbUtils(二) 結果集實例

      單行數據處理:ScalarHandler    ArrayHandler    MapHandler    BeanHandlerhtml

      多行數據處理:BeanListHandler    AbstractListHandlerArrayListHandler MapListHandler ColumnListHandlerjava

                          AbstractKeyedHandlerKeyedHandler BeanMapHandlersql

      可供擴展的類:BaseResultSetHandler數據庫

      Dbutils使用結果集的方法有query、insert、insertBatch三個。這些方法都在QueryRunner類中,須要注意的是insert和update方法都能執行 「insert」開頭的sql語句,可是返回值有區別。insert 執行後返回的是表中的插入行生成的主鍵值,update 返回的是受語句影響的行數。因此,若是目標表中有主鍵且須要返回插入行的主鍵值就用 insert 方法,若是表沒有主鍵或者不須要返回主鍵值可以使用 update 方法。編程

      先創建測試用數據表[users]:數組

id userName loginName userPassword userLevel userLock
1 測試用戶1 test1 jiseflwes 10 0
2 知道什麼 hello 2556sefsfs 10 1
3 編程就編程 cjava sfsfsef254sefs 2 0

      字段類型,id 爲主鍵:ide

	[id] [int] IDENTITY(1,1) NOT NULL,
	[userName] [nchar](20) NOT NULL,
	[loginName] [nchar](20) NOT NULL,
	[userPassword] [nchar](100) NOT NULL,
	[userLevel] [int] NOT NULL,
	[userLock] [bit] NOT NULL,

一、ScalarHandler<T>     測試

      用於獲取結果集中第一行某列的數據並轉換成 T 表示的實際對象。this

      該類對結果集的處理直接在 handle 方法中進行,不涉及 dbutils 庫的其餘類。spa

String sql = "select * from users";
// ---- query 語句 ----
// ScalarHandler 的參數爲空或null時,返回第一行第一列的數據
int rs = runner.query(sql, new ScalarHandler<Integer>());
System.out.println("ScalarHandler: " + rs); // Print:[ScalarHandler: 1]

// ScalarHandler 的參數能夠是列的索引(從1開始)或列名
String rs = runner.query(sql, new ScalarHandler<String>(2));
// 或者 String rs = runner.query(sql, new ScalarHandler<String>("userName"));
System.out.println("ScalarHandler: " + rs); // Print:[ScalarHandler: 測試用戶1]

// ---- insert 語句 ----
// 由於我使用的是mssql數據庫,QueryRunner的insert獲取插入數據的主鍵其實調用的是select SCOPE_IDENTITY()
// 數據庫執行後返回的類型是numeric,映射到 java 類型就是 java.math.BigDecimal
String inSql = "insert users (userName, loginName, userPassword, userLevel, userLock) values (?, ?, ?, ?, ?)";
BigDecimal insertRs = runner.insert(inSql,new ScalarHandler(),  "java程序編寫", "javahello", "sefsfsfwew", "15", false);
System.out.println("ScalarHandler: " + insertRs); // Print:[ScalarHandler: 4]

      使用的時候必定要保證提供正確的列索引或列名,而且結果類型也要正確可轉換。

二、ArrayHandler

      用於獲取結果集中的第一行數據,並將其封裝到一個數組中,一列值對應一個數組元素。

      handle 源碼:

public Object[] handle(ResultSet rs) throws SQLException {
    // convert = new BasicRowProcessor()
    // 若是有數據,將調用 BasicRowProcessor 的 toArray(rs) 方法處理
    return rs.next() ? this.convert.toArray(rs) : EMPTY_ARRAY;
}
// ---- query 語句 ----
String sql = "select * from users";
Object[] rs = runner.query(sql, new ArrayHandler());
// Print: ArrayHandler: [1, 測試用戶1, test1, jiseflwes, 10, false]
System.out.println("ArrayHandler: " + Arrays.toString(rs));

// ---- insert 語句 ----
String inSql = "insert users_t (userName, loginName, userPassword, userLevel, userLock) values (?, ?, ?, ?, ?)";
Object[] insertRs = runner.insert(inSql,new ArrayHandler(),  "java程序編寫", "javahello", "sefsfsfwew", "15", false);
// Print: ArrayHandler: [5]
System.out.println("ArrayHandler: " + Arrays.toString(insertRs));

三、MapHandler

      用於獲取結果集中的第一行數據,並將其封裝到一個Map中,Map 中 key 是數據的列別名(as label),若是沒有就是列的實際名稱,Map 中 value 就是列的值,注意表明列的 key 不區分大小寫。

      handle 源碼:

public Map<String, Object> handle(ResultSet rs) throws SQLException {
    // convert = new BasicRowProcessor()
    // 若是有數據,將調用 BasicRowProcessor 的 toMap(rs) 方法處理
    return rs.next() ? this.convert.toMap(rs) : null;
}

      經過查看 BasicRowProcessor 代碼,能夠知道封裝結果集的 Map 實際上是一個 LinkedHashMap 對象。

// ---- query 語句 ----
String sql = "select userName, loginName, userPassword as password, userLevel, userLock from users";
Map<String, Object> rs = runner.query(sql, new MapHandler());
// Print: MapHandler: {userName=測試用戶1, loginName=test1, password=jiseflwes, userLevel=10, userLock=false}
System.out.println("MapHandler: " + rs);
// 列名小寫 Print: username: 測試用戶1
System.out.println("username: " + rs.get("username"));
// 列名大寫 Print: USERNAME: 測試用戶1
System.out.println("USERNAME: " + rs.get("USERNAME"));
// 使用了as指定別名,那麼取數據的時候必定要用別名,不然返回null。
// Print: userPassword as password: jiseflwes
System.out.println("userPassword as password: " + rs.get("password"));
// Print: userPassword as password: null
System.out.println("userPassword as password: " + rs.get("userPassword"));

// ---- insert 語句 ----
String inSql = "insert users (userName, loginName, userPassword, userLevel, userLock) values (?, ?, ?, ?, ?)";
Map<String, Object> insertRs = runner.insert(inSql,new MapHandler(),  "java程序編寫", "javahello", "sefsfsfwew", "15", false);
// 注意這個鍵(key)是由數據庫驅動定義的。
// Print: MapHandler:{GENERATED_KEYS=6}
System.out.println("MapHandler: " + insertRs);
// 我用的是微軟提供的驅動,因此是GENERATED_KEYS,若是是其餘驅動就又不一樣了(好比jtds驅動key是ID)
// jtds驅動使用 insertRs.get("ID")
// Print: MapHandler: 6
System.out.println("MapHandler: " + insertRs.get("GENERATED_KEYS"));

四、BeanHandler<T>

      用於獲取結果集中的第一行數據,並將其封裝到JavaBean對象。

      整個轉換過程最終會在 BeanProcessor 類中完成。

/**
 * Users類
 */
public class Users {
    private int id;
    private String userName;
    private String loginName;
    private String userPassword;
    private int userLevel;
    private boolean userLock;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    // ... 略過其餘 getter 和 setter 方法

    @Override
    public String toString() {
        return "Users{" +
                "id=" + id +
                ", userName='" + userName + '\'' +
                ", loginName='" + loginName + '\'' +
                ", userPassword='" + userPassword + '\'' +
                ", userLevel=" + userLevel +
                ", userLock=" + userLock +
                '}';
    }
}

      執行代碼:

//---- query 語句 ----
String sql = "select * from users";
Users rs = runner.query(sql, new BeanHandler<Users>(Users.class));
// Print: BeanHandler: Users{id=1, userName='測試用戶1', loginName='test1', userPassword='jiseflwes', userLevel=10, userLock=true}
System.out.println("BeanHandler: " + rs);
// Print: BeanHandler: test1
System.out.println("BeanHandler: " + rs.getLoginName());

      須要注意的是,默認的狀況下要保證表的字段和javabean的屬性一致(字符一致便可,對大小寫不敏感),好比字段是userLock,那麼javabean中屬性必須是userLock這幾個字母(能夠是userlock,userLock,userLOCK,不過仍是建議按照規範來定義)。

      但有個問題,數據表中的字段可能已經定下來了,並且名稱可能不太規範,好比用下劃線(login_name),或者加了一個類型前綴(chrLoginName),若是修改表字段,那麼涉及到的修改地方太多了,其實查看源碼能夠知道BeanHandler有兩個構造方法:

    // BeanHandler 構造方法
    public BeanHandler(Class<T> type) {
        this(type, ArrayHandler.ROW_PROCESSOR);
    }
    public BeanHandler(Class<T> type, RowProcessor convert) {
        this.type = type;
        this.convert = convert;
    }

      能夠看出其實都是調用的第二個方法。

runner.query(sql, new BeanHandler<Users>(Users.class));
// 等價於
runner.query(sql, new BeanHandler<Users>(Users.class, new BasicRowProcessor()));
// 等價於
runner.query(sql, new BeanHandler<Users>(Users.class, new BasicRowProcessor(new BeanProcessor())));
// 因此關鍵的地方在 new BeanProcessor() 這個具體處理結果的對象

      狀況一:只涉及到下劃線,表字段名用下劃線間隔(如表users_t字段:[id],[user_name],[login_name],[user_password],[user_level],[user_lock]),如今要封裝到Javabean中Users類(代碼見上),其中屬性使用駝峯命名。能夠用dbutils1.6提供的BeanProcessor類的子類GenerousBeanProcessor。

String sql = "select id,user_name,login_name,user_password,user_level,user_lock from users_t"; 
// 建立一個BeanProcessor對象
// GenerousBeanProcessor 僅僅重寫了父類BeanProcessor的mapColumnsToProperties方法
BeanProcessor bean = new GenerousBeanProcessor();
// 將GenerousBeanProcessor對象傳遞給BasicRowProcessor
RowProcessor processor = new BasicRowProcessor(bean);
// 最後使用GenerousBeanProcessor的mapColumnsToProperties處理表字段到javabean的屬性映射
Users rs = runner.query(sql, new BeanHandler<Users>(Users.class, processor));
// Print: BeanHandler: Users{id=1, userName='測試用戶1', loginName='test1', userPassword='jiseflwes', userLevel=10, userLock=true}
System.out.println("BeanHandler: " + rs);

      狀況二:徹底改變表字段到Javabean屬性的映射(如表users_m字段:[yhmid],[charUsername],[charLoginName],[charPassword],[intLevel],[boolLock])映射到Users類(代碼同上):

// BeanProcessor 有兩個構造方法,能夠傳入一個HashMap集合
// HashMap 規定了表字段映射到Javabean的哪一個屬性,即key爲字段名稱,value爲對應的javabean屬性
// map.put(表字段名稱, Javabean屬性名稱)
Map<String, String> map = new HashMap<String, String>();
map.put("yhmid", "id");
map.put("charUsername", "userName");
map.put("charLoginName", "loginName");
map.put("charPassword", "userPassword");
map.put("intLevel", "userLevel");
map.put("boolLock", "userLock");
// 用構建好的HashMap創建一個BeanProcessor對象
BeanProcessor bean = new BeanProcessor(map);
RowProcessor processor = new BasicRowProcessor(bean);
Users rs = runner.query(sql, new BeanHandler<Users>(Users.class, processor));
// Print: BeanHandler: Users{id=1, userName='測試用戶1', loginName='test1', userPassword='jiseflwes', userLevel=10, userLock=true}
System.out.println("BeanHandler: " + rs);

五、BeanListHandler<T>

      用於將結果集的每一行數據轉換爲Javabean,再將這個Javabean添加到ArrayList中。能夠簡單的看着是BeanHandler的高級版,只不過是多了一步,就是將生成的Javabean添加到ArrayList中,其餘的處理都和BeanHandler同樣。

String sql = "select * from users";
List<Users> rs = runner.query(sql, new BeanListHandler<Users>(Users.class));
// Print: BeanListHandler: [
// Users{id=1, userName='測試用戶1', loginName='test1', userPassword='jiseflwes', userLevel=10, userLock=true},
// Users{id=1, userName='知道什麼', loginName='hello', userPassword='2556sefsfs', userLevel=10, userLock=true},
// Users{id=1, userName='編程就編程', loginName='cjava', userPassword='sfsfsef254sefs', userLevel=2, userLock=false}]
System.out.println("BeanListHandler: " + rs);

六、AbstractListHandler<T>

    // AbstractListHandler 類實現了handle方法
    @Override
    public List<T> handle(ResultSet rs) throws SQLException {
        List<T> rows = new ArrayList<T>();
        while (rs.next()) {
            rows.add(this.handleRow(rs)); // 每一個子類實現本身的handleRow方法
        }
        return rows;
    }

      AbstractListHandler抽象類已經實現handle方法,該方法其實只是起到一個包裝做用,將處理好的每行數據添加到ArrayList中。每行的數據處理經過調用handleRow方法實現,全部它的3個子類都必須實現這個方法。

6.1 ArrayListHandler (extends AbstractListHandler<Object[]>)

      用於將結果集每行數據轉換爲Object數組(處理過程等同與ArrayHandler),再將該數組添加到ArrayList中。簡單點,就是將每行數據通過ArrayHandler處理後添加到ArrayList中。

String sql = "select * from users";
List rs = runner.query(sql, new ArrayListHandler());
// Print:
// [1, 測試用戶1, test1, jiseflwes, 10, true]
// [2, 知道什麼, hello, 2556sefsfs, 10, true]
// [3, 編程就編程, cjava, sfsfsef254sefs, 2, false]
for(Object user : rs) {
    System.out.println(Arrays.toString((Object[])user));
}

6.2 MapListHandler (extends AbstractListHandler<Map<String, Object>>)

      用於將結果集每行數據轉換爲Map(處理過程等同與MapHandler),再將Map添加到ArrayList中。簡單點,就是將每行數據通過MapHandler處理後添加到ArrayList中。

String sql = "select * from users";
List rs = runner.query(sql, new MapListHandler());
// Print:
// {1, 測試用戶1, test1, jiseflwes, 10, true}
// {2, 知道什麼, hello, 2556sefsfs, 10, true}
// {3, 編程就編程, cjava, sfsfsef254sefs, 2, false}
for(Object user : rs) {
    System.out.println((Map<String, Object>)user);
}

6.3 ColumnListHandler<T> (extends AbstractListHandler<T>)

      根據列索引或列名獲取結果集中某列的全部數據,並添加到ArrayList中。能夠理解爲ScalarHandler<T>的增強版。

String sql = "select * from users";
List<String> rs = runner.query(sql, new ColumnListHandler<String>(2));
// 等同 List<String> rs = runner.query(sql, new ColumnListHandler<String>("userName"));
// Print:
// 測試用戶1
// 知道什麼
// 編程就編程
for(String user : rs) {
    System.out.println(user);
}

七、AbstractKeyedHandler<K, V>

     AbstractKeyedHandler是一個抽象類,已經實現了handle方法,其子類必須實現createKey(ResultSet rs)和createRow(ResultSet rs)方法,以便handle()的調用。

   /**
     * 返回一個HashMap<K, V>
     * createKey(rs) 將某列的值做爲HashMap的key
     * createRow(rs) 將結果集轉換後做爲HashMap的value
     */
    @Override
    public Map<K, V> handle(ResultSet rs) throws SQLException {
        Map<K, V> result = createMap();
        while (rs.next()) {
            result.put(createKey(rs), createRow(rs)); // 須要子類本身實現
        }
        return result;
    }

7.1 KeyedHandler<K> (extends AbstractKeyedHandler<K, Map<String, Object>>)

      用於獲取全部結果集,將每行結果集轉換爲Map<String, Object>,並指定某列爲key。能夠簡單的認爲是一個雙層Map,至關於先對每行數據執行MapHandler,再爲其指定key添加到一個HaspMap中。KeyedHandler<K> 中的<K>是指定的列值的類型。

String sql = "select * from users";
// 在這兒指定主鍵id爲結果key,也能夠傳入列名 new KeyedHandler<Integer>("id")
Map<Integer, Map<String, Object>> rs = runner.query(sql, new KeyedHandler<Integer>(1));
// Print: KeyedHandler: {
//        1={id=1, userName=測試用戶1, loginName=test1, userPassword=jiseflwes, userLevel=10, userLock=true},
//        2={id=2, userName=知道什麼, loginName=hello, userPassword=2556sefsfs, userLevel=10, userLock=true},
//        3={id=3, userName=編程就編程, loginName=cjava, userPassword=sfsfsef254sefs, userLevel=2, userLock=false}}
System.out.println("KeyedHandler: " + rs);

// 也能夠指定其餘列做爲key,可是須要注意若是指定的列值存在重複值,那麼後面的值將覆蓋前面的,最終HashMap中key都是惟一的。
// 如指定列userLevel爲key,最終只有兩個結果,由於前兩行userLevel值都是10。
Map<Integer, Map<String, Object>> rs = runner.query(sql, new KeyedHandler<Integer>("userLevel"));
// Print: KeyedHandler: {
//        2={id=3, userName=編程就編程, loginName=cjava, userPassword=sfsfsef254sefs, userLevel=2, userLock=false},
//        10={id=2, userName=知道什麼, loginName=hello, userPassword=2556sefsfs, userLevel=10, userLock=true}}
System.out.println("KeyedHandler: " + rs);

7.2 BeanMapHandler<K, V> (extends AbstractKeyedHandler<K, V>)

       用於獲取全部結果集,將每行結果集轉換爲Javabean做爲value,並指定某列爲key,封裝到HashMap中。至關於對每行數據的作BeanHandler同樣的處理後,再指定列值爲Key封裝到HashMap中。

String sql = "select * from users";
// new BeanMapHandler<Integer, Users>(Users.class,"id")
Map<Integer, Users> rs = runner.query(sql, new BeanMapHandler<Integer, Users>(Users.class,1));
// Print: BeanMapHandler: {
// 1=Users{id=1, userName='測試用戶1', loginName='test1', userPassword='jiseflwes', userLevel=10, userLock=true},
// 2=Users{id=2, userName='知道什麼', loginName='hello', userPassword='2556sefsfs', userLevel=10, userLock=true},
// 3=Users{id=3, userName='編程就編程', loginName='cjava', userPassword='sfsfsef254sefs', userLevel=2, userLock=false}}
System.out.println("BeanMapHandler: " + rs);

       須要注意這個結果轉換類也能夠像BeanHandler的狀況一和狀況二介紹的那樣定義一個processor,但默認狀況下這麼作了就會以每行的第一列做爲Key,不能指定其餘列爲Key。

// 這種狀況下,以每行第一列爲key
Map<Integer, Users> rs = runner.query(sql, new BeanMapHandler<Integer, Users>(Users.class,processor));

八、BaseResultSetHandler<T>

        根據文檔介紹,若是上面的結果集處理類都不能知足你的要求,能夠經過繼承這個抽象類定義本身的結果處理類,子類必須實現無參方法handle()。

        作個簡單的例子,好比要將指定列值加一個前綴"class-"後添加到ArrayList中:

//------------- 定義類 MeResultHandler.java -------------
/**
 * 自定義的結果處理類,對結果集的操做直接調用父類已經封裝好的方法。
 * 這兒只是對取到的結果包裝加工。
 */
public class MeResultHandler extends BaseResultSetHandler<List<String>> {

    private final int columnIndex;

    // 指定要獲取值的列索引
    public MeResultHandler(int columnIndex) {
        this.columnIndex = columnIndex;
    }

    // 重寫父類的方法,封裝每行數據
    @Override
    protected List<String> handle() throws SQLException {
        List<String> rows = new ArrayList<String>();
        // 由於父類已經封裝好了對ResultSet各類操做,直接調用父類方法 next()
        while(this.next()) {
            rows.add(handleRow());
        }
        return rows;
    }

    // 自定義的數據處理方法
    @SuppressWarnings("unchecked")
    private String handleRow() throws SQLException {
        // 直接調用父類方法 getObject()
        return "class-" + String.valueOf(this.getObject(this.columnIndex));
    }
}
//------------- 使用類 -------------
List<String> rs = runner.query(sql, new MeResultHandler(1));
// Print: MeResultHandler: [class-1, class-2, class-3]
System.out.println("MeResultHandler: " + rs);

   ======================================================================

      總的來講,最終的數據處理是在 BasicRowProcessor 的四個方法中進行,涉及到JavaBean的話會經過 BasicRowProcessor 調用 BeanProcessor 的兩個方法。其餘的都是對每行數據轉換後的結果的封裝。

相關文章
相關標籤/搜索