DButils
严格来说,DButils不能算作框架,最多算一个小工具。
框架:框架就是一些脚手架,或者是一些模板。框架里面提供了一部分现成的代码供开发者来使用,但是要成为一个完整的产品,还需要开发者在框架的基础之上去补充一些代码。
框架不是万能的,一个框架只能在一个特定的领域起作用,不能去干所有的事情。
例如:Mybatis,在数据的持久化这一块很有优势,但是不能去做别的事情,例如去进行网络通信啊之类的。
那么框架是怎么来的呢?
举个例子,假如现在有很多人都去参与开发一些软件,这些软件都需要去保存数据到数据库。他们一开始都会使用原始的JDBC去把这些数据保存到数据库,后面发现JDBC的功能不够强大,JDBC不够灵活,所以有的人就想把JDBC抽离出来,把这部分处理数据保存的逻辑抽离出来,变成一些接口(你给他传一个sql语句,他自动给你完成后续的事情),这些接口具有独立的功能,并且有通用性。那么我们就说这个抽离出来的逻辑就是一个框架。
DBUtils能干什么
主要是帮助我们去解析返回结果,并且封装为对象或者是集合
核心类
导包
<!--DBUtils-->
<!-- https://mvnrepository.com/artifact/commons-dbutils/commons-dbutils -->
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.7</version>
</dependency>
DBUtils
// DBUtils这个工具类提供了几个方法
// 例如以下
public static void close(Connection conn);
public static void close(ResultSet ret);
public static void close(Statement statement);
public static void closeQuietly(Connection conn);
public static void commitAndClose(Connection conn);
……
QueryRunner
提供 了两个构造方法,一个需要传递数据源进去,还有一个不需要传递数据源进去
// 不传递数据源
public QueryRunner() {
}
// 传递数据源
public QueryRunner(DataSource ds) {
super(ds);
}
QueryRunner还提供了一些方法用来帮助我们去执行sql语句
// 查询
QueryRunner queryRunner = new QueryRunner(dataSource);
queryRunner.query(sql,resultSetHandler); // 这种方式DBUtils会帮助我们关闭连接
queryRunner.query(sql,resultSetHandler,param...); // 这种方式DBUtils会帮助我们关闭连接
// 查询-传递进去连接
QueryRunner queryRunner = new QueryRunner(); // 这个地方就不传递数据源
queryRunner.query(connection,sql,resultSetHandler); // 这种方式DBUtils不会帮助我们关闭连接
queryRunner.query(connection,sql,resultSetHandler,param...);
// 修改、删除、增加都是使用update方法
QueryRunner queryRunner = new QueryRunner(dataSource);
queryRunner.update(sql,params...);
queryRunner.update(connection,sql,params...);
注意:在我们调用QueryRunner的方法的时候,假如我们执行queryRunner.query
或者是queryRunner.update
的时候,假如我们传递进去了connection对象,那么DBUtils就会不管这个connection是不是要关闭,假如没有传递,那么DButils在执行完sql相关的逻辑之后,会帮助我们关闭这个连接。
至于这个连接调用connection.close();
是真的关闭连接还是放回连接池,则取决于我们连接本身,要看这个连接对象实例是否重写了close()
方法
ResultSetHandler
这个接口是定义了帮助我们去解析ResultSet
这个对象的,已经实现好了几个类型帮助我们去干这个活
BeanListHandler
这个是帮助我们把查询的结果转化为一个List< Bean>
@Test
public void testQueryBeanListHandler() throws SQLException {
// 创建QueryRunner对象
QueryRunner queryRunner = new QueryRunner(DruidUtils.getDataSource());
// 我们要执行sql
String sql = "select * from user";
List<User> userList = queryRunner.query(sql, new BeanListHandler<User>(User.class));
System.out.println(userList);
}
BeanHandler
这个是帮助我们把查询的结果转化为一个Bean,注意这里是指我们查询结果的第一条记录
@Test
public void testSelectOne() throws SQLException {
QueryRunner queryRunner = new QueryRunner(DruidUtils.getDataSource());
// 我们要执行sql
String sql = "select * from user where id = ?";
User user = queryRunner.query(sql, new BeanHandler<User>(User.class),1);
System.out.println(user);
}
ScalarHandler
这个是直接返回我们查询的结果(AVG、SUM、COUNT、MAX、MIN)
@Test
public void testSelectCount() throws SQLException {
QueryRunner queryRunner = new QueryRunner(DruidUtils.getDataSource());
String sql = "select count(1) from user";
Object query = queryRunner.query(sql, new ScalarHandler<>());
System.out.println(query);
}
ColunmnListHandler
这个是查询单列的一个结果,然后封装到一个List里面去
// 查询单列 ColumnListHandler
@Test
public void testSelectNameList() throws SQLException {
QueryRunner queryRunner = new QueryRunner(DruidUtils.getDataSource());
String sql = "select username from user";
List<String> strList = queryRunner.query(sql, new ColumnListHandler<String>());
System.out.println(strList);
}
这个ResultSetHandler底层是怎么做的呢?
这个是我们自己实现的解析的代码
class MyResultSetHandler implements ResultSetHandler<List<User>> {
@Override
public List handle(ResultSet resultSet) throws SQLException {
ArrayList<User> users = new ArrayList<>();
while (resultSet.next()) {
User user = new User();
user.setId(resultSet.getInt("id"));
user.setUsername(resultSet.getString("username"));
user.setPassword(resultSet.getString("password"));
user.setGender(resultSet.getString("gender"));
users.add(user);
}
return users;
}
}
@Test
public void testQueryBeanListHandler() throws SQLException {
// 创建QueryRunner对象
QueryRunner queryRunner = new QueryRunner(DruidUtils.getDataSource());
// 我们要执行sql
String sql = "select * from user";
List<User> userList = queryRunner.query(sql, new BeanListHandler<User>(User.class));
System.out.println(userList);
}
结论,我们自己实现的ResultSetHandler里面有一些东西是写死的,但是这个DBUtils帮我们实现的这个BeanListHandler是通用的,为什么可以做到通用,因为我们在创建BeanListHandler的时候,需要传递一个.class
对象进去,那么BeanListHandler就能通过这个字节码对象,通过反射获取到我们需要查询的列名以及类型
假如我们的Bean的成员变量名字和数据库表的列名对应不上怎么办呢?
那这个时候我们的DBUtils的局限性就显示出来了,这个工具没有办法帮助我们解决这个问题。
DBUtils批处理
// 批处理
@Test
public void testBatch() throws SQLException {
QueryRunner queryRunner = new QueryRunner(DruidUtils.getDataSource());
String sql = "insert into user values (?,?,?,?)";
Object[][] params = new Object[3][];
params[0] = new Object[]{4,"金角大王","我叫你一声你敢答应吗","male"};
params[1] = new Object[]{5,"铁扇公主","牛夫人","female"};
params[2] = new Object[]{6,"女儿国国王","唐长老,留下来","female"};
// 这个一位数组是什么呢? 这个batchRet对应的是每一条sql语句影响的行数
int[] batchRet = queryRunner.batch(sql, params);
for (int affectedRows : batchRet) {
System.out.println("影响的行数:" + affectedRows);
}
}
转账案例(事务)
案例一
这个案例可以正常转账,但是有问题
@Test
public void testTransfer1() {
QueryRunner queryRunner = new QueryRunner(DruidUtils.getDataSource());
Connection connection = DruidUtils.getConnection();
try {
connection.setAutoCommit(false);
String sql = "update account set money = money + ? where name = ?";
// 扣钱 -200
int affectedRows = queryRunner.update(sql, -200, "ll");
if (affectedRows < 1) {
System.out.println("扣钱失败!");
return;
}
// 加钱
int affectedRows2 = queryRunner.update(sql, 200, "oo");
if (affectedRows2 < 1) {
System.out.println("加钱失败!!");
connection.rollback();
}
connection.commit();
}catch (Exception ex) {
ex.printStackTrace();
try {
connection.rollback();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
案例二
演示出问题的情况
@Test
public void testTransfer2() {
QueryRunner queryRunner = new QueryRunner(DruidUtils.getDataSource());
Connection connection = DruidUtils.getConnection();
try {
connection.setAutoCommit(false);
String sql = "update account set money = money + ? where name = ?";
// 扣钱 -200
int affectedRows = queryRunner.update(sql, -200, "ll");
if (affectedRows < 1) {
System.out.println("扣钱失败!");
return;
}
int i = 1/0;
// 加钱
int affectedRows2 = queryRunner.update(sql, 200, "oo");
if (affectedRows2 < 1) {
System.out.println("加钱失败!!");
connection.rollback();
}
connection.commit();
}catch (Exception ex) {
ex.printStackTrace();
try {
connection.rollback();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
案例三
正确的姿势
@Test
public void testTransfer3() {
QueryRunner queryRunner = new QueryRunner();
Connection connection = DruidUtils.getConnection();
try {
connection.setAutoCommit(false);
String sql = "update account set money = money + ? where name = ?";
// 扣钱 -200
int affectedRows = queryRunner.update(connection,sql, -200, "ll");
if (affectedRows < 1) {
System.out.println("扣钱失败!");
return;
}
int i = 1/0;
// 加钱
int affectedRows2 = queryRunner.update(connection,sql, 200, "oo");
if (affectedRows2 < 1) {
System.out.println("加钱失败!!");
connection.rollback();
}
connection.commit();
}catch (Exception ex) {
ex.printStackTrace();
try {
connection.rollback();
} catch (SQLException e) {
e.printStackTrace();
}
}finally {
// 关闭连接
DbUtils.closeQuietly(connection);
}
}
总结
DBUtils大家觉得好不好?
优点:
- 对比JDBC,代码要简洁很多
- 批处理操作更加方便
- 去解析结果的时候更加通用
缺点:
- 功能不够强大(例如:如果我们Bean的成员变量名字和数据库对应不上,那么就可能封装的时候出问题)
- 在代码里面写sql语句不够优雅,耦合度比较高
所以在工作以后,大家用DBUtils用的不多,那么在工作里面,我们都使用Mybatis