本文目录导读:

这是一个非常经典的造轮子问题,从零构建一个轻量级ORM(对象关系映射),能让你深刻理解JDBC、反射、泛型和设计模式。
为了控制复杂度,我们构建一个仅支持单表CRUD(增删改查)、基于注解、无缓存的迷你ORM,目标是用类似 userMapper.findById(1) 或 userMapper.save(user) 的方式操作数据库。
以下是完整的步骤和核心代码逻辑。
第一阶段:核心设计
定义映射注解
我们需要告诉ORM,Java类对应哪张表,字段对应哪一列。
// 表名注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Table {
String value(); // 表名
}
// 列名注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Column {
String value(); // 列名
}
// 主键注解(特殊标记,用于更新/删除时的WHERE子句)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Id {
}
创建实体类示例
@Table("t_user")
public class User {
@Id
@Column("id")
private Long id;
@Column("user_name")
private String userName;
@Column("age")
private Integer age;
// getter / setter(必须,反射要用)
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getUserName() { return userName; }
public void setUserName(String userName) { this.userName = userName; }
public Integer getAge() { return age; }
public void setAge(Integer age) { this.age = age; }
}
第二阶段:核心引擎
实体元数据解析器
解析类上的注解,缓存映射关系,避免每次操作都反射。
public class EntityMeta<T> {
private Class<T> entityClass;
private String tableName;
private Map<String, Field> columnFieldMap; // 列名 -> 字段
private Field idField; // 主键字段
private String idColumn; // 主键列名
public EntityMeta(Class<T> clazz) {
this.entityClass = clazz;
// 1. 解析表名
Table table = clazz.getAnnotation(Table.class);
if (table == null) throw new RuntimeException("Missing @Table annotation");
this.tableName = table.value();
// 2. 解析所有字段
this.columnFieldMap = new HashMap<>();
for (Field field : clazz.getDeclaredFields()) {
field.setAccessible(true);
Column column = field.getAnnotation(Column.class);
if (column != null) {
columnFieldMap.put(column.value(), field);
if (field.isAnnotationPresent(Id.class)) {
this.idField = field;
this.idColumn = column.value();
}
}
}
}
// getters...
public String getTableName() { return tableName; }
public Field getIdField() { return idField; }
public String getIdColumn() { return idColumn; }
public Map<String, Field> getColumnFieldMap() { return columnFieldMap; }
}
SQL 生成器
根据元数据动态生成SQL,防止SQL注入(使用 PreparedStatement)。
public class SqlBuilder<T> {
private EntityMeta<T> meta;
public SqlBuilder(EntityMeta<T> meta) {
this.meta = meta;
}
// 生成 INSERT 语句:INSERT INTO t_user (id, user_name, age) VALUES (?, ?, ?)
public String buildInsertSql() {
StringBuilder columns = new StringBuilder("(");
StringBuilder values = new StringBuilder("(");
for (String col : meta.getColumnFieldMap().keySet()) {
columns.append(col).append(",");
values.append("?,");
}
columns.deleteCharAt(columns.length()-1).append(")");
values.deleteCharAt(values.length()-1).append(")");
return "INSERT INTO " + meta.getTableName() + " " + columns + " VALUES " + values;
}
// 生成 SELECT BY ID:SELECT * FROM t_user WHERE id = ?
public String buildSelectByIdSql() {
return "SELECT * FROM " + meta.getTableName() + " WHERE " + meta.getIdColumn() + " = ?";
}
// 生成 UPDATE:UPDATE t_user SET user_name=?, age=? WHERE id=?
public String buildUpdateSql() {
StringBuilder setClause = new StringBuilder("SET ");
for (Map.Entry<String, Field> entry : meta.getColumnFieldMap().entrySet()) {
String col = entry.getKey();
if (col.equals(meta.getIdColumn())) continue; // 主键不更新
setClause.append(col).append("=?,");
}
setClause.deleteCharAt(setClause.length()-1);
return "UPDATE " + meta.getTableName() + " " + setClause + " WHERE " + meta.getIdColumn() + " = ?";
}
// 生成 DELETE BY ID
public String buildDeleteByIdSql() {
return "DELETE FROM " + meta.getTableName() + " WHERE " + meta.getIdColumn() + " = ?";
}
// 生成 SELECT ALL
public String buildSelectAllSql() {
return "SELECT * FROM " + meta.getTableName();
}
}
执行器(核心JDBC操作)
管理数据库连接,并处理 ResultSet -> 对象 的转换。
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public class SimpleExecutor {
private DataSource dataSource; // 使用连接池,或简单的DriverManager
public SimpleExecutor(DataSource dataSource) {
this.dataSource = dataSource;
}
// 通用查询,返回实体列表
public <T> List<T> query(String sql, Object[] params, EntityMeta<T> meta) {
List<T> result = new ArrayList<>();
try (Connection conn = dataSource.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
setParameters(pstmt, params);
try (ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) {
T entity = meta.getEntityClass().newInstance();
// 遍历所有列,通过反射赋值
for (Map.Entry<String, Field> entry : meta.getColumnFieldMap().entrySet()) {
String columnName = entry.getKey();
Field field = entry.getValue();
Object value = rs.getObject(columnName);
// 处理类型转换(如数据库中int到Java Integer)
if (value != null) {
field.set(entity, value);
}
}
result.add(entity);
}
}
} catch (Exception e) {
throw new RuntimeException("Query failed", e);
}
return result;
}
// 通用更新(INSERT/UPDATE/DELETE)
public int executeUpdate(String sql, Object[] params) {
try (Connection conn = dataSource.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
setParameters(pstmt, params);
return pstmt.executeUpdate();
} catch (SQLException e) {
throw new RuntimeException("Update failed", e);
}
}
// 设置参数到PreparedStatement
private void setParameters(PreparedStatement pstmt, Object[] params) throws SQLException {
if (params != null) {
for (int i = 0; i < params.length; i++) {
pstmt.setObject(i + 1, params[i]);
}
}
}
}
第三阶段:对外API(BaseMapper)
定义通用的CRUD接口,用户只需针对实体类调用。
public class BaseMapper<T> {
private final EntityMeta<T> meta;
private final SqlBuilder<T> sqlBuilder;
private final SimpleExecutor executor;
public BaseMapper(Class<T> clazz, DataSource dataSource) {
this.meta = new EntityMeta<>(clazz);
this.sqlBuilder = new SqlBuilder<>(meta);
this.executor = new SimpleExecutor(dataSource);
}
// --- CRUD 方法 ---
public T findById(Object id) {
String sql = sqlBuilder.buildSelectByIdSql();
List<T> list = executor.query(sql, new Object[]{id}, meta);
return list.isEmpty() ? null : list.get(0);
}
public List<T> findAll() {
String sql = sqlBuilder.buildSelectAllSql();
return executor.query(sql, null, meta);
}
public int save(T entity) {
// 获取所有非主键字段的值 + 主键值(如果主键是自增的,可以忽略主键字段)
List<Object> params = new ArrayList<>();
for (Field field : meta.getColumnFieldMap().values()) {
try {
params.add(field.get(entity));
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
String sql = sqlBuilder.buildInsertSql();
return executor.executeUpdate(sql, params.toArray());
}
public int updateById(T entity) {
// 收集所有字段值(除主键外),最后加上主键值
List<Object> params = new ArrayList<>();
for (Map.Entry<String, Field> entry : meta.getColumnFieldMap().entrySet()) {
if (entry.getKey().equals(meta.getIdColumn())) continue;
try {
params.add(entry.getValue().get(entity));
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
// 主键值放最后
try {
params.add(meta.getIdField().get(entity));
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
String sql = sqlBuilder.buildUpdateSql();
return executor.executeUpdate(sql, params.toArray());
}
public int deleteById(Object id) {
String sql = sqlBuilder.buildDeleteByIdSql();
return executor.executeUpdate(sql, new Object[]{id});
}
}
如何使用?
public class Main {
public static void main(String[] args) {
// 1. 配置数据源(这里假设使用HikariCP或简单连接池)
DataSource dataSource = getDataSource();
// 2. 创建BaseMapper实例
BaseMapper<User> userMapper = new BaseMapper<>(User.class, dataSource);
// 3. 使用
// 查询
User user = userMapper.findById(1L);
System.out.println(user.getUserName());
// 新增
User newUser = new User();
newUser.setUserName("Alice");
newUser.setAge(30);
userMapper.save(newUser);
// 更新
user.setAge(31);
userMapper.updateById(user);
}
private static DataSource getDataSource() {
// 返回一个实现了 javax.sql.DataSource 的对象
// HikariDataSource
}
}
| 组件 | 职责 | 关键技术 |
|---|---|---|
| 注解 | 定义映射规则 | @Retention(RUNTIME) |
| EntityMeta | 缓存类/表/字段关系 | 反射 + Map |
| SqlBuilder | 预编译SQL生成 | PreparedStatement |
| SimpleExecutor | 执行JDBC,结果集转对象 | ResultSet.getObject + Field.set |
| BaseMapper | 统一CRUD接口 | 泛型 |
扩展方向(进阶)
如果你想让这个轻量级ORM更实用,可以逐步添加:
- 连接池集成:使用HikariCP或Druid作为DataSource。
- 命名策略:支持
camelCase -> snake_case自动映射,减少注解。 - 懒加载:对于关联对象,只在调用getter时查询。
- 条件查询:实现
QueryBuilder支持WHERE age > ? AND name LIKE ?。 - 事务管理:通过
Connection的commit/rollback实现。
这个Mini ORM的核心逻辑就在 反射(读注解、赋值) 和 JDBC(执行SQL、映射结果集) 之间来回穿梭,掌握它,你就掌握了所有ORM框架(MyBatis、Hibernate)的底层基因。