利用反射和ResultSetMetaData实现DBUtils的基本功能
DBUtils大大简化了JDBC的书写,极大的提高了开发效率,和数据库连接池一起,简化了JDBC开发的流程.简易的自定义数据库连接池可以通过装饰者设计模式和动态代理模式得到很简单的实现,那么DBUtils应该怎么实现呢?为了了解DBUtils其内部工作的流程,我实现了一个自己的DBUtils工具类,实现一些简单的更新和查询操作.
ResultSetMetaData是可以获取ResultSet对象的列类型和属性信息的对象.这个类里面有很多方法,在这个案例中,只用到两个:getColumnCount():获取ResultSet结果集中列的数目.getColumnName(int column):根据指定的列数目获取列名.有了这两个方法就可以自己动手去实现一个简易版的DBUtils啦~下面是我实现的步骤:
1.编写MyQueryRunner的executeUpdate方法.
这个方法的编写非常简单,因为可以通过dataSource获取Connection,在方法的内部就是简单的jdbc操作.需要注意的是,需要手动设置传入的参数到PreparedStatement中.代码如下:
public int update(String sql, Object... params) {
Connection connection=null;
PreparedStatement preparedStatement=null;
try {
connection=dataSource.getConnection();
preparedStatement=connection.prepareStatement(sql);
for(int i=0;i<params.length;i++) {
preparedStatement.setObject((i+1), params[i]);//设置参数.
}
int x=preparedStatement.executeUpdate();//执行更新操作.
return x;
} catch (SQLException e) {
throw new RuntimeException(e);
}
finally {
if(preparedStatement!=null) {
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(connection!=null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
2.编写MyQueryRunner的executeQuery方法.
这个方法的实现也不难,因为我们将重要的代码交给传入的ResultSetHandler实现对象来处理.代码如下:
//查询会比较麻烦.
//作出三个实现BeanHandler BeanListHandler MapListHandler
public <T> T query(String sql, ResultSetHandler<T> rsh, Object... params) {
Connection connection=null;
PreparedStatement preparedStatement=null;
ResultSet rs=null;
try {
connection=dataSource.getConnection();
preparedStatement=connection.prepareStatement(sql);
if(params!=null) {
for(int i=0;i<params.length;i++) {
preparedStatement.setObject((i+1), params[i]);
}
}
rs=preparedStatement.executeQuery();
return rsh.handle(rs);//交给处理器处理
} catch (SQLException e) {
throw new RuntimeException(e);
}
finally {
if(rs!=null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(preparedStatement!=null) {
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(connection!=null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
3.3个处理器的编写.
在MyQueryRunner实现自定义查询的实现中,我实现了三个处理器,它们分别是采用了BeanHandler,BeanListHandler,MapListHandler的实现思想,并且给出了最简单易懂(实际上是因为我水平不够= =)的实现.下面说说BeanHandler的实现流程,Handler的实现基本上都是一样的,无非是采用反射获得元素对象,并且将数据封装进去.
BeanHandler中有两个成员变量,一个T t用来作为要返回的JavaBean,声明在外面供之后封装数据使用,一个Class type对象,用来获取Class,创建JavaBean对象赋值给t,创建Field对象为t的成员变量赋值.在方法的实现中,先通过getColumnCount方法来获取列的数目,遍历每一列,通过getColumnName(int columnCount)方法获取列名,用Class对象的getField方法获取Field对象(这里JavaBean的取值一定要和数据库中相同!否则会报错),再利用Field对象的set方法赋值.当然Field对象对应的成员变量一定是私有的(JavaBean的特性.)因此需要先调用setAccessable方法才可以.具体的代码如下:
public class MyBeanHandler<T> implements ResultSetHandler{
private Class<T> type;
T t;//需要封装的JavaBean
public MyBeanHandler(Class<T> type) {
this.type=type;
}
@Override
public Object handle(ResultSet rs) throws SQLException {
try {
t=type.newInstance();
ResultSetMetaData metaData = rs.getMetaData();
int count=metaData.getColumnCount();//获取ResultSet中数据的列数
rs.next();//移动指针
//遍历获的每一列的列名,采用反射机制设置值
for(int i=1;i<=count;i++) {
String name=metaData.getColumnName(i);
Object obj=rs.getObject(i);
Field field = type.getDeclaredField(name);
field.setAccessible(true);
field.set(t, obj);//封装数据进入JavaBean
}
} catch (Exception e) {
e.printStackTrace();
}
return t;
}
}
MyBeanListHandler的实现步骤,基本和BeanHandler一致.唯一的区别就是采用结果集的next方法遍历每一条记录,而将对象的创建移动到了循环里,这里考虑返回的List对象的增删应该比较少因此采用的是ArrayList.具体实现如下:
public class MyBeanListHandler<T> implements ResultSetHandler<List<T>>{
private Class<T> type;
List<T> list=new ArrayList<T>();//可能获取比较多.因此采用ArrayList
public MyBeanListHandler(Class<T> type) {
this.type = type;
}
@Override
public List<T> handle(ResultSet rs) throws SQLException {
try {
ResultSetMetaData metaData = rs.getMetaData();
int count=metaData.getColumnCount();
while(rs.next()) {
T t=type.newInstance();
for(int i=1;i<=count;i++) {
String name=metaData.getColumnName(i);//该方法获取列名.获取一系列字段名称.例如name,age...
Object obj=rs.getObject(i);//获取字段值
Field field = type.getDeclaredField(name);//获取field对象
field.setAccessible(true);
field.set(t, obj);//设置值
}
list.add(t);
}
return list;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
MapList看上去似乎最复杂,但是实际上实现起来却由于没有采用泛型和反射,是最方便的,代码如下:
public class MyMapListHandler implements ResultSetHandler<List<Map<String, Object>>> {
private List<Map<String,Object>> data=new ArrayList<>();
@Override
public List<Map<String, Object>> handle(ResultSet rs) throws SQLException {
ResultSetMetaData metaData = rs.getMetaData();
int count=metaData.getColumnCount();
while(rs.next()) {
Map<String,Object> map=new HashMap<>();
for(int i=1;i<=count;i++) {
Object value=rs.getObject(i);
String name=metaData.getColumnName(i);
map.put(name, value);
}
data.add(map);
}
return data;
}
}
利用反射和ResultSetMetaData实现DBUtils的基本功能的更多相关文章
- <五>JDBC_利用反射及JDBC元数据编写通用的查询方法
此类针对javaBean类写了一个通用的查询方法,List<javaBean> 通用查询更新中...:通过学习,深刻体会到学会反射就等于掌握了java基础的半壁江山! 一.使用JDBC驱动 ...
- 利用反射及JDBC元数据编写通用查询方法
元数据:描述数据的数据,ResultSetMetaData是描述ResultSet的元数据对象,从它可以得到数据集有多少了,每一列的列名... ResultSetMetaData可以通过ResultS ...
- 利用反射及jdbc元数据实现通用的查询方法
---------------------------------------------------------------------------------------------------- ...
- JDBC学习笔记(5)——利用反射及JDBC元数据编写通用的查询方法
JDBC元数据 1)DatabaseMetaData /** * 了解即可:DatabaseMetaData是描述数据库的元数据对象 * 可以由Connection得到 */ 具体的应用代码: @Te ...
- 【转】JDBC学习笔记(5)——利用反射及JDBC元数据编写通用的查询方法
转自:http://www.cnblogs.com/ysw-go/ JDBC元数据 1)DatabaseMetaData /** * 了解即可:DatabaseMetaData是描述数据库的元数据对象 ...
- Java -- JDBC_利用反射及 JDBC 元数据编写通用的查询方法
先利用 SQL 进行查询,得到结果集: 利用反射创建实体类的对象:创建对象: 获取结果集的列的别名: 再获取结果集的每一列的值, 结合 3 得到一个 Map,键:列的别名,值:列的值: 再利用反射为 ...
- Java JDBC利用反射技术将查询结果封装为对象
1.JDBC将返回结果集封装成对象demo class JdbcDemo { /** * 获取数据库列名 * @param rs * @return */ private static String[ ...
- MYSQL 之 JDBC(六): 增删改查(四)利用反射及JDBC元数据编写通用的查询
1.先利用SQL进行查询,得到结果集2.利用反射创建实体类的对象:创建Student对象3.获取结果集的列的别名:idCard.studentName4.再获取结果集的每一列的值,结合3得到一个Map ...
- 【转】利用反射快速给Model实体赋值
原文地址:http://blog.csdn.net/gxiangzi/article/details/8629064 试想这样一个业务需求:有一张合同表,由于合同涉及内容比较多所以此表比较庞大,大概有 ...
随机推荐
- Mac配置
1.显示Mac隐藏文件的命令: defaults write com.apple.finder AppleShowAllFiles -bool true 2.Mac键盘如何开启键盘上F1 - F12功 ...
- 使用TarOutputStream出现 request to write '1024' bytes exceeds size in header错误的解决方法
因为测试流程中,所测客户端会根据服务器A返回的response决定发送给服务器B的请求里各参数的值,所以现在需要模拟服务器的响应.而这个项目服务器A的响应式返回一个流,一个GZIP压缩格式流,压缩的是 ...
- iOS开发中常用到的宏定义
1.首次启动判断: #define First_Launched @"firstLaunch" 2.ios7系统判断: #define IsIOS7 ([[[UIDevice cu ...
- ansible使用笔记
ansible使用笔记 介绍 ansible 是一个模型驱动的配置管理器,支持多节点发布.远程任务执行.默认使用 SSH 进行远程连接.无需在被管理节点上安装附加软件,可使用各种编程语言进行扩展.an ...
- oracle从零开始学习笔记 二
多表查询 等值连接(Equijoin) select ename,empno,sal,emp.deptno from emp,dept where dept.deptno=emp.deptno; 非等 ...
- angularJs禁用或启用输入框指令ng-disabled="true"
ng-disabled 指令设置表单输入字段的 disabled 属性(input, select, 或 textarea). 如果 ng-disabled 中的表达式返回 true 则表单字段将被禁 ...
- Web Project犯错误!
创建一个Web Project关于数据库连接,输入doGet中调用req.setCharacterEncoding("utf-8)错误输成uft-8 经常忘记HttpServletReque ...
- Inside Flask - signal 信号机制
Inside Flask - signal 信号机制 singal 在平常的 flask web 开发过程中较少接触到,但对于使用 flask 进行框架级别的开发时,则必须了解相关的工作机制.flas ...
- cocos2dx 3.x(场景(层)的生命周期)
//进入当前层初第一步始化层调用 bool GameScence::init() { if( !void init() ) { returnfalse; } log("进入当前层初第一步始化 ...
- 移动端html页面布局
记录一下: ---------- 如果设计稿尺寸为 : 96px: 1. rem html{ font-size:62.5%; } 的情况下 : 4.8rem: 2. rem html{ font-s ...