JDBC通用的数据访问模块

JDBC通用的数据访问模块

通用数据访问模块的包结构及主要文件

在这里插入图片描述

各个文件的作用如下:

​ DatabasesUtil类:数据库连接管理类,包含创建连接,销毁连接等方法;

​ ConfigManager类:负责读取保存有数据库连接参数的配置文件,配置文件默认为存储在src根目录下的

​ database.properties文件。

​ CommonDao类:定义了通用的数据访问方法,并对查询条件和结果的处理进行封装。

​ TypeConstant类: 定义了java数据类型与数据库数据类型的对应关系,以便在CommonDao中进行自动数据处理.


database.properties文件

driver=com.mysql.cj.jdbc.Driver
#在和mysql传递数据的过程中,使用unicode编码格式,并且字符集设置为utf-8
url=jdbc\:mysql\://127.0.0.1\:3306/onlineauctionsystem?serverTimezone\=GMT%2B8&useUnicode\=true&characterEncoding\=utf-8
user=root
password=root	

ConfigManager类

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;


/**
 * 读取数据库的配置信息
 *
 */
public class ConfigManager {
    //初始化配置对象
    private static Properties properties=new Properties();

    static {
        try {
            //读取相关配置文件并返回输入流对象
            InputStream inputStream=ConfigManager.class.getClassLoader()
               .getResourceAsStream("database.properties");
            //让配置对象获取输入流的信息
            properties.load(inputStream);
            //关闭输入流
            inputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 根据key得到对应的值的方法
     * @param key
     * @return
     */
    public static String getValue(String key){
       
        return  properties.getProperty(key);
    }
}

DatabasesUtil类


import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

/**
 * 数据库连接与关闭工具类。
 */
public class DatabaseUtil {
//	获取配置文件信息
    private static String driverClassName=ConfigManager.getValue("driver");
	private static String user=ConfigManager.getValue("user");
	private static String url=ConfigManager.getValue("url");
	private static String password=ConfigManager.getValue("password");
	//初始化本地线程对象---利用get(),set()方法 获取设置当前线程对应的线程内部变量---Connection对象
	private static final ThreadLocal<Connection> threadLocal = new ThreadLocal<>();
	
    /**
	 * 获取数据库连接对象。
	 * 
	 * @throws SQLException
	 */
	public static Connection getConnection() throws SQLException {
        Connection connection = threadLocal.get(); //从当前线程中获得数据库的链接
		if(connection==null){ //如果数据库链接为null(当前线程中没有链接)
			try {
				//加载驱动,获得链接
				Class.forName(driverClassName);
				connection=DriverManager.getConnection(url,user,password);
				threadLocal.set(connection);//当前线程中保存链接
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			}
		}
		return connection;// 返回连接对象
	}
    
	/**
	 * 关闭语句容器。
	 * 
	 * @param stmt
	 *            Statement对象
	 * @param rs
	 *            结果集
	 */
	public static void closeStatement(Statement stmt, ResultSet rs) {
		try {
			if(stmt!=null){
				stmt.close();
			}
			if(rs!=null){
				rs.close();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 关闭数据库连接。
	 */
	public static void closeConnection() {
		try {
			//从当前线程中获得数据库的链接
			Connection connection = threadLocal.get();
			//将当前线程中的链接设置为null
			threadLocal.set(null);
			//当前线程中的链接不为null,关闭
			if(connection!=null){ 
				connection.close();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}

	}

}

TypeConstant类

import java.util.HashMap;
import java.util.Map;

/**
 * 实现数据库类型与Java类型的映射
 */
public class TypeConstant {
	/***
	 * 数据库类型和Java类型映射,key为数据库类型,value为Java类型
	 */
	private static Map<String, Class<?>> typeMap = new HashMap<String, Class<?>>();

	static {
		typeMap.put("BIGINT", Long.class);
		typeMap.put("INT", Integer.class);
		typeMap.put("VARCHAR", String.class);
		typeMap.put("TEXT", String.class);
		typeMap.put("DATETIME", java.util.Date.class);
		typeMap.put("DECIMAL", Double.class);
		typeMap.put("TINYINT", Integer.class);
		typeMap.put("BIT", Boolean.class);
		typeMap.put("TIMESTAMP", java.util.Date.class);
	}

	/**
	 * 新增类型
	 * @param columnType
	 * @param javaType
	 */
	public static void addType(String columnType, Class<?> javaType) {
		typeMap.put(columnType, javaType);
	}

	/**
	 * 根据数据库类型返回相应的java类型
	 * @param columnType
	 * @return
	 */
	public static Class<?> getJavaType(String columnType) {
		
		return typeMap.get(columnType);
	}
}

CommonDao类


import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

/**
 * 执行数据库操作的工具类。
 */
public class CommonDao {

	/**
	 * 增删改的通用方法
	 * @param sql	sql语句
	 * @param params	参数数组
	 * @return
	 * @throws SQLException
	 */
	public int executeUpdate(String sql, Object[] params) throws SQLException {
		Connection connection=DatabaseUtil.getConnection();//获得数据库的链接
		PreparedStatement pstmt=connection.prepareStatement(sql);	//预编译sql语句
		if(params!=null && params.length>0){
			for(int i=0;i<params.length;i++){
				pstmt.setObject(i+1,params[i]);
			}
		}
		int result= pstmt.executeUpdate();	//执行sql语句
		DatabaseUtil.closeStatement(pstmt,null);	//关闭资源
		DatabaseUtil.closeConnection();//关闭连接
		return result;
	}

	/**
	 * 增删改的通用方法
	 * @param sql sql语句
	 * @param entity  对象
	 * @return
	 * @throws SQLException
	 */
	public int executeUpdate(String sql, Object entity) throws SQLException {
		Map<String, Object[]> map=
				handleParameterMapping(sql,entity);
		//map中的key是执行的sql语句(转换后的sql语句,?占位符的sql语句)
		//map中的value是执行的sql语句对应的数组参数
		//1 map.entrySet 方法获得是 map中的key value的集合
		//2 map.entrySet().iterator()  方法获得是 map中的key value的集合的迭代器
		//3 迭代器中的每条记录都是key-value。
		//4 map.entrySet().iterator().next() 迭代器中的一条记录
		Entry<String,Object[]> entry= map.entrySet().iterator().next();

		return executeUpdate(entry.getKey(),entry.getValue());
	}


	/**
	 * 将带#{}占位符 的sql语句 转为带 ?占位符的sql语句
	 * 将entity的参数 转为 数组参数
	 * @param sql
	 * @param entity
	 * @return
	 * @throws SQLException
	 */
	protected Map<String, Object[]> handleParameterMapping(
			String sql,Object entity) throws SQLException {
		Map<String, Object[]> returnResultMap=new HashMap<>();//最后需要返回的集合以{sql-数组参数}的形式
		List<String> placeholders=new ArrayList<>(); //保存的是 sql语句中的#{..}内容
		//找出sql语句中的#{..}内容,将其保存到集合中
		int offset=0; //从sql语句的哪个位置开始查找#{
		while(true){
			int start=sql.indexOf("#{",offset);  //查找sql语句中的#{的位置
			if(start>=offset){
				int end=sql.indexOf("}",offset);  //查找sql语句中的}的位置
				String placeHolder=sql.substring(start+2,end);
				placeholders.add(placeHolder);
				offset=end+1;
			}else{ //sql语句中已经找不到#{,start的返回值是-1,退出循环
				break;
			}
		}
		Object [] values=new Object[placeholders.size()]; //传递的参数值
		//将#{}sql语句 改成?sql语句
		for(int i=0;i<placeholders.size();i++){
			String temp=placeholders.get(i); //userName
			sql=sql.replaceFirst("\\#\\{"+temp+"\\}","?");
			//entity对象中的属性的值   占位符上的值
			//占位符的名字(temp)  就是 entity对象的属性的名字(temp)
			//获得属性的值,怎么获得属性的值? 通过get方法获得属性的值
			//怎么得到get方法???
			// 找个例子:获得属性名的get方法叫作getUserName
			// 占位符的名字(属性的名字)userName
			try {
				Method getMethod= entity.getClass()
						.getMethod("get"+temp.substring(0,1).toUpperCase()
								+temp.substring(1));
				Object value=getMethod.invoke(entity);//执行get方法,获取entity对象中的参数值
				values[i]=value;//将此值存入数组中
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		returnResultMap.put(sql,values);//将拿到的sql,和参数数组都存入要返回的集合中
		return returnResultMap;
	}

	/**
	 * 查询操作
	 * 
	 * @param sql
	 *            sql语句
	 * @param params
	 *            参数数组
	 * @return 查询结果
	 * @throws SQLException
	 */
	public <T> List<T> executeQuery(Class<T> clz, String sql, Object[] params) throws SQLException {
		//获取连接
		Connection connection=DatabaseUtil.getConnection();
			//初始化PreparedStatement对象,进行预编译
		PreparedStatement preparedStatement= connection.prepareStatement(sql);

		if(params!=null && params.length>0){
			for(int i=0;i<params.length;i++){
				preparedStatement.setObject(i+1,params[i]);
			}
		}
		//执行查询操作,并返回结果集
		ResultSet resultSet=preparedStatement.executeQuery();

		try {
			//将结果集以对象存入集合的方式返回集合
			return handleResultMapping(clz,resultSet);
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}finally{
			//关闭PreparedStatement对象,但是rs对象不关闭
			DatabaseUtil.closeStatement(preparedStatement,null);
			//关闭连接
			DatabaseUtil.closeConnection();
		}
		return null;
	}

	/**
	 * sql:的占位符是#{}, entity:对象信息
	 * @param clz
	 * @param sql
	 * @param entity
	 * @param <T>
	 * @return
	 * @throws SQLException
	 */
	public <T> List<T> executeQuery(Class<T> clz, String sql, Object entity) throws SQLException {
		//将【sql-entity】格式的集合通过handleParameterMapping()方法转换为【sql-Object[]】,然后执行查询方法
		Map<String,Object[]> map= handleParameterMapping(sql,entity);
		//获取map集合中第一个sql,及其对应的值(Object[])
		sql=map.entrySet().iterator().next().getKey(); //转换后的sql语句
		Object [] params=map.entrySet().iterator().next().getValue(); //转换后的参数
		return executeQuery(clz,sql,params);
	}


	/**
	 * 将查询出来的结果集转成List集合
	 * @param clz   返回集合的元素类型
	 * @param rs
	 * @param <T>
	 * @return
	 * @throws SQLException
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws IllegalArgumentException
	 * @throws InvocationTargetException
	 */
	protected <T> List<T> handleResultMapping(Class<T> clz, ResultSet rs)
			throws SQLException, InstantiationException,
			IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		//如果目标类型是整型的,直接返回整型的集合
		if(clz.equals(Integer.class)){    //select count(1) from ......
			List<Integer> list=new ArrayList<>();
			while(rs.next()){
				list.add(rs.getInt(1));
			}
			return (List<T>)list;
		}
		//如果返回的结果不是整型的, 获得返回结果类型中所有的set方法(为啥? 因为通过set方法将查询的结果封装到对象的属性中)
		ResultSetMetaData resultSetMetaData=rs.getMetaData(); //结果集的摘要信息
		int count=resultSetMetaData.getColumnCount(); //结果集中的字段的数量
		//结果集中字段的数量就是set方法的数量(为啥?通过set方法 将查询出的字段的信息封装到对象的属性中)
		Method [] methods=new Method[count];
		//获得返回结果类型中所有set方法的参数类型
		Class [] typesClass=new Class[count]; //有多少个set方法,就有多少个参数的类型(为啥?每个set方法 都只有一个参数)

		//给set方法的数组赋值
		for(int i=0;i<count;i++){
			//假设数据库中有一个userId字段 在类中就有一个userId的属性  应该就有一个setUserId方法
			//根据数据中字段的名字  推导出 set方法的名字
			//只要知道字段的名字就可以了  推导出 set方法的名字
			//找字段(列)的名字---列名字和属性名字一致
			String columnLable=resultSetMetaData.getColumnLabel(i+1);
			//找字段的类型(为啥? 根据字段的类型找到字段对应的属性的类型)
			String columnTypeName=resultSetMetaData.getColumnTypeName(i+1);
			//根据字段的类型 获得字段类型对应的java类型
			Class javaClass=TypeConstant.getJavaType(columnTypeName);
			typesClass[i]=javaClass;//将此类型存入类型参数数组中
			try {
				//得到具体的set()方法,并放入methods数组中
				methods[i]= clz.getMethod("set"+columnLable.substring(0,1).toUpperCase()
								+columnLable.substring(1),javaClass);
			} catch (NoSuchMethodException e) {
				e.printStackTrace();
			}
		}
		//使用反射将结果集中的每条记录封装到结果对象中,将结果对象添加到集合中返回
		List<T> resultList=new ArrayList<>(); //返回的最终结果
		T instance; //每一条记录对应的 对象信息
		while(rs.next()){  //判断结果集中是否有记录
			//如果有记录,每一条记录 对应的是一个对象
			instance=clz.newInstance();

			//对象中的属性值都是默认的,将数据库中查询出来的数据赋值给对象中的属性
			//怎么给对象中的属性赋值??? 通过set方法  调用set方法
			for(int i=0;i<methods.length;i++){

				if(methods[i]==null){ //如果数组中的某个set方法为空,set方法不能执行
					continue;
				}

				//判断set方法中传递的参数的值的类型,如果是日期类型,进行一下特殊的处理
				if(typesClass[i].equals(Date.class)){
					 Date date=new Date(rs.getTimestamp(i+1).getTime());
					 methods[i].invoke(instance,date);
				}else{
				     //为啥是i,因为i是set方法的下标,set方法和字段对应的。数据库中列的下标从1开始。
					methods[i].invoke(instance,rs.getObject(i+1,typesClass[i]));
				}
			}
			//执行完赋值后,将对象添加到要返回的集合中。
			resultList.add(instance);
		}

		return resultList;
	}
}

每日一句: 学而不知道,与不学同;知而不能行,与不知同。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值