自己实现简单的DbUtils类,模仿Apache的DbUtils核心原理

用最基本的JDBC进行数据连接的时候,效率低且违背面向对象,引入c3p0连接池进行优化处理,使用DbUtils进一步封装jDBC的过程、——自己手动实现一个简单的DbUtils类,对JDBC过程进行封装。

在对JDBC的执行过程进行分析中,我们会发现两个主要的问题:1.PreparedStatement执行sql语句,如何设置参数 2.ResultSet结果集中如何取出参数并封装到到一个对象中。经过对问题的分析,可以看出sql语句,结果集的处理以及要传递的参数是变量,而且我们不确定要传递参数的个数,数据库查询语句和数据库的更改语句的处理过程是不一样的,所以我们定义了两个方法都使用了可变参数,一个是Query数据库的查询语句,根据返回需要处理结果的不同,设计了ResultSetHandler接口,实现ResultSetHandler中的handler方法对结果集Result进行不同的处理,并返回相应的结果。BeanHandler与BeanListHandler都实现了ResultSetHandler接口,内部handler方法的实现逻辑也大体相同的、回头看需要解决的那个问题,ResultSetResultSet结果集中如何取出参数并封装到一个对象中,首先对象的类型我们不能确定下来,不过要处理的对象都符合JavaBean规范,所以使用了泛型提高了通用性,rs.getObject(columnLabel)可以解决数据库中数据映射到对象中类型不同的问题,但我们还是不能确定代表每列的字段名,但我们约定数据库中的列名与User实体类中属性名是一致的,所以我们可以获得User对象的属性名集合。而反射是将类中的各种信息映射到一个类中,这样我们需要一个传递Class<T>对象的参数,来获得类中的信息,为更方面的操作类中信息,引入java.beans包下的一些类,通过Introspector.getBeanInfo(Clz).getPeropertyDescriptors()来获取描述类中所有属性的数组,这样迭代这个数组就可以获得每个属性的名字,我们还要将获取的数据封装到对象中,我们的对象都是符合javaBean规范的,所以可以通过属性获得相应的set方法,然后通过反射调用相关方法对对象进行封装、当我们PropertyDescriptor数组进行迭代式,属性中可能可能会有父类的属性,这样会抛出异常,捕获异常,并利用continue关键字继续迭代。

DbUtils的主要实现类:

/**jdbc查询的方法(可以查单个对象,也可以查多个)
	 * @param sql:sql语句
	 * @param rsh:ResultSetHandler接口的实现类的对象
	 * @param params:查询条件对应的参数值
	 * @return 对应的javabean对象或对应List<javabean>对象
	 */
	public static <T> T query(String sql, ResultSetHandler<T> rsh,
			Object... params){
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		try {
			//获取数据库连接
			conn = getConn();
			//预编译sql语句并返回PreparedStatement对象
			ps = conn.prepareStatement(sql);
			//为占位符赋值
			for(int i = 0;i<params.length;i++){//占位符设置下标从1开始
				ps.setObject(i+1, params[i]);
			}
			//执行查询操作
			rs = ps.executeQuery();
			return rsh.handler(rs);
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}finally{
			//关闭数据连接释放资源
			close(rs, ps, conn);
		}
	}
	/**添加、修改、删除
	 * @param sql:sql语句
	 * @param params:用于为占位符赋值
	 * @return 影响的行数
	 */
	public static int update(String sql, Object... params){
		Connection conn = null;
		PreparedStatement ps = null;
		try {
			//获取数据库连接
			conn = getConn();
			//预编译sql语句并返回PreparedStatement对象
			ps = conn.prepareStatement(sql);
			//为占位符赋值
			for(int i = 0;i<params.length;i++){
				ps.setObject(i+1, params[i]);
			}
			//执行操作,并返回影响的行数
			return ps.executeUpdate();
		} catch (SQLException e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}finally{
			close(ps, conn);
		}
		
	}
对结果集的处理,ResultSetHandler是一个接口,内部只有一个Handler方法,BeanHandler与BeanListHandler都实现了ResultSetHandler接口:

BeanHandler的内部实现,BeanListHandler的内部实现原理也是大体一致的,只不过将封装的对象迭代进集合中:

public class BeanHandler<T> implements ResultSetHandler<T> {
	private Class<T> clz;
	public BeanHandler(Class<T> clz){
		this.clz = clz;
	}
	/**
	 * 从结果集rs对象获取信息,并进行封装:
	 * 封装一个JavaBean对象
	 */
	public T handler(ResultSet rs) throws Exception {
		if(rs.next()){//存在记录
			//创建JavaBean对象
			T t = clz.newInstance();
			//将Class对象clz转化成BeanInfo对象,可以属性信息逐一进行封装
			BeanInfo bi = Introspector.getBeanInfo(clz);
			PropertyDescriptor[] pds = bi.getPropertyDescriptors();
			//t  setXxx  rs.getObject(xxx);
			//t.setUsername(rs.getString("username"));
			for (PropertyDescriptor pd:pds){
				//获取属性的名称
				String name = pd.getName();
				//获取setXxx方法
				Method mtd = pd.getWriteMethod();
				//t.setXxx(rs.getObject(name)
				try{
					mtd.invoke(t, rs.getObject(name));
				}catch (SQLException e) {
					continue;
				}
			}
			return t;
		}
		return null;
	}

}
这样通过DbUtils工具类查询数据库只需两行代码:

String sql = "select * from user where username = ?";
		return DbUtils.query(sql, new BeanHandler<User>(User.class), username);

更改操作也是大体相同的用法:

String sql = "insert into user(username,nickname,password,email)" +
				" values(?, ?, ?, ?)";
		return DbUtils.update(sql, user.getUsername(),user.getNickname(),user.getPassword(),user.getEmail());


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值