重新封装JDBC的连接、关闭资源、查询数据、更新数据 - 利用内省、反射、元数据 -- 强烈建议看一下自我感觉挺有用的

10 篇文章 0 订阅

1. 数据库连接、关闭工具类 - JDBCUtil

这里没用到连接池,如果用连接池,可查看我以前写的文章,其实代码都差不多。改一下类,导一下包就行了。

import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

import org.junit.Test;

// 数据库连接、关闭工具类
public class JDBCUtil {
	
	// 类加载时自动运行静态代码块读取配置文件
	private static String url;
	private static String userName;
	private static String password;
	static {
		try {
			// 1. 加载配置文件
			Properties properties = new Properties();
			properties.load(JDBCUtil.class.getClassLoader().getResourceAsStream("DB.properties"));
			
			// 2. 加载驱动
			Class.forName(properties.getProperty("className"));
			
			// 3. 初始化成员属性
			url = properties.getProperty("url");
			userName = properties.getProperty("userName");
			password = properties.getProperty("password");
			
			
		} catch (IOException |  ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
	
	// 返回一个数据库连接
	public static Connection getConnection() {
		try {
			return DriverManager.getConnection(url, userName, password);
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	// 关闭数据库连接、SQL对象、结果集的资源
	public static void close(ResultSet rs, Statement statement, Connection conn) {
		
			try {
				if(rs != null) {
					rs.close();
				}
				
				if(statement != null) {
					statement.close();
				}
				if(conn != null) {
					conn.close();
				}
				
			} catch (SQLException e) {
				e.printStackTrace();
			}
		
	}
	
}

2. 内省JavaBean工具类 - BeanUtil

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.sql.Date;
import java.util.HashMap;
import java.util.Map;

public class BeanUtil {
	
	// 获取JavaBean的  所有  属性名:set属性名() 的键值对
	public static Map<String, Method> getFields(Class<?> beanType) {
		
		Map<String, Method> map = new HashMap<String, Method>();
		
		try {
			
			//利用内省获取  BeanInfo 即  Bean的所有信息对象
			BeanInfo beanInfo = Introspector.getBeanInfo(beanType, Object.class);
			
			// 根据  BeanInfo对象  获取 关于  属性的描述器  - 可从中获取  get、set方法、属性的类型 这三个重要方法
			PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
			
			// JavaBean有很多个属性,每个属性对应一个描述器
			for(PropertyDescriptor propertyDescriptor : propertyDescriptors) {
				
				// 获取属性名
				String beanPropertyName = propertyDescriptor.getName().toLowerCase();
				
				// 获取 set属性() 的方法Method对象
				Method writeMethod = propertyDescriptor.getWriteMethod();
				
				// 将上述两者放入 HashMap中
				map.put(beanPropertyName, writeMethod);
				
			}
			
		} catch (IntrospectionException e) {
			e.printStackTrace();
		}
		
		return map;
	}
	
	// 将 Util.Date 装为  数据库的sql.Date
	public static Date invertToSQLData(java.util.Date date) {
		if(date == null) {
			throw new RuntimeException("能别搞笑吗?我不需要空值");
		}
		return new Date(date.getTime());
	}
	
	
	// 针对Oracle进行类型转换 -- 转为JavaBean的属性类型
	public static Object invertToBeanAttributeType(Object value) {
		
		if(value instanceof BigDecimal) {
			value = ((BigDecimal) value).intValue();
		}else if ( value instanceof java.sql.Date) {
			value = new java.util.Date(((Date) value).getTime());
		}
		return value;
		
	}
	
}

3. 数据CRUD工具类 - 增删改查 - CURDUtil

考虑事务,其实这就数据库的Connection由程序代码的使用者自己关闭,数据的提交也由程序代码的使用者自己commit

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.junit.Test;

import top.linruchang.TestBean.Dept;

// 增删改查类
public class CRUDUtil {

	/**
	 * 
	 * @Description 不用处理事务,用于对表的更改 - DML
	 * @param sql  传入SQL语句 - DML语句
	 * @param args where的条件值
	 * @return -1表示并没有操纵表,1表示成功操纵表
	 */
	public static int update(String sql, Object... args) {

		Connection conn = JDBCUtil.getConnection();
		PreparedStatement prepareStatement = null;

		try {
			prepareStatement = conn.prepareStatement(sql);
			ParameterMetaData parameterMetaData = prepareStatement.getParameterMetaData();

			// 1. 判断sql参数与实参个数是否相同
			int fillValueCount = parameterMetaData.getParameterCount();
			if (fillValueCount != args.length) {
				throw new RuntimeException("SQL语句的填充数跟传入参数个数不符");
			}

			// 2.填充SQL语句
			for (int i = 0; i < args.length; i++) {
				prepareStatement.setObject(i + 1, args[i]);
			}

			// 3.执行SQL语句
			return prepareStatement.executeUpdate();
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			JDBCUtil.close(null, prepareStatement, conn);
		}

		return -1;
	}

	/**
	 * 
	 * @Description 考虑事务,用于对表的更改 - DML
	 * @param sql  传入SQL语句 - DML语句
	 * @param args where的条件值
	 * @return -1表示并没有操纵表,1表示成功操纵表
	 */
	public static int update(Connection conn, String sql, Object... args) {

		PreparedStatement prepareStatement = null;

		try {
			prepareStatement = conn.prepareStatement(sql);
			ParameterMetaData parameterMetaData = prepareStatement.getParameterMetaData();

			// 1. 判断sql参数与实参个数是否相同
			int fillValueCount = parameterMetaData.getParameterCount();
			if (fillValueCount != args.length) {
				throw new RuntimeException("SQL语句的填充数跟传入参数个数不符");
			}

			// 2.填充SQL语句
			for (int i = 0; i < args.length; i++) {
				prepareStatement.setObject(i + 1, args[i]);
			}

			// 3.执行SQL语句
			return prepareStatement.executeUpdate();
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			JDBCUtil.close(null, prepareStatement, null);
		}

		return -1;
	}

	/**
	 * 
	 * @Description 不考虑事务,查询数据,返回一个容器 - 不同的隔离级别有对数据行进行锁定,返回数据不一致
	 * @param beanType 传入JavaBean的反射对象
	 * @param SQL      查询SQL语句 - DQL
	 * @param args     where的条件值
	 * @return 返回List<T>容器对象
	 */
	public static <T> List<T> query(Class<T> beanType, String SQL, Object... args) {

		// 1. 获取数据库连接
		Connection conn = JDBCUtil.getConnection();
		PreparedStatement statement = null;
		ResultSet rs = null;

		// 2. 用来存储查找到的 行记录 Bean对象
		List<T> lists = new ArrayList<T>();

		try {
			// 3. 编译SQL语句 - 还没编译,代码运行时在编译
			statement = conn.prepareStatement(SQL);

			// 4. 获取 PrepareStatement的元数据,用于查看SQL语句中有多少个填充符。
			ParameterMetaData parameterMetaData = statement.getParameterMetaData();
			int fillValueCount = parameterMetaData.getParameterCount();
			if (fillValueCount != args.length) {
				throw new RuntimeException("SQL语句的填充数跟传入参数个数不符");
			}

			// 5. 填充SQL语句
			for (int i = 0; i < args.length; i++) {
				statement.setObject(i + 1, args[i]);
			}

			// 6. 执行SQL语句,并且得到结果集的元数据,用于获取 列名、以及 列数
			rs = statement.executeQuery();
			ResultSetMetaData metaData = rs.getMetaData();

			// 7. 进行行记录的Bean封装
			while (rs.next()) {

				// 8.利用反射生成一个 JavaBean对象
				T beanObj = beanType.newInstance();

				// 9. 获取该JavaBean所有的set方法
				Map<String, Method> map = BeanUtil.getFields(beanType);
				String columnName = null;
				for (int i = 0; i < metaData.getColumnCount(); i++) {
					// 10. 获取表在数据库的列名, getColumnLabel可能获取到查询语句的列别名,故不使用这个
					columnName = metaData.getColumnName(i + 1).toLowerCase();

					// 11. 查看Map里面是否有该列的set方法,用于设置字段
					if (!map.containsKey(columnName)) {
						throw new RuntimeException("可能你Bean字段、set方法写错了,请检查一下");
					}

					// 12. 获取列值,下面的if判断是用于oracle数据的number类型,我没有调试过其他数据库。读者可以自行在这基础上进行优化
					Object value = rs.getObject(columnName);
					value = BeanUtil.invertToBeanAttributeType(value);
					
					// 13. 调用对应字段的set属性方法,设置 JavaBean对象
					map.get(columnName).invoke(beanObj, value);

				}

				lists.add(beanObj);
			}

		} catch (SQLException | InstantiationException | IllegalAccessException | IllegalArgumentException
				| InvocationTargetException e) {
			e.printStackTrace();
		} finally {

			// 14. 关闭连接
			JDBCUtil.close(rs, statement, conn);
		}

		// 15. 返回javaBean结果集
		return lists;
	}

	/**
	 * 
	 * @Description 考虑事务,查询数据,返回一个容器
	 * @param beanType 传入JavaBean的反射对象
	 * @param SQL      查询SQL语句 - DQL
	 * @param args     where的条件值
	 * @return 返回List<T>容器对象
	 */
	public static <T> List<T> query(Connection conn, Class<T> beanType, String SQL, Object... args) {
		
		PreparedStatement statement = null;
		ResultSet rs = null;

		// 2. 用来存储查找到的 行记录 Bean对象
		List<T> lists = new ArrayList<T>();

		try {
			// 3. 编译SQL语句 - 还没编译,代码运行时在编译
			statement = conn.prepareStatement(SQL);

			// 4. 获取 PrepareStatement的元数据,用于查看SQL语句中有多少个填充符。
			ParameterMetaData parameterMetaData = statement.getParameterMetaData();
			int fillValueCount = parameterMetaData.getParameterCount();
			if (fillValueCount != args.length) {
				throw new RuntimeException("SQL语句的填充数跟传入参数个数不符");
			}

			// 5. 填充SQL语句
			for (int i = 0; i < args.length; i++) {
				statement.setObject(i + 1, args[i]);
			}

			// 6. 执行SQL语句,并且得到结果集的元数据,用于获取 列名、以及 列数
			rs = statement.executeQuery();
			ResultSetMetaData metaData = rs.getMetaData();

			// 7. 进行行记录的Bean封装
			while (rs.next()) {

				// 8.利用反射生成一个 JavaBean对象
				T beanObj = beanType.newInstance();

				// 9. 获取该JavaBean所有的set方法
				Map<String, Method> map = BeanUtil.getFields(beanType);
				String columnName = null;
				for (int i = 0; i < metaData.getColumnCount(); i++) {
					// 10. 获取表在数据库的列名, getColumnLabel可能获取到查询语句的列别名,故不使用这个
					columnName = metaData.getColumnName(i + 1).toLowerCase();

					// 11. 查看Map里面是否有该列的set方法,用于设置字段
					if (!map.containsKey(columnName)) {
						throw new RuntimeException("可能你Bean字段、set方法写错了,请检查一下");
					}

					// 12. 获取列值,下面的if判断是用于oracle数据的number类型,我没有调试过其他数据库。读者可以自行在这基础上进行优化
					Object value = rs.getObject(columnName);
					value = BeanUtil.invertToBeanAttributeType(value);

					// 13. 调用对应字段的set属性方法,设置 JavaBean对象
					map.get(columnName).invoke(beanObj, value);

				}

				lists.add(beanObj);
			}

		} catch (SQLException | InstantiationException | IllegalAccessException | IllegalArgumentException
				| InvocationTargetException e) {
			e.printStackTrace();
		} finally {

			// 14. 关闭连接
			JDBCUtil.close(rs, statement, null);
		}

		// 15. 返回javaBean结果集
		return lists;
	}
	
}

4. 总结

封装之后我们到时查询、修改表数据的时候,只需要提供 SQL语句、以及填充的参数即可以了

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值