DbUtils(二) 结果集实例——handler应用

来源:互联网 发布:excel2016数据记录单 编辑:程序博客网 时间:2024/06/15 23:45

      单行数据处理:ScalarHandler    ArrayHandler    MapHandler    BeanHandler

      多行数据处理:BeanListHandler    AbstractListHandler(ArrayListHandlerMapListHandler ColumnListHandler)

                          AbstractKeyedHandler(KeyedHandlerBeanMapHandler)

      可供扩展的类:BaseResultSetHandler

      Dbutils使用结果集的方法有query、insert、insertBatch三个。这些方法都在QueryRunner类中,需要注意的是insert和update方法都能执行 “insert”开头的sql语句,但是返回值有区别。insert 执行后返回的是表中的插入行生成的主键值,update 返回的是受语句影响的行数。所以,如果目标表中有主键且需要返回插入行的主键值就用 insert 方法,如果表没有主键或者不需要返回主键值可使用 update 方法。

      先建立测试用数据表[users]:

iduserNameloginNameuserPassworduserLeveluserLock1测试用户1test1jiseflwes1002知道什么hello2556sefsfs1013编程就编程cjavasfsfsef254sefs20

      字段类型,id 为主键:

1
2
3
4
5
6
[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,

1、ScalarHandler<T>     

      用于获取结果集中第一行某列的数据并转换成 T 表示的实际对象。

      该类对结果集的处理直接在 handle 方法中进行,不涉及 dbutils 库的其他类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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,newScalarHandler(),  "java程序编写","javahello", "sefsfsfwew", "15",false);
System.out.println("ScalarHandler: "+ insertRs); // Print:[ScalarHandler: 4]

      使用的时候一定要保证提供正确的列索引或列名,并且结果类型也要正确可转换。

2、ArrayHandler

      用于获取结果集中的第一行数据,并将其封装到一个数组中,一列值对应一个数组元素。

      handle 源码:

1
2
3
4
5
public Object[] handle(ResultSet rs) throws SQLException {
    // convert = new BasicRowProcessor()
    // 如果有数据,将调用 BasicRowProcessor 的 toArray(rs) 方法处理
    returnrs.next() ? this.convert.toArray(rs) : EMPTY_ARRAY;
}
1
2
3
4
5
6
7
8
9
10
11
// ---- 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,newArrayHandler(),  "java程序编写","javahello", "sefsfsfwew", "15",false);
// Print: ArrayHandler: [5]
System.out.println("ArrayHandler: "+ Arrays.toString(insertRs));

3、MapHandler

      用于获取结果集中的第一行数据,并将其封装到一个Map中,Map 中 key 是数据的列别名(as label),如果没有就是列的实际名称,Map 中 value 就是列的值,注意代表列的 key 不区分大小写。

      handle 源码:

1
2
3
4
5
public Map<String, Object> handle(ResultSet rs) throwsSQLException {
    // convert = new BasicRowProcessor()
    // 如果有数据,将调用 BasicRowProcessor 的 toMap(rs) 方法处理
    returnrs.next() ? this.convert.toMap(rs) :null;
}

      通过查看 BasicRowProcessor 代码,可以知道封装结果集的 Map 其实是一个 LinkedHashMap 对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// ---- 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,newMapHandler(),  "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"));

4、BeanHandler<T>

      用于获取结果集中的第一行数据,并将其封装到JavaBean对象。

      整个转换过程最终会在 BeanProcessor 类中完成。

      执行代码:

1
2
3
4
5
6
7
//---- 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有两个构造方法:

1
2
3
4
5
6
7
8
// BeanHandler 构造方法
public BeanHandler(Class<T> type) {
    this(type, ArrayHandler.ROW_PROCESSOR);
}
public BeanHandler(Class<T> type, RowProcessor convert) {
    this.type = type;
    this.convert = convert;
}

      可以看出其实都是调用的第二个方法。

1
2
3
4
5
6
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(newBeanProcessor())));
// 所以关键的地方在 new BeanProcessor() 这个具体处理结果的对象

      情况一:只涉及到下划线,表字段名用下划线间隔(如表users_t字段:[id],[user_name],[login_name],[user_password],[user_level],[user_lock]),现在要封装到Javabean中Users类(代码见上),其中属性使用驼峰命名。可以用dbutils1.6提供的BeanProcessor类的子类GenerousBeanProcessor。

1
2
3
4
5
6
7
8
9
10
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类(代码同上):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 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);

5、BeanListHandler<T>

      用于将结果集的每一行数据转换为Javabean,再将这个Javabean添加到ArrayList中。可以简单的看着是BeanHandler的高级版,只不过是多了一步,就是将生成的Javabean添加到ArrayList中,其他的处理都和BeanHandler一样。

1
2
3
4
5
6
7
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);

6、AbstractListHandler<T>

