Appearance
JDBCTemplate 模版方法与 RowMapper 泛型 ORM 封装
本文探讨如何运用设计模式中的模版方法模式(Template Method Pattern)对原生 JDBC 操作进行抽象封装,以及利用泛型回调接口
RowMapper<T>实现轻量级 ORM 数据库结果集自动映射。
1. 原生 JDBC 操作的痛点
原生 JDBC 在进行数据库交互时,代码极其冗长且包含大量重复的样板控制代码:
[ 1. 注册驱动 ] ➔ [ 2. 获取 Connection ] ➔ [ 3. 创建 PreparedStatement ]
➔ [ 4. 填充占位符 ? ] ➔ [ 5. 执行 SQL ] ➔ [ 6. 循环遍历 ResultSet 赋值 ] ➔ [ 7. 关闭资源 Try-Catch ]上述流程中只有步骤 4(绑定参数)与步骤 6(数据映射)是变化的,其余全部是固定的结构代码。如果不做封装,开发效率极低且极易引发连接泄露。
2. JDBCTemplate 模版设计模式实现
通过抽取不变的控制流程,将变动的参数绑定与结果映射抽象出来:
java
package net.wanho.util;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import net.wanho.entity.mapper.RowMapper;
public class JDBCTemplate {
/**
* 通用 DML(INSERT, UPDATE, DELETE)模版
*/
public static int update(String sql, Object... params) {
Connection conn = null;
PreparedStatement ps = null;
try {
conn = JDBCUtil.getConnection();
ps = conn.prepareStatement(sql);
if (params != null) {
for (int i = 0; i < params.length; i++) {
ps.setObject(i + 1, params[i]);
}
}
return ps.executeUpdate();
} catch (SQLException e) {
// 将编译时异常包装为自定义运行时异常抛出
throw new DataAccessException("数据库 DML 操作失败", e);
} finally {
JDBCUtil.close(null, ps, conn);
}
}
/**
* 通用 DQL(SELECT)多条记录查询模版
*/
public static <T> List<T> query(String sql, RowMapper<T> mapper, Object... params) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
List<T> list = new ArrayList<>();
try {
conn = JDBCUtil.getConnection();
ps = conn.prepareStatement(sql);
if (params != null) {
for (int i = 0; i < params.length; i++) {
ps.setObject(i + 1, params[i]);
}
}
rs = ps.executeQuery();
while (rs.next()) {
// 回调 RowMapper 回调接口将 ResultSet 映射为 Entity/VO 对象
T obj = mapper.mapRow(rs);
list.add(obj);
}
return list;
} catch (SQLException e) {
throw new DataAccessException("数据库 DQL 查询失败", e);
} finally {
JDBCUtil.close(rs, ps, conn);
}
}
}3. RowMapper<T> 泛型 ORM 映射回调
java
package net.wanho.entity.mapper;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* 泛型回调接口:规定如何将结果集 ResultSet 某一行数据映射为具体的 Java 对象 T
*/
public interface RowMapper<T> {
T mapRow(ResultSet rs) throws SQLException;
}DAO 层的调用示例
得益于 JDBCTemplate 与 RowMapper,DAO 层的代码将被精简为只需 2~3 行:
java
public class UserDaoImpl implements UserDaoI {
@Override
public List<User> findAllUsers() {
String sql = "SELECT id, username, password FROM t_user WHERE status = ?";
// 一行代码搞定数据库查询与实体封装!
return JDBCTemplate.query(sql, new UserMapper(), 1);
}
}