手写ibatis

  1. 通用 Mapper:提供通用的 CRUD 方法。

  2. 条件构造器:支持链式调用,构建查询条件。

  3. 分页查询:支持 Oracle 的分页查询。

  4. 连表查询:支持 JOIN 查询。

  5. 指定字段查询:支持动态选择查询字段。

  6. 注解支持:通过注解配置实体类和数据库表的映射关系。


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 支持动态映射,代码复用性高 需要处理反射和类型转换,性能略低
posted @ 2025-10-24 16:55  18sui  阅读(7)  评论(0)    收藏  举报