QueryRunner查询数据库& QueryRunner如何封装起来的 |
---|
一、简单描述JDBC获取连接Connection后能做什么?
首先 查看相关API
(一).使用类和方法
- QueryRunner(接口的方法如下两个):
- update(connection,sql,object…objs) //进行增删改方法
- query(connection,sql,ResultSetHandler,object…objs)//查询方法
-
- ResultSetHandler接口
- 常用实现类:
- BeanHandler 封装一个实体对象
- BeanListHandler 封装 一组多个实体对象到list
- ScalarHandler 获取单个值
- MapListHanlder 封装 一组map到list
(二)在详细的解析了JDBC使用C3P0 或 DBCP获取了连接Connection 之后
进一步的需要对数据库进行数据的增删改查一系列的操作
其中大多数操作(其实可以封装在DAO里)不外乎(操作这些共同的方法)
public interface DAO<T> {
/* 一、批量处理的方法
*
* @param connection: 数据库连接
* @param sql: SQL语句
* @param args: 填充占位符的Object []类型的可变参数
*/
void batch(Connection connection,String sql,Object[] ...args);
/* 二、返回具体的一个值 如 平均成绩 总数
*
* @param connection: 数据库连接
* @param sql: SQL语句
* @param args: 填充占位符的可变参数
*/
<E> E getForValue(Connection connection,String sql,Object ...args);
/* 三、返回一个T的一个集合相当于查询所有记录
*
* @param connection: 数据库连接
* @param sql: SQL语句
* @param args: 填充占位符的可变参数
*/
List<T> getForList(Connection connection,String sql,Object ...args);
/* 四、返回一个T的对象
*
* @param connection: 数据库连接
* @param sql: SQL语句
* @param args: 填充占位符的可变参数
*/
T get(Connection connection,String sql,Object ...args) throws SQLException;
/* 五、增删改操作
*
* INSERT UPDATE DELETE
* @param connection: 数据库连接
* @param sql: SQL语句
* @param args: 填充占位符的可变参数
*/
void update(Connection connection,String sql,Object ...args) throws SQLException;
}
二、此文主要解析使用框架QueryRunner 进行 ★查询qruery()操作
//正如我们平时使用最流行的 方法进行操作 不多说 上代码
<1>首先导入jar包(驱动和连接池的不算)
commons-dbutils-1.3.jar
<2>封装使用C3P0获取连接和关闭连接 这两个方法封装如下:
public class JDBCUtils {
//数据库连接池值应该被初始化一次放在静态代码块中
private static DataSource datasource=null;
static{
datasource =new ComboPooledDataSource("helloc3p0");
}
public static Connection getConnection() throws SQLException{
return datasource.getConnection();
}
//用完资源后需要关闭
/*释放资源:
Connection
Statement
ResultSet
1 尽量晚创建早释放
2 后使用的先关闭*/
public static void release(ResultSet rs,Statement statement,Connection conn){
if(rs!=null){
try{
rs.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(statement!=null){
try{
statement.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
<3>步创建QueryRunner 实例对象 调用query()方法
例一、以最复杂查询所有对象的键值对为例如下:
//分析所有对象的键值对 ①键值对需要放在Map集合中 多个对象需要放在集合中 所以如下
//不同的查询方法只是返回结果需要实现的ResultSetHandler接口对象不同
//测试查询List
@Test
public void testMapList() throws Exception{
Connection conn =JDBCUtils.getConnection();
//创建QueryRunner对象
QueryRunner qr = new QueryRunner();
//调用方法其query查询方法
try{
//下面需要根据查询方法创建ResultSetHandler对象
//将结果集中的每一行数据都封装到一个Map里,然后再存放到List
List<Map<String, Object>> query = qr.query(conn, "select * from
表名 where 字段名=?", new MapListHandler(),"女");
}catch (Exception e){
System.out(e.getMasage());
}finally{ //关闭资源
JDBCUtils.release(conn, null, null);
}
//此处作测试直接遍历Map打印 否则返回
for (Map<String, Object> map : query) {
Set<Entry<String, Object>> entrys = map.entrySet();
Iterator<Entry<String, Object>> iterator = entrys.iterator();
while (iterator.hasNext()) {
Map.Entry<String, Object> entry = iterator.next();
String key = entry.getKey();
Object value = entry.getValue();
System.out.print(key+"\t"+value+" |||| ");
}
System.out.println();
}
}
}
例二、查询返回某个值如(总数和 或 平均分)
实现如下:
//测试查询单个值
@Test
public void testScalar() throws Exception{
Connection conn =JDBCUtils.getConnection();
//创建QueryRunner对象
QueryRunner qr = new QueryRunner();
//调用方法
Object query = qr.query(conn, "select count(*) from student", new ScalarHandler());
//此处测试直接打印(实际应用中返回结果)
System.out.println(query);
//关闭连接
JDBCUtils.release(conn, null, null);
}
三、在没有使用QueryRunner 框架之前是如何实现的呢?
使用的操作实现类有两个是prepareStatement和Statement
使用的结果处理有ResultSetMetaData实现类获取结果对象的对应值
★★那么prepareStatement和Statement它们有什么区别呢???
第一:
prepareStatement会先初始化SQL
先把这个SQL提交到数据库中进行预处理,多次使用可提高效率。
createStatement不会初始化,没有预处理,没次都是从0开始执行SQL
第二:
prepareStatement可以替换变量
在SQL语句中可以包含?,可以用ps=conn.prepareStatement("select* from Cust where ID=?");
int sid=1001;
ps.setInt(1, sid);
rs = ps.executeQuery();
可以把?替换成变量。
而Statement只能用
int sid=1001;
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("select * from Cust where ID="+sid);
来实现。
第三:
prepareStatement会先初始化SQL,
先把这个SQL提交到数据库中进行预处理,多次使用可提高效率。
createStatement不会初始化,没有预处理,没次都是从0开始执行SQL
例一:
使用方式一:获取对象为statement进行查询单个对象如下
/**一、使用statement测试查询单个对象
*
* @throws Exception
*/
@Test
public void testQuery() throws Exception{
Connection connection =JDBCUtils.getConnection();
//获取执行命令对象
Statement statement = connection.createStatement();
//执行★
ResultSet set = statement.executeQuery("select sex,name from 表名");
while(set.next()){
// Object id = set.getObject(1);//得到第一列
Object name = set.getObject(2);//得到第2列
Object sex = set.getObject(1);//得到第3列
// Object email = set.getObject(4);//得到第4列
// Object borndate = set.getObject(5);//得到第5列
//输出一个就行测试
System.out.println(name+"\t"+sex);
}
//释放资源
JDBCUtils.release(set, statement,connection);
}
}
例二:
使用方式二:使用PreparedStatement 的对象prepareStatement 改进方式一
查询所有对象如:
public List<T> query(Class<T> clazz, String sql, Object... objects) {
Connection connection = null;
PreparedStatement statement = null;
ResultSet set = null;
List<T> list=new ArrayList<>();
try {
//1.获取连接
connection = JDBCUtils.getConnection();
//2.获取PreparedStatement对象prepareStatement
statement = connection.prepareStatement(sql);
//为sql占位符 赋值
for (int i = 0; i < objects.length; i++) {
statement.setObject(i+1, objects[i]);
}
//3.执行sql 进行executeQuery()查询操作并处理结果
set= statement.executeQuery();
//创建ResultSetMetaData对象获取查询结果的对象对应值
ResultSetMetaData metaData = set.getMetaData();
while(set.next()){
//通过反射创建对象
T t = clazz.newInstance();//实体类必须有无参构造
//遍历结果集
for (int i = 0; i < metaData.getColumnCount(); i++) {
//得到列名
String name = metaData.getColumnLabel(i+1);
//得到列对应的值
Object value = set.getObject(name);
//通过反射设置属性
Field field = clazz.getDeclaredField(name);
//利用反射对private私有属性进行暴力破解
field.setAccessible(true);
//调用封装的实体类中的set()方法给属性赋值
field.set(t, value);
}
// 将对象放入ArrayList集合中
list.add(t);//将t添加到list
}
//返回最终结果
return list;
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}finally{
//关闭连接
JDBCUtils.release(connection, statement, set);
}
}
四、分析总结
在使用QueryRunner 之前我们的操作对象有两个
1.prepareStatement和Statement
调用其方法:
execute()进行查询 相当于 QueryRunner的query()方法
executeUpdate()进行增删改 相当于 QueryRunner的update()方法
2、使用apache下的PropertyUtils将对象赋予查询的结果
import org.apache.commons.beanutils.PropertyUtils;
PropertyUtils.setProperty(t, name, value);
2.处理结果方法有ResultSetMetaData的对象
调用其方法:
metaData.getColumnCount()获取结果集长度
//获取其列名或别名
String name = metaData.getColumnLabel(i+1);
//获取其列名或别名对应的值
Object value = set.getObject(name);
//将获取值放入对象中
PropertyUtils.setProperty(t, name, value);
相当于相当于 QueryRunner的ResultSetHandler的不同实现对象方法