纯 jdbc + 反射封装的 basedao 以泛型约束实体、反射自动映射字段与列、preparedstatement 防注入为核心,统一管理连接与资源,强调映射准确、参数匹配、事务清晰、资源干净。

在纯 JDBC + 反射环境下封装通用 BaseDao,核心是用泛型约束实体类型、用反射自动映射字段与列、用 PreparedStatement 防 SQL 注入,并统一管理连接和资源。关键不在“写得多”,而在“映射准、参数对、事务清、资源净”。
一、定义泛型 BaseDao 接口(契约先行)
接口只声明能力,不涉及实现细节,方便后续 Mock 或多数据源扩展:
public interface BaseDao<t> {
// 插入返回主键(支持自增)
Long insert(T entity);
// 根据主键查询
T findById(Long id, Class<t> clazz);
// 查询全部(按类名推表名)
List<t> findAll(Class<t> clazz);
// 根据条件更新(非 null 字段)
int update(T entity);
// 根据主键删除
int deleteById(Long id, Class<t> clazz);
}</t></t></t></t></t>二、实现类中用反射解析实体元信息
每个实体类对应一张表,字段名默认映射列名(支持 @Column 注解可选增强)。重点处理三件事:
-
表名推导:默认取类简单名小写(
User.class → "user"),也可加@Table(name="sys_user") -
主键识别:找标注
@Id的字段(或约定名为id的 Long 类型字段) -
字段-列映射:遍历所有 getter 方法,排除
getClass/hashCode等,用方法名转下划线(getUserName → user_name),再通过反射读值
示例片段(获取非空字段用于 UPDATE):
private <t> Map<string object> getNonNullFieldValues(T entity) {
Map<string object> params = new LinkedHashMap();
for (Method method : entity.getClass().getDeclaredMethods()) {
if (method.getName().startsWith("get") && !method.getName().equals("getClass")) {
String fieldName = Introspector.decapitalize(
method.getName().substring(3)
);
String columnName = camelToUnderline(fieldName); // userName → user_name
try {
Object value = method.invoke(entity);
if (value != null || isUpdateNullableField(fieldName)) {
params.put(columnName, value);
}
} catch (Exception ignored) {}
}
}
return params;
}</string></string></t>三、SQL 拼装与 PreparedStatement 绑定一体化
避免字符串拼接 SQL,所有参数走 ? 占位符。利用反射拿到的字段顺序,严格匹配 setObject(i++, value):
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
-
INSERT:生成
INSERT INTO user (name, age) VALUES (?, ?),按 getter 返回顺序设参 -
UPDATE:动态拼
SET name=?, age=? WHERE id=?,WHERE 固定用主键字段 -
SELECT:查全部时用
SELECT * FROM user,构造对象时按 ResultSetMetaData 列名反查 setter 方法
注意:主键插入后需调用 ps.getGeneratedKeys() 获取自增 ID。
四、连接管理与资源释放必须显式可靠
不依赖 Spring,就自己管好 Connection/PreparedStatement/ResultSet:
- 每次操作从 DataSource 获取新连接(不复用,避免事务污染)
- 使用 try-with-resources 或 finally 块确保三层资源关闭(ResultSet → PreparedStatement → Connection)
- 统一异常转为 RuntimeException(如 DaoException),避免上层强制捕获 SQLException
推荐工具方法:
private void close(AutoCloseable... resources) {
for (AutoCloseable r : resources) {
if (r != null) {
try { r.close(); } catch (Exception ignored) {}
}
}
}不复杂但容易忽略的是字段类型与 JDBC 类型的隐式转换(比如 LocalDateTime 要注册 Timestamp 处理器),以及空值在 INSERT/UPDATE 中是否允许写入 NULL —— 这些需在反射读值时结合字段注解(如 @NotNull)或数据库元数据校验。做好这几点,一个轻量、可测、不绑定框架的 BaseDao 就立住了。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










