问题分析
使用JDBC技术操作数据库通常要经过6个步骤:加载驱动,获得连接,获得处理命令的对象,执行sql操作,结果处理,资源关闭;每次进行不同的操作就需要重写这几个步骤,因此存在大量重复代码,为了便于使用,可以将这几个步骤单独分为不同方法组合在同一个工具类中封装起来,在别处进行数据库操作直接调用这个工具类中方法即可。
代码实现
package com.jdbc_03.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;
/**
* 自己封装的数据库工具类,包含一些操作数据库的简便方法
* 1. 获取连接
* 2. 关闭资源
* 3..
* @author mrchai
*/
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<params.length;i++){
ps.setObject(i + 1, params[i]);
}
}
//执行更新
int i = ps.executeUpdate();
return i;
} catch (SQLException e) {
e.printStackTrace();
}finally{
close(null, ps, null);
}
return 0;
}
/**
* 封装通用的查询集合操作
* @param t 返回数据的类型
* @param sql
* @param params
* @return
*/
public static <T> List<T> queryList(Class<T> t,String sql,Object...params){
//声明空集合
List<T> data = new ArrayList<>();
//获取查询结果信息
List<Map<String,Object>> list = getDataPair(sql,params);
if(list.isEmpty()){
return data;
}
for(Map<String,Object> map:list){
T obj = parseMapToBean(map,t);
data.add(obj);
}
return data;
}
/**
* 封装通用的查询对象操作
* @param t
* @param sql
* @param params
* @return
*/
public static <T> T queryOne(Class<T> t,String sql,Object...params){
List<Map<String,Object>> list = getDataPair(sql, params);
T obj = null;
if(list.size()>0){
Map<String,Object> map = list.get(0);
obj = parseMapToBean(map,t);
return obj;
}
return obj;
}
/**
* 将一个Map集合对象转换为一个JavaBean并返回
* @param map
* @param t
* @return
*/
private static <T> T parseMapToBean(Map<String, Object> map, Class<T> t) {
T obj = null;
try {
//创建一个空实例
obj = t.newInstance();
//获取Map集合的键集
Set<String> 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 m = t.getMethod(setMethodName, f.getType());
//执行方法
m.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<Map<String,Object>> getDataPair(String sql,Object...params){
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
List<Map<String,Object>> 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<String,Object> map = new HashMap<>();
//遍历每一列
for (int i = 1; i <= count; i++) {
//获取列名称
String columnName = rsmd.getColumnName(i);
//获取列值
Object value = rs.getObject(i);
if(Objects.nonNull(value)){
//将数据存入map
map.put(columnName, value);
}
}
//将Map集合存入List
list.add(map);
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
close(rs, ps, conn);
}
return list;
}
}
说明
数据库和Druid连接池的配置信息统一写在一个配置文件中,当数据库改变时就不需要修改源代码而直接修改配置文件里的配置信息即可。sql增删改操作返回的是更新的数据行数,因此可以封装在一起,而查询操作需要返回查询的结果,相对比较复杂,将查询一条记录与查询所有记录分开更符合需求,利用到集合框架泛型和反射这些高级技术避免了大量重复的代码,外界调用查询方法可以和调用更新方法一样简单。