1
2
3
4
5
6
7
8
9
// 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方法
    }
    returnrows;
}

      AbstractListHandler抽象类已经实现handle方法,该方法其实只是起到一个包装作用,将处理好的每行数据添加到ArrayList中。每行的数据处理通过调用handleRow方法实现,所有它的3个子类都必须实现这个方法。

6.1 ArrayListHandler (extends AbstractListHandler<Object[]>)

      用于将结果集每行数据转换为Object数组(处理过程等同与ArrayHandler),再将该数组添加到ArrayList中。简单点,就是将每行数据经过ArrayHandler处理后添加到ArrayList中。

1
2
3
4
5
6
7
8
9
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中。

1
2
3
4
5
6
7
8
9
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>的加强版。

1
2
3
4
5
6
7
8
9
10
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);
}

7、AbstractKeyedHandler<K, V>

     AbstractKeyedHandler是一个抽象类,已经实现了handle方法,其子类必须实现createKey(ResultSet rs)和createRow(ResultSet rs)方法,以便handle()的调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
  * 返回一个HashMap<K, V>
  * createKey(rs) 将某列的值作为HashMap的key
  * createRow(rs) 将结果集转换后作为HashMap的value
  */
 @Override
 publicMap<K, V> handle(ResultSet rs) throwsSQLException {
     Map<K, V> result = createMap();
     while(rs.next()) {
         result.put(createKey(rs), createRow(rs));// 需要子类自己实现
     }
     returnresult;
 }

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

      用于获取所有结果集,将每行结果集转换为Map<String, Object>,并指定某列为key。可以简单的认为是一个双层Map,相当于先对每行数据执行MapHandler,再为其指定key添加到一个HaspMap中。KeyedHandler<K> 中的<K>是指定的列值的类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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中。

1
2
3
4
5
6
7
8
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。

1
2
// 这种情况下,以每行第一列为key
Map<Integer, Users> rs = runner.query(sql,new BeanMapHandler<Integer, Users>(Users.class,processor));

8、BaseResultSetHandler<T>

        根据文档介绍,如果上面的结果集处理类都不能满足你的要求,可以通过继承这个抽象类定义自己的结果处理类,子类必须实现无参方法handle()。

        做个简单的例子,比如要将指定列值加一个前缀"class-"后添加到ArrayList中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//------------- 定义类 MeResultHandler.java -------------
/**
 * 自定义的结果处理类,对结果集的操作直接调用父类已经封装好的方法。
 * 这儿只是对取到的结果包装加工。
 */
public class MeResultHandler extendsBaseResultSetHandler<List<String>> {
 
    privatefinal int columnIndex;
 
    // 指定要获取值的列索引
    publicMeResultHandler(intcolumnIndex) {
        this.columnIndex = columnIndex;
    }
 
    // 重写父类的方法,封装每行数据
    @Override
    protectedList<String> handle() throwsSQLException {
        List<String> rows =new ArrayList<String>();
        // 因为父类已经封装好了对ResultSet各种操作,直接调用父类方法 next()
        while(this.next()) {
            rows.add(handleRow());
        }
        returnrows;
    }
 
    // 自定义的数据处理方法
    @SuppressWarnings("unchecked")
    privateString handleRow() throwsSQLException {
        // 直接调用父类方法 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 的两个方法。其他的都是对每行数据转换后的结果的封装。

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 微信支付月限额怎么办 微信超额20万怎么办 微信支付超额了怎么办 微信零钱超额了怎么办 微信的充值冲错了怎么办 有流量还扣话费怎么办 自动取款机充值到电子账户怎么办 淘宝qb充错了怎么办 q币冲错了人家不给怎么办 qq充值话费错号怎么办 qq充错号码了怎么办 qq交话费不到账怎么办 充错手机号码而且是空号怎么办 微信钱包充错话费怎么办 QQ充值话费充到空号了怎么办 给别人充错话费怎么办 用qq交错话费对方是空号怎么办 号码变成空号了怎么办 qq冲流量冲错了怎么办 流量冲错了套餐怎么办 微信流量充错号码怎么办 微信支付不进账怎么办 充话费充不进去怎么办 用支付宝充话费没到账怎么办 支付宝充话费未到账怎么办 话费充了不到账怎么办 转转买家不确认收货怎么办 充话费错了怎么办啊 淘宝充值流量没到账怎么办 微信手机充错了怎么办 支付宝充话费没到账怎么办 裤子摔了一个洞怎么办 顾客反应衣服质量不好怎么办 淘宝买的衣服味道很大怎么办 三国杀账号忘了怎么办 宽带连接被删了 怎么办 手机被偷了qq怎么办 手机丢了微信怎么办啊 手机店把手机修坏了怎么办 在手机店买到山寨机手机怎么办 有人在qq群上骂我怎么办