dbutils API介绍
1.简介
dbutils就是JDBC操作的类库,是对JDBC操作的封装,提供一些简易的API来操作数据库,提供了数据库增删改查等通用的JDBC实现。
2.org.apache.commons.dbutils.handlers包
这个包里面的类是对ResultSetHandler接口的实现类,主要是用来处理ResultSet(结果集)的转换,将结果集转成Object[],List< Object[] >,List< T >,Map< K,V >等的数据结构。这些实现类会在做数据库查询操作时使用,主要有以下类:
- AbstractListHandler 将ResultSet转为List的抽象类
- ArrayHandler 将ResultSet转为一个Object[]的ResultSetHandler实现类
- ArrayListHandler 将ResultSet转换为List< Object[] >的ResultSetHandler实现类
- BeanHandler 将ResultSet行转换为一个JavaBean的ResultSetHandler实现类
- BeanListHandler 将ResultSet转换为List< T >的ResultSetHandler实现类
- ColumnListHandler 将ResultSet的一个列转换为List< Object >的ResultSetHandler实现类
- KeyedHandler 将ResultSet转换为Map< K,V>的ResultSetHandler实现类
- MapHandler 将ResultSet的首行转换为一个Map的ResultSetHandler实现类
- MapListHandler 将ResultSet转换为List< K,V>的ResultSetHandler实现类
- ScalarHandler 将ResultSet的一个列到一个对象。
3.org.apache.commons.dbutils.QueryRunner
它与ResultSetHandler组合在一起使用可以完成大部分的数据库操作,是用来执行SQL语句的工具类。主要的方法如下。
QueryRunner的无参构造函数和有参构造函数:
- 用QueryRunner无参构造函数创建的对象,手动提交事务。
- 用QueryRunner有参构造函数创建的对象,自动提交事务。
QueryRunner中一共有6种方法:
- execute(执行SQL语句)
- batch(批量处理语句)
- insert(执行INSERT语句)
- insertBatch(批量处理INSERT语句)
- query(SQL中 SELECT 语句)
- update(SQL中 INSERT, UPDATE, 或 DELETE 语句)
比较常用就是query和update
3.1 query
query要用到org.apache.commons.dbutils.handlers包下的实现类,这类提供了ResultSet(结果集)的转换。
ArrayHandler例子:
这个是放回一个Object[]对象,一个Object[]对应一行数据,如:{1,“zs”}
public static void testArrayHandler() throws SQLException{
//自己写的DataSourceUtil.getDataSourceWithC3P0(),返回datasource
//QueryRunner执行工具
QueryRunner runner = new QueryRunner(DataSourceUtil.getDataSourceWithC3P0());
//返回第一个数据(ResultSet转成Object[]),一个Object[]对应表查询的一行数据
Object[] student = runner.query("select id,name from student where id>?", new ArrayHandler(),1);
System.out.println(student[0]+"\t"+student[1]);
}
ArrayListHandler例子
这个是放回一个List<Object[]>对象,如:[ {1,“zs”},{2,“李”} ]
public static void testArrayHandler() throws SQLException{
//自己写的DataSourceUtil.getDataSourceWithC3P0(),返回datasource
//QueryRunner执行工具
QueryRunner runner = new QueryRunner(DataSourceUtil.getDataSourceWithC3P0());
//ResultSet转成list<Object[]>,查询多行操作
List<Object[]> student = runner.query("select id,name from student", new ArrayHandler());
System.out.println(student[0]+"\t"+student[1]);
}
MapHandler例子
这个是放回一个Map<K,V>对象,{ID=1,name=“ZS”}
public void testMapHandler() throws SQLException{
QueryRunner qr = new QueryRunner(DataSourceUtil.getDataSourceWithC3P0());
String sql = "select * from users";
//做出map
Map<String,Object> map = qr.query(sql, new MapHandler());
for(Map.Entry<String, Object> me : map.entrySet())
{
System.out.println(me.getKey() + "=" + me.getValue());
}
}
MapListHandler例子
List<Map<K,V>>,如[{ID=1,name=“ZS”},{ID=2,name=“ls”}]
public void testMapListHandler() throws SQLException{
QueryRunner qr = new QueryRunner(DataSourceUtil.getDataSourceWithC3P0());
String sql = "select * from users";
List<Map<String,Object>> list = (List) qr.query(sql, new MapListHandler());
for(Map<String,Object> map :list){
for(Map.Entry<String, Object> me : map.entrySet())
System.out.println(me.getKey() + "=" + me.getValue());
}
}
KeyedHandler例子
Map<Integer,Map<String,Object>>,就是Map里面套map,如:{1={ID=1,name=“ZS”},2={ID=2,name=“ls”}}
public void testKeyedHandler() throws Exception{
QueryRunner qr = new QueryRunner(DataSourceUtil.getDataSourceWithC3P0());
String sql = "select * from users";
//MAP里面套map
Map<Integer,Map<String,Object>> map = qr.query(sql, new KeyedHandler("id"));
for(Map.Entry<Integer, Map> me : map.entrySet()){
int id = me.getKey();
Map<String,Object> innermap = me.getValue();
for(Map.Entry<String, Object> innerme : innermap.entrySet()){
String columnName = innerme.getKey();
Object value = innerme.getValue();
System.out.println(columnName + "=" + value);
}
}
}
3.2 update
update会相对简单很多,用来执行数据库的DML操作,因为不涉及ResultSet,所以直接给SQL语句和变量值就可以了。
增操作
public void Insert() throws SQLException{
QueryRunner qr = new QueryRunner(DataSourceUtil.getDataSourceWithC3P0());
String sql = "insert into student(id,name) value(?,?)";
int count = qr.query(sql, new Object[]{"1","zs"});
}
删操作
public void Delete() throws SQLException{
QueryRunner qr = new QueryRunner(DataSourceUtil.getDataSourceWithC3P0());
String sql = "delete from student where id = ?";
qr.query(sql, 1);
}
改操作
public void Updata() throws SQLException{
QueryRunner qr = new QueryRunner(DataSourceUtil.getDataSourceWithC3P0());
String sql = "updata from student set name = ? where id = ?";
qr.query(sql, new Object[]{"1","xiaoming"});
}
4.事务手动提交
重复一下QueryRunner的构造函数,如果我们需要手动提交事务用无参构造。
QueryRunner的无参构造函数和有参构造函数:
- 用QueryRunner无参构造函数创建的对象,手动提交事务。
- 用QueryRunner有参构造函数创建的对象,自动提交事务。
自动提交事务将每个SQL语句都当成一个事务并进行提交,而有时候我们需要把多个SQL语句当成一个事务来处理,举个简单的也是最经典的例子:银行转账。
如:A转钱给B,我们需要完成以下操作,A账户减去转账金额,B账户加上转账金额。这个过程涉及多个SQL操作(两个修改updata),如果我们使用自动提交(每个SQL一个事务),那么就可能发生以下情况,A账户减去转账金额,但当B账户加上转账金时额发生错误。出现A减钱了B没加钱。因为是自动提交,B加钱发生错误也只是B的事务回滚,不涉及A。如果我把这个过程涉及多个SQL操作放到一个事务里面,那么任何一边发生错误,事务都会回滚,因为事务的原子性(Atomicity):事务中的全部操作在数据库中是不可分割的,要么全部完成,要么全部不执行。每次手动提交都会将执行SQL语句放到同一个事务里面(运行多个SQL语句,然后提交,那么执行的SQL语句都放在一个事务里面)。
其中这里与JDBC的基础有关,可以看以下代码例子:
public void QueryRunnertest() throws SQLException{
String url = "jdbc:mysql://127.0.0.1:3306/test?useSSL=false&serverTimezone=UTC";
String user = "root" ;
String password = "root";
//这里随便创建一个连接
Connection conn= DriverManager.getConnection(url,user,password);
//设置连接为手动提交
conn.setAutoCommit(false);
//因为用手动提交,所以需要无参构造,有参的会帮你自动提交的
QueryRunner qr = new QueryRunner();
//插入SQL
String sql = "insert into student(id,name) value(?,?)";
try{
//只需要把连接传给函数就行了
int count1 = qr.query(conn,sql, new Object[]{"1","zs"});
int count2 = qr.query(conn,sql, new Object[]{"2","ls"});
int count3 = qr.query(conn,sql, new Object[]{"3","ww"});
//提交事务
conn.commit();
}catch(Exception e){
//操作回滚
conn.rollback(); //一旦其中一个操作出错都将回滚,使全部操作都不成功 (事务原子性)
}
}
在这里例子里面,先获取连接,然后设置连接为手动提交,并创建QueryRunner工具类,并使用工具里面的函数通过传递连接,SQL语句,变量。