JDBC工具类封装详解
JDBC工具类封装详解
1. 工具类设计目标
核心目标
- 代码复用:避免重复的JDBC代码
- 资源管理:确保连接、语句、结果集的正确关闭
- 异常处理:统一的异常处理机制
- 配置管理:集中管理数据库连接参数
- 事务支持:简化事务操作
2. 基础工具类实现
2.1 配置文件管理
db.properties:
# 数据库连接配置
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/bank_db?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
jdbc.username=root
jdbc.password=123456# 连接池配置
jdbc.initialSize=5
jdbc.maxActive=20
jdbc.maxWait=3000
2.2 基础工具类
package com.bank.util;import java.io.InputStream;
import java.sql.*;
import java.util.Properties;/*** JDBC工具类 - 基础版本*/
public class JDBCUtil {private static String driver;private static String url;private static String username;private static String password;// 静态代码块,类加载时初始化配置static {try {// 1. 加载配置文件Properties props = new Properties();InputStream is = JDBCUtil.class.getClassLoader().getResourceAsStream("db.properties");props.load(is);// 2. 读取配置driver = props.getProperty("jdbc.driver");url = props.getProperty("jdbc.url");username = props.getProperty("jdbc.username");password = props.getProperty("jdbc.password");// 3. 注册驱动Class.forName(driver);} catch (Exception e) {throw new RuntimeException("JDBC工具类初始化失败", e);}}/*** 获取数据库连接*/public static Connection getConnection() throws SQLException {return DriverManager.getConnection(url, username, password);}/*** 关闭连接*/public static void close(Connection conn) {if (conn != null) {try {conn.close();} catch (SQLException e) {e.printStackTrace();}}}/*** 关闭语句*/public static void close(Statement stmt) {if (stmt != null) {try {stmt.close();} catch (SQLException e) {e.printStackTrace();}}}/*** 关闭结果集*/public static void close(ResultSet rs) {if (rs != null) {try {rs.close();} catch (SQLException e) {e.printStackTrace();}}}/*** 关闭所有资源*/public static void close(Connection conn, Statement stmt, ResultSet rs) {close(rs);close(stmt);close(conn);}/*** 关闭连接和语句*/public static void close(Connection conn, Statement stmt) {close(stmt);close(conn);}
}
3. 增强版工具类(推荐)
3.1 支持连接池的工具类
package com.bank.util;import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;/*** JDBC工具类 - 增强版(支持连接池)*/
public class JDBCUtil {private static DataSource dataSource;private static ThreadLocal<Connection> threadLocal = new ThreadLocal<>();static {try {// 1. 加载配置文件Properties props = new Properties();InputStream is = JDBCUtil.class.getClassLoader().getResourceAsStream("db.properties");props.load(is);// 2. 创建连接池dataSource = DruidDataSourceFactory.createDataSource(props);} catch (Exception e) {throw new RuntimeException("数据库连接池初始化失败", e);}}/*** 获取数据库连接(从连接池)*/public static Connection getConnection() throws SQLException {// 首先从ThreadLocal中获取,支持事务Connection conn = threadLocal.get();if (conn != null) {return conn;}return dataSource.getConnection();}/*** 获取数据源*/public static DataSource getDataSource() {return dataSource;}/*** 开启事务*/public static void beginTransaction() throws SQLException {Connection conn = threadLocal.get();if (conn != null) {throw new SQLException("已经开启事务,不能重复开启");}conn = getConnection();conn.setAutoCommit(false);threadLocal.set(conn);}/*** 提交事务*/public static void commitTransaction() throws SQLException {Connection conn = threadLocal.get();if (conn == null) {throw new SQLException("没有开启事务,不能提交");}try {conn.commit();} finally {conn.setAutoCommit(true);close(conn);threadLocal.remove();}}/*** 回滚事务*/public static void rollbackTransaction() throws SQLException {Connection conn = threadLocal.get();if (conn == null) {throw new SQLException("没有开启事务,不能回滚");}try {conn.rollback();} finally {conn.setAutoCommit(true);close(conn);threadLocal.remove();}}/*** 关闭连接(特殊处理事务连接)*/public static void close(Connection conn) {// 如果是事务连接,不真正关闭,由事务方法管理if (threadLocal.get() != null) {return;}if (conn != null) {try {conn.close();} catch (SQLException e) {e.printStackTrace();}}}// 其他close方法保持不变...
}
4. 数据库操作模板类
4.1 查询模板
/*** 数据库操作模板类*/
public class JDBCTemplate {/*** 执行查询(带结果集处理)*/public static <T> T executeQuery(String sql, ResultSetHandler<T> handler, Object... params) {Connection conn = null;PreparedStatement ps = null;ResultSet rs = null;try {conn = JDBCUtil.getConnection();ps = conn.prepareStatement(sql);// 设置参数setParameters(ps, params);rs = ps.executeQuery();return handler.handle(rs);} catch (SQLException e) {throw new RuntimeException("数据库查询失败", e);} finally {JDBCUtil.close(conn, ps, rs);}}/*** 执行更新(INSERT/UPDATE/DELETE)*/public static int executeUpdate(String sql, Object... params) {Connection conn = null;PreparedStatement ps = null;try {conn = JDBCUtil.getConnection();ps = conn.prepareStatement(sql);// 设置参数setParameters(ps, params);return ps.executeUpdate();} catch (SQLException e) {throw new RuntimeException("数据库更新失败", e);} finally {JDBCUtil.close(conn, ps);}}/*** 批量更新*/public static int[] executeBatch(String sql, List<Object[]> paramsList) {Connection conn = null;PreparedStatement ps = null;try {conn = JDBCUtil.getConnection();ps = conn.prepareStatement(sql);for (Object[] params : paramsList) {setParameters(ps, params);ps.addBatch();}return ps.executeBatch();} catch (SQLException e) {throw new RuntimeException("批量更新失败", e);} finally {JDBCUtil.close(conn, ps);}}/*** 设置预处理参数*/private static void setParameters(PreparedStatement ps, Object... params) throws SQLException {if (params != null) {for (int i = 0; i < params.length; i++) {ps.setObject(i + 1, params[i]);}}}/*** 结果集处理器接口*/public interface ResultSetHandler<T> {T handle(ResultSet rs) throws SQLException;}
}
4.2 常用结果集处理器
/*** 常用的结果集处理器实现*/
public class ResultSetHandlers {/*** 返回单个对象*/public static final ResultSetHandler<Object> SINGLE_OBJECT = rs -> {if (rs.next()) {return rs.getObject(1);}return null;};/*** 返回单个Long值(用于count等)*/public static final ResultSetHandler<Long> SINGLE_LONG = rs -> {if (rs.next()) {return rs.getLong(1);}return 0L;};/*** 返回Map结果*/public static final ResultSetHandler<Map<String, Object>> SINGLE_MAP = rs -> {if (rs.next()) {ResultSetMetaData metaData = rs.getMetaData();int columnCount = metaData.getColumnCount();Map<String, Object> map = new HashMap<>();for (int i = 1; i <= columnCount; i++) {String columnName = metaData.getColumnLabel(i);Object value = rs.getObject(i);map.put(columnName, value);}return map;}return null;};/*** 返回Bean对象*/public static <T> ResultSetHandler<T> beanHandler(Class<T> beanClass) {return rs -> {if (rs.next()) {return mapRowToBean(rs, beanClass);}return null;};}/*** 返回Bean列表*/public static <T> ResultSetHandler<List<T>> beanListHandler(Class<T> beanClass) {return rs -> {List<T> list = new ArrayList<>();while (rs.next()) {list.add(mapRowToBean(rs, beanClass));}return list;};}/*** 将结果集行映射到Bean*/private static <T> T mapRowToBean(ResultSet rs, Class<T> beanClass) throws SQLException {try {T bean = beanClass.newInstance();ResultSetMetaData metaData = rs.getMetaData();int columnCount = metaData.getColumnCount();for (int i = 1; i <= columnCount; i++) {String columnName = metaData.getColumnLabel(i);Object value = rs.getObject(i);// 使用反射设置属性值setBeanProperty(bean, columnName, value);}return bean;} catch (Exception e) {throw new RuntimeException("映射Bean失败", e);}}/*** 反射设置Bean属性*/private static void setBeanProperty(Object bean, String columnName, Object value) throws Exception {// 简单的属性设置逻辑// 实际应用中可以使用BeanUtils等工具类String setterName = "set" + columnName.substring(0, 1).toUpperCase() + columnName.substring(1);java.lang.reflect.Method method = findSetterMethod(bean.getClass(), setterName, value);if (method != null) {method.invoke(bean, value);}}private static java.lang.reflect.Method findSetterMethod(Class<?> clazz, String setterName, Object value) {for (java.lang.reflect.Method method : clazz.getMethods()) {if (method.getName().equals(setterName) && method.getParameterCount() == 1) {// 简单的类型匹配Class<?> paramType = method.getParameterTypes()[0];if (value == null || paramType.isInstance(value) || isCompatibleType(paramType, value.getClass())) {return method;}}}return null;}private static boolean isCompatibleType(Class<?> paramType, Class<?> valueType) {// 简单的类型兼容性检查if (paramType == int.class && valueType == Integer.class) return true;if (paramType == long.class && valueType == Long.class) return true;if (paramType == double.class && valueType == Double.class) return true;if (paramType == boolean.class && valueType == Boolean.class) return true;return false;}
}
5. 使用示例
5.1 基础使用
public class AccountDAO {/*** 查询账户余额*/public double getBalance(String actno) {String sql = "SELECT balance FROM act WHERE actno = ?";return JDBCTemplate.executeQuery(sql, rs -> {if (rs.next()) {return rs.getDouble("balance");}throw new RuntimeException("账户不存在: " + actno);}, actno);}/*** 更新账户余额*/public int updateBalance(String actno, double newBalance) {String sql = "UPDATE act SET balance = ? WHERE actno = ?";return JDBCTemplate.executeUpdate(sql, newBalance, actno);}/*** 转账操作(带事务)*/public boolean transfer(String fromActno, String toActno, double money) {try {// 开启事务JDBCUtil.beginTransaction();// 扣款String debitSQL = "UPDATE act SET balance = balance - ? WHERE actno = ?";int count1 = JDBCTemplate.executeUpdate(debitSQL, money, fromActno);if (count1 == 0) {throw new RuntimeException("扣款失败,账户不存在: " + fromActno);}// 存款String creditSQL = "UPDATE act SET balance = balance + ? WHERE actno = ?";int count2 = JDBCTemplate.executeUpdate(creditSQL, money, toActno);if (count2 == 0) {throw new RuntimeException("存款失败,账户不存在: " + toActno);}// 提交事务JDBCUtil.commitTransaction();return true;} catch (Exception e) {// 回滚事务try {JDBCUtil.rollbackTransaction();} catch (SQLException ex) {ex.printStackTrace();}throw new RuntimeException("转账失败: " + e.getMessage(), e);}}
}
5.2 使用Bean映射
public class AccountDAO {/*** 查询账户信息(返回Bean)*/public Account getAccount(String actno) {String sql = "SELECT actno, balance, create_time FROM act WHERE actno = ?";return JDBCTemplate.executeQuery(sql, ResultSetHandlers.beanHandler(Account.class), actno);}/*** 查询所有账户*/public List<Account> getAllAccounts() {String sql = "SELECT actno, balance, create_time FROM act";return JDBCTemplate.executeQuery(sql, ResultSetHandlers.beanListHandler(Account.class));}
}/*** 账户实体类*/
public class Account {private String actno;private double balance;private Date createTime;// getter和setter方法public String getActno() { return actno; }public void setActno(String actno) { this.actno = actno; }public double getBalance() { return balance; }public void setBalance(double balance) { this.balance = balance; }public Date getCreateTime() { return createTime; }public void setCreateTime(Date createTime) { this.createTime = createTime; }
}
6. Maven依赖
6.1 基础依赖
<dependencies><!-- MySQL驱动 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.33</version></dependency><!-- Druid连接池 --><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.18</version></dependency><!-- 日志 --><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>2.0.9</version></dependency>
</dependencies>
7. 最佳实践
7.1 配置管理
- 使用配置文件管理数据库连接参数
- 区分开发、测试、生产环境配置
- 敏感信息加密存储
7.2 异常处理
- 统一异常处理,避免SQLException污染业务代码
- 提供有意义的异常信息
- 记录详细的错误日志
7.3 性能优化
- 使用连接池管理数据库连接
- 合理设置连接池参数
- 及时关闭数据库资源
7.4 事务管理
- 在Service层管理事务,而不是DAO层
- 使用声明式事务(如Spring @Transactional)
- 正确处理事务的传播行为
这种JDBC工具类封装可以大大简化数据库操作代码,提高开发效率,同时保证代码的质量和性能。