Java DAO代码重构(连接池方式)

DAO设计简化思路

首先初始化数据库连接池(使用Alibaba的Druid连接池,需先下载druid-1.x.x.jar包)

public class JDBCUtil {
    private static DataSource ds = null; 
	//初始化数据库连接池
    static {
    	try {
    		Properties p = new Properties();
			InputStream input = new FileInputStream("sources/db.properties");
			p.load(input);
			ds = DruidDataSourceFactory.createDataSource(p);
		} catch (Exception e) {
			e.printStackTrace();
		}
    }
    
    /**
     * 获取数据库连接对象
     * @return 数据库连接对象
     */
    public static Connection getConnection() {
    	Connection con = null;
    	try {
    		con = ds.getConnection();
		} catch (Exception e) {
			e.printStackTrace();
		}
    	return con;
    }
    
    /**
     * 释放资源
     * @param con
     * @param st
     * @param re
     */
    public static void release(Connection con,Statement st,ResultSet re) {
    	try {
    		if(con!=null) {
    			con.close();
        	}
    		if(st!=null) {
    			st.close();
        	}
    		if(re!=null) {
    			re.close();
        	}
    	}catch (SQLException e) {
			e.printStackTrace();
		}
    }
}

随后建立一个AbstractDAO抽象类作为DAO实现类的父类,并设置泛型表示要操作的VO类

public abstract class AbstractDAO<T>{
}

抽象出execute方法,接受sql语句和可变参数,返回sql的执行结果,原理如下图:
execute方法
此时子类DAO进行增加、修改、删除时就可以直接调用父类的execute方法,传入自己编写的SQL语句,并通过可变参数的形式为预处理语句设置值,示例代码如下。

public abstract class AbstractDAO<T> {
	private Connection con;
	private PreparedStatement pstmt;
	private ResultSet re;
	
	public AbstractDAO() {
		
	}
	
	/**
	 * 通过可变参数的形式设置数据库语句并执行,适用于新增、修改和删除
	 * @param sql 预处理语句
	 * @param params 预处理语句的预留参数
	 * @return 执行成功返回true,否则返回false
	 * @throws SQLException
	 */
	public boolean execute(String sql,Object...params) throws SQLException{
		try {
			//取得JDBC连接对象
			this.con = JDBCUtil.getConnection();
			this.pstmt = this.con.prepareStatement(sql);
			//循环遍历设置预处理语句的预留参数
			for(int x=0;x<params.length;x++) {
				this.pstmt.setObject(x+1,params[x]);
			}
			return this.pstmt.executeUpdate()>0;
		}catch(SQLException e) {
			throw e;
		}finally {
			//释放资源
			JDBCUtil.release(this.con,this.pstmt,this.re);
		}
	}
}

查询方法的抽象要复杂一点,因为我们查询出的数据要以简单Java类的形式返回,而父类不知道要将数据库返回的数据封装为什么类型,这时我们可以通过泛型来实现。将查询的数据封装为子类传入的类型,方法如下,调用此方法就可以得到传入泛型的实例化对象。

/**
	 * 使用泛型反射实例化对象
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private T getInstanceOfT(){
        ParameterizedType superClass = (ParameterizedType) getClass().getGenericSuperclass();
        Class<T> type = (Class<T>) superClass.getActualTypeArguments()[0];
        try{
            return type.newInstance();
        }
        catch (Exception e){
            throw new RuntimeException(e);
        }
    }

即使得到了要封装的对象,我们怎么为对象的每一个属性都赋值呢(AbstractDAO为工具类,每一个DAO实现类都可以继承它来简化开发,因此传入的泛型一定会不同,要封装对象里面的属性更不同)这就要用到“内省”。
在这里插入图片描述
内省的本质是通过反射来操作JavaBean中的属性,得到了要封装类的属性,查询的操作就简单了。与增加、修改、删除不同,JDBC的查询操作会返回一个ResultSet数据集用来存储查询得到的数据。数据的保存就是在ResultSet数据集中读取数据然后设置到返回VO对象的属性值中(JavaBean中的属性名称应与数据库存储字段名称相同,数据类型也应该相同。否则取不到值或产生异常)。
query方法
利用Introspector类中的getBeanInfo(A.class,B.class)方法可以取得字节码中的属性信息,返回为BeanInfo类型,保存A类及A类的父类中的所有属性信息,但不包括B类(包括B类的父类)中的属性信息。(假设A类继承B类,B类继承C类,我们使用Introspector.getBeanInfo(A.class,B.class)只能够得到A类中的所有属性,而使用Introspector.getBeanInfo(A.class,C.class)则可以得到A类和B类中的所有属性)。
利用BeanInfo中的getPropertyDescriptors()方法可以得到具体属性描述器的集合,返回为PropertyDescriptor类型,其可以获得属性名、set方法、get方法等。取得属性名称后,我们直接利用属性名称作为索引值在返回的ResultSet数据集中取值。再通过set方法.invoke(对象名称,参数)赋值给对象。具体实现代码如下:

/**
	 * 查询单条数据
	 * @param sql 预处理语句
	 * @param params 预处理语句的预留参数
	 * @return 将查询结果封装在VO类中返回
	 * @throws Exception
	 */
	public T query(String sql,Object...params) throws Exception{
		T t = null;
		try {
			this.con = JDBCUtil.getConnection();
			this.pstmt = this.con.prepareStatement(sql);
			//循环遍历设置预处理语句的预留参数
			for(int x=0;x<params.length;x++) {
				this.pstmt.setObject(x+1,params[x]);
			}
			//查询结果集
			this.re = this.pstmt.executeQuery();
			if(this.re.next()) {
				//反射实例化VO类对象
				t = getInstanceOfT();
				//取得指定字节码的属性信息
				BeanInfo beanInfo = Introspector.getBeanInfo(t.getClass(),Object.class);
				//取得所有的属性描述器
				PropertyDescriptor[] dps = beanInfo.getPropertyDescriptors();
				for(PropertyDescriptor dp:dps) {
				    //以属性名称为索引,再ResultSet结果集中取值
					Object value = this.re.getObject(dp.getName());
					//通过set方法将取出的值设置在t的同名属性中
					dp.getWriteMethod().invoke(t,value);
				}
			}
			return t;
		}catch(Exception e) {
			throw e;
		}finally {
			JDBCUtil.release(this.con,this.pstmt,this.re);
		}
	}

