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语句、以及填充的参数即可以了