1. 简介
使用 JDBC 时冗余的代码太多了,为了简化开发我们可以选择使用 DbUtils 。
Commons DbUtils是Apache组织提供的一个对JDBC进行简单封装的开源工具类库,使用它能够简化JDBC应用程序的开发,同时也不会影响程序的性能。
2. 导入Jar包
下载网址:https://mirrors.tuna.tsinghua.edu.cn/apache//commons/dbutils/binaries/commons-dbutils-1.7-bin.zip
下载后解压,复制 commons-dbutils-1.7.jar 文件,导入项目即可。
3. 核心类
3.1 DbUtils类
常用方法:
DbUtils.closeQuietly(); // 它有多种重载可以用于关闭 Connection Statement ResultSet等
3.2 QueryRunner类
构造方法:
QueryRunner(); // 无参构造,需要在使用时提供Connection对象,和自己手动关闭
QueryRunner(DataSource ds); // 构造方法中给出数据源,查询时不必提供Connection对象,自然也没有关闭操作
一般使用有参构造,在DAO类中声明为成员变量,然后各个方法共用。这样在执行SQL语句时只有一行代码,看起来就很简洁了。
常用方法:
int update([Connection conn, ]String sql,Object... params); // 增 删 改 操作
T query([Connection conn, ]String sql,ResultSetHandler<T> handler,Object... params);
// 查询操作
update()
方法很简单,就是传入SQL执行就行了,SQL中有 ?时,SQL之后跟着写上就行,返回值为受影响的行数。
如:
qr.update("update employee set salary=? where id=?",9000,4); // 把id为4的员工的薪资改为9000
query()
方法就是多了个 ResultSerHandler “结果集处理器” ,这是一个接口,作用就是处理结果集,它有多个实现类,也就是多种处理方式,如BeanHandler就是将结果集中的第一行打包成对应的实体类。在使用时只需要根据功能创建并传入所需对象即可。
各实现类的功能如下:
实现类 | 说明 |
---|---|
new ArrayHandler() | 将结果集中的第一条记录封装到一个Object[]数组中,数组中的每一个元素就是这条记录中的每一个字段的值 |
new ArrayListHandler() | 将结果集中的每一条记录都封装到一个Object[]数组中,将这些数组在封装到List集合中。 |
new BeanHandler<Employee>(Employee.class) | 将结果集中第一条记录封装到一个指定的javaBean中(这里Employee就是一个实体类) |
new BeanListHandler<Employee>(Employee.class) | 将结果集中指定的列的字段值,封装到一个List集合中 |
new ScalarHandler<Long>(1) | 获取结果集第一行,某一列的值。参数可为列名或列号,默认获取第一列数据。 常用来获取 Count( * ) 的结果,但注意它是 long 类型的 |
new ColumnListHandler(“name”) | 将结果集中指定的列的字段值,封装到一个List集合中,如本例就是获取name列,也可以传入列号,如果没有参数,默认获取第一列。 |
new MapHandler() | 将结果集中第一条记录封装到了Map集合中,key就是字段名称, value就是字段值 |
new MapListHandler() | 将结果集中每一条记录封装到了Map集合中,key就是字段名称, value就是字段值,在将这些Map封装到List集合中。 query 的返回值类型为 List<Map<String,Object>> |
new KeyedHandler<Integer>(1) | 将结果集中每一条记录封装到Map,在将这个map集合做为另一个 Map的value,另一个Map集合的key是指定的字段的值。 本例 query 返回值类型为: Map<Integer,Map<String,Object>> 以第一列(id–int类型)为外层Map的key,这样就可以快捷的通过 id 找到每一行数据。 |
示例:
@Test // BeanHandler
public void test52() throws Exception{
QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
Employee e = qr.query("select * from employee where id=?", new BeanHandler<Employee>(Employee.class), 3);
System.out.println(e);
}
@Test // ArrayListHandler
public void test70() throws Exception{
QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
List<Object[]> l = qr.query("select * from employee", new ArrayListHandler());
for(Object[] o : l){
System.out.println(Arrays.toString(o));
}
}
关于泛型的一些解释:
可以发现ResultSetHandler接口就是带泛型的,也就是说结果集处理器是支持泛型的,那这有什么用呢?通过查看 query 函数的定义可知:query是一个泛型方法,其返回值类型,由参数类型(也就是ResultSetHandler中出入的泛型参数 T)来决定。故:给 ResultSetHandler 指定泛型的作用就是确定返回值类型。
那为什么有些实现类不支持泛型呢?当然就是因为在继承 ResultSetHandler 时就确定了泛型。
如:ArrayHandler 其定义如下:
可以发现 T 被指定为了 Object[] 类型。
还有,
既然传入泛型决定了query的返回值类型,那么BeanHandler为什么还要传入 类型对象 的参数呢?
可知,一定是通过反射机制,为我们分装返回的结果的,要进行反射操作,就要首先获得类的类对象,然而泛型只是在编译期有效,JVM无法通过泛型获取对应的类对象,因为在JVM的世界里就不存在泛型这个东西。所以我们需要手动传入。
4. 实体类
Java中以类对应数据库中的表,每一张表对应一个类,每一列对应类中的一个属性;每一行数据对应一个对象。
这样的类就是实体类,它们需要满足如下条件:
-
符合 Java Bean 规范
- 需要实现 序列化接口, Serializable (暂时可以省略)
- 提供私有字段: private 类型 变量名;
- 提供 getter 和 setter
- 提供 空参构造
-
类中的成员变量名需要与表中的字段名保持一致
在开发中数据库名很重要,修改不易,所以是 成员变量名 以 表字段 名为准。
那表字段名又如何设计呢?
阿里云规范中:
【强制】表名、字段名必须使用小写字母或数字 , 禁止出现数字开头,禁止两个下划线中间只出现数字。数据库字段名的修改代价很大,因为无法进行预发布,所以字段名称需要慎重考虑。
说明: MySQL 在 Windows 下不区分大小写,但在 Linux 下默认是区分大小写。因此,数据库名、 表名、字段名,都不允许出现任何大写字母,避免节外生枝。
正例: aliyun _ admin , rdc _ config , level 3_ name 反例: AliyunAdmin , rdcConfig , level 3 name