手写ibatis
-
通用 Mapper:提供通用的 CRUD 方法。
-
条件构造器:支持链式调用,构建查询条件。
-
分页查询:支持 Oracle 的分页查询。
-
连表查询:支持 JOIN 查询。
-
指定字段查询:支持动态选择查询字段。
-
注解支持:通过注解配置实体类和数据库表的映射关系。
1. 项目结构
src/main/java
├── com.example.orm
│ ├── annotation
│ │ ├── Table.java
│ │ └── Column.java
│ ├── core
│ │ ├── QueryWrapper.java
│ │ ├── Page.java
│ │ └── SqlHelper.java
│ ├── mapper
│ │ └── BaseMapper.java
│ ├── util
│ │ └── ReflectionUtil.java
│ └── OrmApplication.java
2. 完整代码
(1) 注解类
Table.java
package com.example.orm.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
String value(); // 表名
}
Column.java
package com.example.orm.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
String value() default ""; // 列名,默认为字段名
boolean isPrimaryKey() default false; // 是否为主键
}
(2) 核心工具类
ReflectionUtil.java
package com.example.orm.util;
import com.example.orm.annotation.Column;
import com.example.orm.annotation.Table;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class ReflectionUtil {
/**
* 获取表名
*/
public static String getTableName(Class<?> clazz) {
Table table = clazz.getAnnotation(Table.class);
if (table != null) {
return table.value();
}
return clazz.getSimpleName().toLowerCase();
}
/**
* 获取字段名和列名的映射
*/
public static Map<String, String> getColumnMappings(Class<?> clazz) {
Map<String, String> mappings = new HashMap<>();
for (Field field : clazz.getDeclaredFields()) {
Column column = field.getAnnotation(Column.class);
if (column != null) {
String columnName = column.value().isEmpty() ? field.getName() : column.value();
mappings.put(field.getName(), columnName);
}
}
return mappings;
}
/**
* 获取主键字段名
*/
public static String getPrimaryKeyField(Class<?> clazz) {
for (Field field : clazz.getDeclaredFields()) {
Column column = field.getAnnotation(Column.class);
if (column != null && column.isPrimaryKey()) {
return field.getName();
}
}
throw new RuntimeException("No primary key found in class: " + clazz.getName());
}
/**
* 获取字段值
*/
public static Object getFieldValue(Object obj, String fieldName) {
try {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
return field.get(obj);
} catch (Exception e) {
throw new RuntimeException("Failed to get field value: " + fieldName, e);
}
}
}
(3) 条件构造器
QueryWrapper.java
package com.example.orm.core;
import java.util.ArrayList;
import java.util.List;
public class QueryWrapper<T> {
private final Class<T> entityClass;
private final List<String> conditions = new ArrayList<>();
private final List<String> joinClauses = new ArrayList<>();
private final List<String> selectFields = new ArrayList<>();
private String orderByClause;
private Integer limit;
private Integer offset;
private String mainTableAlias; // 主表别名
public QueryWrapper(Class<T> entityClass) {
this.entityClass = entityClass;
this.mainTableAlias = "t1"; // 默认主表别名
}
/**
* 设置主表别名
*/
public QueryWrapper<T> alias(String alias) {
this.mainTableAlias = alias;
return this;
}
/**
* 添加等值条件
*/
public QueryWrapper<T> eq(String column, Object value) {
conditions.add(mainTableAlias + "." + column + " = '" + value + "'");
return this;
}
/**
* 添加模糊查询条件
*/
public QueryWrapper<T> like(String column, String value) {
conditions.add(mainTableAlias + "." + column + " LIKE '%" + value + "%'");
return this;
}
/**
* 添加 JOIN 子句
*/
public QueryWrapper<T> join(JoinType joinType, String table, String alias, String onClause) {
joinClauses.add(joinType.getValue() + " " + table + " " + alias + " ON " + onClause);
return this;
}
/**
* 添加排序条件
*/
public QueryWrapper<T> orderBy(String orderByClause) {
this.orderByClause = orderByClause;
return this;
}
/**
* 设置分页参数
*/
public QueryWrapper<T> page(int offset, int limit) {
this.offset = offset;
this.limit = limit;
return this;
}
/**
* 添加需要查询的字段
*/
public QueryWrapper<T> select(String... fields) {
for (String field : fields) {
selectFields.add(field);
}
return this;
}
/**
* 构建 SQL 查询语句
*/
public String build() {
StringBuilder sql = new StringBuilder("SELECT ");
// 添加查询字段
if (selectFields.isEmpty()) {
sql.append(mainTableAlias + ".*"); // 默认查询主表所有字段
} else {
sql.append(String.join(", ", selectFields));
}
sql.append(" FROM ").append(ReflectionUtil.getTableName(entityClass)).append(" ").append(mainTableAlias);
// 添加 JOIN 子句
if (!joinClauses.isEmpty()) {
sql.append(" ").append(String.join(" ", joinClauses));
}
// 添加 WHERE 条件
if (!conditions.isEmpty()) {
sql.append(" WHERE ").append(String.join(" AND ", conditions));
}
// 添加排序条件
if (orderByClause != null) {
sql.append(" ORDER BY ").append(orderByClause);
}
// 添加分页条件
if (limit != null && offset != null) {
sql = new StringBuilder("SELECT * FROM (")
.append("SELECT t.*, ROWNUM AS rn FROM (")
.append(sql)
.append(") t WHERE ROWNUM <= ").append(offset + limit)
.append(") WHERE rn >= ").append(offset + 1);
}
return sql.toString();
}
}
(4) 分页工具
Page.java
package com.example.orm.core;
import java.util.List;
public class Page<T> {
private List<T> records; // 当前页数据
private long total; // 总记录数
private long size; // 每页大小
private long current; // 当前页码
// Getters and Setters
public List<T> getRecords() {
return records;
}
public void setRecords(List<T> records) {
this.records = records;
}
public long getTotal() {
return total;
}
public void setTotal(long total) {
this.total = total;
}
public long getSize() {
return size;
}
public void setSize(long size) {
this.size = size;
}
public long getCurrent() {
return current;
}
public void setCurrent(long current) {
this.current = current;
}
}
(5) SQL 工具类
SqlHelper.java
package com.example.orm.core;
import com.example.orm.util.ReflectionUtil;
import java.util.Map;
public class SqlHelper {
/**
* 生成插入 SQL
*/
public static <T> String buildInsertSql(T entity) {
Class<?> clazz = entity.getClass();
String tableName = ReflectionUtil.getTableName(clazz);
Map<String, String> columnMappings = ReflectionUtil.getColumnMappings(clazz);
StringBuilder columns = new StringBuilder();
StringBuilder values = new StringBuilder();
for (Map.Entry<String, String> entry : columnMappings.entrySet()) {
String fieldName = entry.getKey();
String columnName = entry.getValue();
Object value = ReflectionUtil.getFieldValue(entity, fieldName);
columns.append(columnName).append(",");
values.append("'").append(value).append("',");
}
// 去掉最后一个逗号
columns.deleteCharAt(columns.length() - 1);
values.deleteCharAt(values.length() - 1);
return "INSERT INTO " + tableName + " (" + columns + ") VALUES (" + values + ")";
}
/**
* 生成更新 SQL
*/
public static <T> String buildUpdateSql(T entity) {
Class<?> clazz = entity.getClass();
String tableName = ReflectionUtil.getTableName(clazz);
Map<String, String> columnMappings = ReflectionUtil.getColumnMappings(clazz);
String primaryKeyField = ReflectionUtil.getPrimaryKeyField(clazz);
String primaryKeyColumn = columnMappings.get(primaryKeyField);
Object primaryKeyValue = ReflectionUtil.getFieldValue(entity, primaryKeyField);
StringBuilder setClause = new StringBuilder();
for (Map.Entry<String, String> entry : columnMappings.entrySet()) {
String fieldName = entry.getKey();
String columnName = entry.getValue();
Object value = ReflectionUtil.getFieldValue(entity, fieldName);
setClause.append(columnName).append(" = '").append(value).append("',");
}
// 去掉最后一个逗号
setClause.deleteCharAt(setClause.length() - 1);
return "UPDATE " + tableName + " SET " + setClause + " WHERE " + primaryKeyColumn + " = '" + primaryKeyValue + "'";
}
/**
* 生成删除 SQL
*/
public static <T> String buildDeleteSql(T entity) {
Class<?> clazz = entity.getClass();
String tableName = ReflectionUtil.getTableName(clazz);
String primaryKeyField = ReflectionUtil.getPrimaryKeyField(clazz);
Map<String, String> columnMappings = ReflectionUtil.getColumnMappings(clazz);
String primaryKeyColumn = columnMappings.get(primaryKeyField);
Object primaryKeyValue = ReflectionUtil.getFieldValue(entity, primaryKeyField);
return "DELETE FROM " + tableName + " WHERE " + primaryKeyColumn + " = '" + primaryKeyValue + "'";
}
}
(6) 通用 Mapper
BaseMapper.java
package com.example.orm.mapper;
import com.example.orm.core.Page;
import com.example.orm.core.QueryWrapper;
import com.example.orm.core.SqlHelper;
import java.util.List;
public interface BaseMapper<T> {
/**
* 插入记录
*/
default int insert(T entity) {
String sql = SqlHelper.buildInsertSql(entity);
return executeUpdate(sql);
}
/**
* 更新记录
*/
default int update(T entity) {
String sql = SqlHelper.buildUpdateSql(entity);
return executeUpdate(sql);
}
/**
* 删除记录
*/
default int delete(T entity) {
String sql = SqlHelper.buildDeleteSql(entity);
return executeUpdate(sql);
}
/**
* 查询记录
*/
List<T> selectList(QueryWrapper<T> queryWrapper);
/**
* 分页查询
*/
Page<T> selectPage(Page<T> page, QueryWrapper<T> queryWrapper);
/**
* 执行更新操作
*/
int executeUpdate(String sql);
/**
* 执行查询操作
*/
List<T> executeQuery(String sql);
}
3. 使用示例
(1) 实体类
@Table("user")
public class User {
@Column(isPrimaryKey = true)
private Long id;
@Column("username")
private String name;
@Column
private Integer age;
// Getters and Setters
}
(2) Mapper 实现
public class UserMapper implements BaseMapper<User> {
@Override
public List<User> selectList(QueryWrapper<User> queryWrapper) {
String sql = queryWrapper.build();
return executeQuery(sql);
}
@Override
public Page<User> selectPage(Page<User> page, QueryWrapper<User> queryWrapper) {
String sql = queryWrapper.build();
List<User> records = executeQuery(sql);
page.setRecords(records);
return page;
}
@Override
public int executeUpdate(String sql) {
// 实现 JDBC 更新逻辑
return 0;
}
@Override
public List<User> executeQuery(String sql) {
// 实现 JDBC 查询逻辑
return null;
}
}
4. 总结
-
支持通用 CRUD 操作。
-
支持动态条件构造、分页查询、连表查询和指定字段查询。
-
通过注解配置实体类和数据库表的映射关系。
在 iBatis 中,resultClass 用于指定 SQL 查询结果的映射类型。要支持不同的 resultClass,可以通过以下方式实现通用性:
1. 使用 java.util.Map 作为通用结果类型
将 resultClass 设置为 java.util.Map,这样查询结果会以 Map<String, Object> 的形式返回,其中键是列名,值是列值。
(1) XML 映射文件
<select id="executeSql" parameterClass="java.util.Map" resultClass="java.util.HashMap">
${sql}
</select>
(2) 代码调用
Map<String, String> params = new HashMap<>();
params.put("sql", "SELECT id, name, age FROM user WHERE age = 25");
List<Map<String, Object>> result = sqlMapClient.queryForList("executeSql", params);
for (Map<String, Object> row : result) {
System.out.println("ID: " + row.get("ID") + ", Name: " + row.get("NAME") + ", Age: " + row.get("AGE"));
}
优点
-
无需定义实体类,适合动态查询。
-
结果以
Map形式返回,灵活通用。
缺点
-
需要手动处理列名和类型转换。
-
代码可读性较差。
2. 使用反射动态映射结果
通过反射将查询结果动态映射到不同的实体类中。可以在 XML 映射文件中使用 resultClass="java.lang.Object",然后在代码中手动处理映射。
(1) XML 映射文件
<select id="executeSql" parameterClass="java.util.Map" resultClass="java.lang.Object">
${sql}
</select>
(2) 代码调用
Map<String, String> params = new HashMap<>();
params.put("sql", "SELECT id, name, age FROM user WHERE age = 25");
List<Object> result = sqlMapClient.queryForList("executeSql", params);
for (Object row : result) {
if (row instanceof Map) {
Map<String, Object> map = (Map<String, Object>) row;
System.out.println("ID: " + map.get("ID") + ", Name: " + map.get("NAME") + ", Age: " + map.get("AGE"));
} else {
// 处理其他类型的映射
}
}
优点
-
支持动态映射到不同的实体类。
-
灵活性较高。
缺点
-
需要手动处理类型转换。
-
代码复杂度较高。
3. 使用泛型和反射实现通用 Mapper
通过泛型和反射,实现一个通用的 Mapper 类,支持动态映射到不同的实体类。
(1) 通用 Mapper 接口
public interface GenericMapper<T> {
List<T> executeQuery(String sql, Class<T> resultClass);
}
(2) 通用 Mapper 实现
import com.ibatis.sqlmap.client.SqlMapClient;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class GenericMapperImpl<T> implements GenericMapper<T> {
private final SqlMapClient sqlMapClient;
public GenericMapperImpl(SqlMapClient sqlMapClient) {
this.sqlMapClient = sqlMapClient;
}
@Override
public List<T> executeQuery(String sql, Class<T> resultClass) {
try {
Map<String, String> params = new HashMap<>();
params.put("sql", sql);
List<Map<String, Object>> result = sqlMapClient.queryForList("executeSql", params);
// 将 Map 转换为实体类
return mapToEntity(result, resultClass);
} catch (Exception e) {
throw new RuntimeException("Failed to execute query", e);
}
}
private List<T> mapToEntity(List<Map<String, Object>> result, Class<T> resultClass) {
try {
List<T> entities = new ArrayList<>();
for (Map<String, Object> row : result) {
T entity = resultClass.getDeclaredConstructor().newInstance();
for (Map.Entry<String, Object> entry : row.entrySet()) {
String fieldName = entry.getKey().toLowerCase(); // 列名转小写
Object value = entry.getValue();
Field field = resultClass.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(entity, value);
}
entities.add(entity);
}
return entities;
} catch (Exception e) {
throw new RuntimeException("Failed to map result to entity", e);
}
}
}
(3) 使用示例
public class Main {
public static void main(String[] args) {
SqlMapClient sqlMapClient = ...; // 初始化 SqlMapClient
GenericMapper<User> userMapper = new GenericMapperImpl<>(sqlMapClient);
String sql = "SELECT id, name, age FROM user WHERE age = 25";
List<User> users = userMapper.executeQuery(sql, User.class);
for (User user : users) {
System.out.println("User: " + user.getName() + ", Age: " + user.getAge());
}
}
}
优点
-
支持动态映射到不同的实体类。
-
代码复用性高。
缺点
-
需要处理反射和类型转换。
-
性能可能略低于直接映射。
4. 总结
| 方法 | 优点 | 缺点 |
使用 java.util.Map |
简单灵活,无需定义实体类 | 需要手动处理列名和类型转换 |
| 使用反射动态映射结果 | 支持动态映射到不同的实体类 | 需要手动处理类型转换,代码复杂度较高 |
| 使用泛型和反射实现通用 Mapper | 支持动态映射,代码复用性高 | 需要处理反射和类型转换,性能略低 |

浙公网安备 33010602011771号