该方法实现后,子类就可以通过调用此方法进行查询操作,避免了查询操作的重复代码,我们只需自己写好SQL语句然后调用super.query传入参数即可取得查询结果,调用示例如下:

public class UserDAOImpl extends AbstractDAO<User> implements IUserDAO {
    @Override
	public User findById(String id) throws Exception {
		String sql = "SELECT bank_customer_id,bank_customer_pwd,bank_customer_name,"
				+ "bank_customer_email,bank_customer_phone,bank_customer_type"
				+ " FROM bank_customer_auth WHERE bank_customer_id=?";
		return super.query(sql,id);
	}
}

虽然以上方法确实可以完成查询功能,但只能查询单条数据,如果想要查询多条数据,则应返回List集合,具体代码如下:

/**
     * 查询多条数据
     * @param sql 预处理语句
     * @param params 预处理语句的预留参数
     * @return 将查询结果封装在List集合中返回
     * @throws Exception
     */
	public List<T> queryList(String sql,Object... params) throws Exception {
		List<T> all = new ArrayList<T>();
		T t = null;
		try {
			this.con = JDBCUtil.getConnection();
			this.pstmt = this.con.prepareStatement(sql);
			//循环遍历设置预处理语句的预留参数
			for(int x=0;x<params.length;x++) {
				this.pstmt.setObject(x+1,params[x]);
			}
			//查询结果集
			this.re = this.pstmt.executeQuery();
			while(this.re.next()) {
				//反射实例化VO类对象
				t = getInstanceOfT();
				BeanInfo beanInfo = Introspector.getBeanInfo(t.getClass(),Object.class);
				PropertyDescriptor[] dps = beanInfo.getPropertyDescriptors();
				for(PropertyDescriptor dp:dps) {
					Object value = this.re.getObject(dp.getName());
					dp.getWriteMethod().invoke(t,value);
				}
				all.add(t);
			}
		}catch(Exception e) {
			throw e;
		}finally {
			JDBCUtil.release(this.con,this.pstmt,this.re);
		}
		return all;
	}

同单次查询类似,子类就可以通过调用此方法进行多组数据的查询操作:

@Override
	public List<User> findAll() throws Exception {
		String sql = "SELECT bank_customer_id,bank_customer_pwd,bank_customer_name,"
				+ "bank_customer_email,bank_customer_phone,bank_customer_type"
				+ " FROM bank_customer_auth";
		return super.queryList(sql);
	}

总结:DAO代码的重构在于提取出重复的代码然后建立一种模板,使其适用于任意的CRUD。
以上代码汇总
使用Apache公司的DBUtils工具可以更好实现上述所有功能。DBUtils官网下载
在这里插入图片描述
DBUtils的使用非常简单,主要是依靠数据库连接池提供的数据源来进行操作。我们改写数据库连接工具类,加入getDs()方法取得DataSource对象。

public class JDBCUtil {
    private static DataSource ds = null; 
	//初始化数据库连接池
    static {
    	try {
    		Properties p = new Properties();
			InputStream input = new FileInputStream("sources/db.properties");
			p.load(input);
			ds = DruidDataSourceFactory.createDataSource(p);
		} catch (Exception e) {
			e.printStackTrace();
		}
    }
    /**
     * 获取数据源
     * @return 
     */
    public static DataSource getDs() {
    	return ds;
    }
}

在具体的DAO实现类中通过QueryRunner对象的update()方法进行数据库的增加、修改、删除操作,通过query()方法进行查询操作。实例如下:
新增操作

    @Override
	public boolean doCreate(User vo) throws SQLException {
		String sql = "INSERT INTO bank_customer_auth (bank_customer_id,"
				+ "bank_customer_pwd,bank_customer_name,bank_customer_email,"
				+ "bank_customer_phone,bank_customer_type) VALUES (?,?,?,?,?,?)";
		if(vo!=null) {
			QueryRunner qr = new QueryRunner(JDBCUtil.getDs());
			return qr.update(sql,vo.getBank_customer_id(),vo.getBank_customer_pwd(),
					vo.getBank_customer_name(),vo.getBank_customer_email(),
					vo.getBank_customer_phone(),vo.getBank_customer_type())>0;
		}
		return false;
	}

查看单条记录

    @Override
	public User findById(String id) throws SQLException {
		String sql = "SELECT bank_customer_id,bank_customer_pwd,bank_customer_name,"
				+ "bank_customer_email,bank_customer_phone,bank_customer_type"
				+ " FROM bank_customer_auth WHERE bank_customer_id=?";
		QueryRunner qr = new QueryRunner(JDBCUtil.getDs());
		return qr.query(sql,new BeanHandler<User>(User.class),id);
	}

查看多条记录

    @Override
	public List<User> findAll() throws SQLException {
		String sql = "SELECT bank_customer_id,bank_customer_pwd,bank_customer_name,"
				+ "bank_customer_email,bank_customer_phone,bank_customer_type"
				+ " FROM bank_customer_auth";
		QueryRunner qr = new QueryRunner(JDBCUtil.getDs());
		return qr.query(sql,new BeanListHandler<User>(User.class));
	}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值