JAVA数据库连接封装(jdbc)

一、前请提要

在我们开发的过程中,除了用数据库来保存我们的数据,那么,怎么来获取我们保存在数据库中的数据呢?,特别是在java代码中呢?这里我们就要用到jdbc了,但是使用发现,我们的代码重复度过高,于是,我们对这些重复的代码进行了封装,当然,封装是一个极具挑战性的任务,这里面用到类回调,用到了反射,都是Java中的难点,小编也是在几天的代码阅读思考下才理解,单独给小编封装任务,小标怕是会跑路,哈哈!话不多说,跟随小编一起来思考这个工具包。

二、概要图片

在这里插入图片描述

三、具体代码详解

package com.softeem.jdbc.utils;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.Set;

import com.alibaba.druid.pool.DruidDataSource;
import com.softeem.jdbc.entity.Account;
import com.softeem.jdbc.entity.Emp;

/**
 * 自己封装的数据库工具类,包含一些操作数据库的简便方法
 * 1. 获取连接
 * 2. 关闭资源
 * 3..
 */
public class DBUtils {

	private static String driverClass;
	private static String url;
	private static String user;
	private static String password;
	
	/**最大活动连接数*/
	private static int maxActive;
	/**最小闲置连接*/
	private static int minIdle;
	/**等待连接获取的最长时间*/
	private static long maxWait;
	
	/**druid数据源对象*/
	private static DruidDataSource dataSource;
	
	
	static{
		init();
	}
	
	public static void init(){
		try {
			Properties prop = new Properties();
			//加载属性文件
			prop.load(new FileInputStream("src/jdbc.properties"));
			//获取属性信息(连接数据库的相关字符串)
			driverClass = prop.getProperty("driverClass");
			url = prop.getProperty("url");
			user = prop.getProperty("user");
			password = prop.getProperty("password");
			
			maxActive = Integer.parseInt(prop.getProperty("maxActive"));
			minIdle = Integer.parseInt(prop.getProperty("minIdle"));
			maxWait = Long.parseLong(prop.getProperty("maxWait"));
			
			//创建数据源并配置
			dataSource = new DruidDataSource();
			dataSource.setDriverClassName(driverClass);
			dataSource.setUrl(url);
			dataSource.setUsername(user);
			dataSource.setPassword(password);
			dataSource.setMaxActive(maxActive);
			dataSource.setMinIdle(minIdle);
			dataSource.setMaxWait(maxWait);
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} 
	}
	
