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

2023-11-06

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));
	}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java DAO代码重构(连接池方式) 的相关文章

随机推荐

  • QT中connect高级链接——指针、lambda、宏、第五参数

    1 connect使用指针 cpp view plain copy connect b2 QPushButton released this MainWidget mySlot 抬起 按钮b2时 修改按钮b2的标题 2 使用lambda表达
  • 数据治理之数据质量

    一 前言 之前我们介绍了数据资产治理类工具 数据资产管理产品架构规划设计思路 本期 我们来聊聊数据质量检测和监控的核心工具 DQC和SLA 二 基本概念 DQC 即Data Quality Control 数据质量检测 数据质量控制 一般我
  • 找了这么多毕业设计题目,反而不知道选哪个了

    1 学生宿舍管理系统 2 仓库管理系统 3 超市销售管理系统 4 工资管理系统 5 供应商管理系统 6 会员卡管理系统 7 计量管理系统 8 进销存财务管理系统 9 进销存管理系统 10 人才管理系统 11 图书管理系统 12 图书销售系统
  • java代码开发完成后,代码走查规范

    代码走查注意事项 1 不变的值 尽量写个常量类 2 尽量使用if else 不要一直if去判断 3 减少循环调用方法查询数据库 4 dao层尽量不要用逻辑 尽量在service里写业务逻辑 5 金额使用Bigdecimal类型的 0 00这
  • 对象的上转型对象

    1 定义 如果B类是A类的子类或间接子类 当用B类创建对象b并将这个对象b的引用赋给A类对象a时 如 A a a new B OR A a B b new B a b 则称A类对象a是子类B对象b的上转型对象 2 性质 对象b的上转型a的实
  • 怎么使用计算机开机关机,笔记本电脑怎么开关机_笔记本电脑正确的开关机步骤-win7之家...

    我们在使用笔记本电脑之后 都会进行开关机 看似简单的开关机 如果方法不正确的话 就会对电脑使用寿命有影响 所以正确的开关机可以帮助我们延长电脑使用寿命 那么笔记本电脑怎么开关机呢 针对这个问题 本文给大家讲述一下笔记本电脑正确的开关机步骤吧
  • 银行家舍入法(四舍六入)

    文章目录 银行家舍入法 银行家舍入法 生活中常见的计算方法就是四舍五入 但是银行家舍入法是四舍六入 五后面有非0的数字将直接向前进位 没有数字的情况下还要看前一位是偶数还是奇数 偶数舍去 奇数进位 总结为一句话就是 四舍六入五考虑 五后非零
  • 无需做任何配置!持安零信任可自动防御Web应用勒索攻击

    01 Attention 中毒终端已超2000个 近日 一则大型企业的勒索病毒事件在网络上传播 引起了广泛关注 某互联网企业财务管理软件 T 被爆出存在任意文件上传 远程代码执行的未授权访问漏洞 未授权访问漏洞 在企业内部一直以来都是非常常
  • 领域驱动模型(DDD)在美团外卖活动管理业务的应用

    什么是领域驱动模型 2004年Eric Evans 发表 领域驱动设计 软件核心复杂性应对之道 Domain Driven Design Tackling Complexity in the Heart of Software 简称Evan
  • 模仿mnist数据集制作自己的数据集

    模仿mnist数据集制作自己的数据集 最近看深度学习 用TensorFlow跑教程里的模型 发现教程里大多都是用mnist和cifar这两个数据集来演示的 想测试自己的数据集看看初步效果 于是就想套用现有的模型 将自己的数据集做成和mnis
  • 解决nginx负载均衡的session共享问题

    查了一些资料 看了一些别人写的文档 总结如下 实现nginx session的共享 PHP服务器有多台 用nginx做负载均衡 这样同一个IP访问同一个页面会被分配到不同的服务器上 如果session不同步的话 就会出现很多问题 比如说最常
  • fireFox post请求插件,火狐浏览器插件

    在开发过程中 为了测试数据 提交表单等 经常会用到post请求 在这里向大家介绍一款比较好用的浏览器插件 它可以很好的模拟post get put等常用的请求 大大便利了我们的开发 它就是fire fox中的RESTClient 安装方法如
  • Linux服务器内存消耗过高

    Linux服务器内存消耗过高 问题描述 Linux服务器的内存持续消耗过高 重启后可以恢复正常 但业务运行后问题依旧存在 而且没有明显高消耗内存进程存在 问题原因 slab消耗内存过多 解决方案 登录问题Linux服务器 执行free和to
  • 部署Oracle 19C RAC

    https www toutiao com i6879691817663595019 tt from weixin utm campaign client share wxshare count 1 timestamp 1602718612
  • 集成springSecurity遇到的跨域问题

    引言 该项目主要使用技术 sprinboot springSecurity vue 其它的技术就不介绍了 其中springSecurity是我参考网上的案例去进行的集成 虽然集成成功了 但是还不是太懂 下面就开始介绍一下我遇到的问题 问题重
  • Android开源框架之Fresco

    简介 Fresco是Facebook最新推出的一款用于Android应用中展示图片的强大图片库 可以从网络 本地存储和本地资源中加载图片 相对于ImageLoader 拥有更快的图片下载速度以及可以加载和显示gif图等诸多优势 是个很好的图
  • 医学生可以跨专业考计算机的专业,可以跨考医学研究生:2016跨专业考研需谨慎的专业解读:临床医学...

    每年的跨专业考研人群有很大一批 或是因为本专业就业不景气 或是因为不感兴趣等等 诸多原因导致跨专业考研的人很多 跨专业考研的难度比一般要大 主要因为起点不同 往往此类考生专业课的基础都很低 从头开始 压力很大 因此在选专业的时候一定要谨慎
  • python怎么输出图片_Python怎么输出图片且不保存

    Python怎么输出图片且不保存 一 输出本地图片 使用open 函数来打开图片 使用show 函数来显示图片 from PIL import Image img Image open d dog png img show 这种图片显示方式
  • 基于BP神经网络的2014世界杯比分预测

    写在前头 科学的方法 娱乐的心态 研究背景 众所周知 今年的世界杯比赛各种坑爹 看了那么多砖家点评就没人说准过 当然足球比赛中有太多的未知变量 如何选择这些变量就成为了预测比赛比分的关键 本文作者另辟蹊径 选用足彩比分赔率作为影响比赛走势的
  • Java DAO代码重构(连接池方式)

    DAO设计简化思路 首先初始化数据库连接池 使用Alibaba的Druid连接池 需先下载druid 1 x x jar包 public class JDBCUtil private static DataSource ds null 初始