	/**
	 * 2.获取连接
	 * @return
	 */
	public static synchronized Connection getConn(){
		if(dataSource == null || dataSource.isClosed()){
			init();
		}
		try {
			return dataSource.getConnection();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	/**
	 * 关闭连接池
	 */
	public static synchronized void closePool(){
		if(dataSource != null){
			dataSource.close();
		}
	}
	
	/**
	 * 6.关闭资源
	 * @param rs
	 * @param stat
	 * @param conn
	 */
	public static void close(ResultSet rs,Statement stat,Connection conn){
		try {
			if(rs != null){
				rs.close();
			}
			if(stat != null){
				stat.close();
			}
			if(conn != null){
				conn.close();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 封装通用的增、删、改操作(针对任何数据库表的更新操作(DML语句)都能通过该方法实现)
	 * @param sql
	 * @return
	 */
	public static int exeUpdate(Connection conn,String sql,Object... params){
		PreparedStatement ps = null;
		try {
			//编译sql语句获取预处理对象
			ps = conn.prepareStatement(sql);
			if(Objects.nonNull(params)){				
				for(int i = 0;i List query(String sql,CallBack callback,Object... params){
		List list = null;
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		try {
			conn = getConn();
			ps = conn.prepareStatement(sql);
			if(Objects.nonNull(params)){
				for (int i = 0; i < params.length; i++) {
					ps.setObject(i + 1, params[i]); 
				}
			}
			//执行查询操作
			rs = ps.executeQuery();
			list = callback.call(rs);
		} catch (SQLException e) {
			e.printStackTrace();
		}finally{
			close(rs, ps, conn); 
		}
		return list;
	}
	
	/**
	 * 内部接口,里面包含一个函数(回调函数)
	 * @author mrchai
	 */
	@FunctionalInterface
	public interface CallBack{
		List call(ResultSet rs) throws SQLException;
	}
	
	/**
	 * 封装通用的查询集合操作,对于任何形式的查询,都能返回一个对象集合
	 * @param t  需要返回的数据类型(泛型)
	 * @param sql 目标查询语句
	 * @param params 执行查询所需的参数
	 * @return 返回包含指定对象的集合
	 */
	public static  List queryList(Class t,String sql,Object... params){
		//声明空集合
		List data = new ArrayList<>();
		//获取查询结果信息
		List> list = getDataPair(sql, params);
		if(list.isEmpty()){
			return data;
		}
		//遍历集合
		for (Map map : list) {
			T obj = parseMapToBean(map,t);
			data.add(obj);
		}
		return data;
	}
	
	/**
	 * 封装通用查询对象操作,对于任何形式的查询,都能返回一个确定的对象
	 * @param t  需要返回的数据类型(泛型)
	 * @param sql 目标查询语句
	 * @param params 执行查询所需的参数
	 * @return 返回指定对象
	 */
	public static  T queryOne(Class t,String sql,Object... params){
		List> list = getDataPair(sql, params);
		if(!list.isEmpty()){
			Map map = list.get(0);
			T obj = parseMapToBean(map,t);
			return obj;
		}
		return null;
	}
	
	/**
	 * 将一个Map集合对象转换为一个JavaBean并返回
	 * @param map
	 * @param t
	 * @return
	 */
	private static  T parseMapToBean(Map map, Class t) {
		T obj = null;
		try {
			//创建一个空实例
			obj = t.newInstance();
			//获取map集合的键集(所有列名称,即要返回对象的属性名)
			Set keys = map.keySet();
			for (String cname : keys) {
				//获取属性对象
				Field f = t.getDeclaredField(cname);
				//获取set方法的名称
				String setMethodName = "set"+cname.substring(0,1).toUpperCase()+cname.substring(1);
				//获取set方法对象
				Method method = t.getMethod(setMethodName, f.getType());
				//执行方法
				method.invoke(obj, map.get(cname));
			}
		} catch (InstantiationException | IllegalAccessException e) {
			e.printStackTrace();
		} catch (NoSuchFieldException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
		return obj;
	}

	/**
	 * 解析指定查询语句,并将获取的数据(列名,列值)以集合的形式返回
	 * @param sql
	 * @param params
	 * @return
	 */
	private static List> getDataPair(String sql,Object... params){
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		//声明集合存储获取的表数据(列名,列值)
		List> list = new ArrayList<>();
		try {
			conn = getConn();
			ps = conn.prepareStatement(sql);
			if(Objects.nonNull(params)){
				for (int i = 0; i < params.length; i++) {
					ps.setObject(i + 1, params[i]);
				}
			}
			rs = ps.executeQuery();
			//获取结果集元数据
			ResultSetMetaData rsmd = rs.getMetaData();
			//获取列总数,获取列名称,获取标签名,获取列值,将相关数据存储到map集合
			//获取查询的总列数
			int count = rsmd.getColumnCount();
			//对结果集遍历
			while(rs.next()){
				//对结果集每遍历一次,获取一条数据(即一个map对象)
				Map map = new HashMap<>();
				//遍历每一列
				for (int i = 1; i <= count; i++) {
					//获取列名称
					String columnName = rsmd.getColumnName(i); 
					//获取标签名(列别名)
					//String columnLabel = rsmd.getColumnLabel(i);
					//获取列值
					Object value = rs.getObject(i);
					//当列值不为null时才将数据存入map集合
					if(Objects.nonNull(value)){						
						//将数据存入map
						map.put(columnName, value);
					}
				}
				//将map集合存入List
				list.add(map);
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally{
			//回收资源
			DBUtils.close(rs, ps, conn);
		}
		return list;
	}
	//查询的使用方法
	public static void main(String[] args) {
		List list = queryList(Emp.class,"select eno,ename,sal from emp");
		list.forEach(e->System.out.println(e));
		
		Account a = queryOne(Account.class,"select * from account where id=?", 2);
		System.out.println(a);
	}
}

四、配置文件(properties)

#mysql connection config
driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/mydb
user=root
password=123456

#mssqlserver connection config
#driverClass=com.miscrosoft.jdbc.sqlserver.SQLServerDriver
#url=jdbc:sqlserver://127.0.0.1:1433;databaseName=test
#user=sa
#password=123456

#oracle connection config
#driverClass=oracle.jdbc.driver.OracleDriver
#url=jdbc:oracle:thin:@127.0.0.1:1521:orcl
#user=scott
#password=tiger

### Connection Pool Config
maxActive=10
minIdle=1
maxWait=10000

五、注意事项

在使用这个工具包时,一定要写配置文件,并且,配置文件里的内容要更具你的实际需求来修改,上面的配置文件包括了三种常用数据库的配置文件,我使用的是mysql配置。其次,为了方便事务完成,把数据库连接单独获取,每次在Dao层需要自己手动添加一个Connection变量,同时构造相应的函数,在server层,要获取连接,这样,我们就可以很方便的使用这个工具包了。

六、小结

这个jdbc的封装方便了我们对数据库的操作,难点有两个,反射和回调,我们可以把回调当做一个新的方法的连接桥梁,在数据连接未关闭之前调用;把反射当成一面镜子,照着镜子来刻画你自己,这样方便大家快速看懂这个封装类,千万不要想着一次性全都弄懂,达到自己可以封装的层度,要花上几天的时间,每天在脑海里思考其所以然,相信付出一定会有收获的。

你可能感兴趣的:(JAVA)