模拟MyBatis源码-V2.0

本文介绍了一个简易版MyBatis框架的设计与实现过程,通过模拟关键组件如SqlSession、Configuration等,展示了如何处理SQL映射及执行查询操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前一版太过于简单, 这个版本依照源码格式创建config,executor,handler,mappedstatement, 整个项目结构如下:
这里写图片描述

本项目模拟了MyBatis的几个主要大类: SqlSession, Configuration, Executor, MapperProxy, MappedStatement, ResultSetHandler ,StatementHandler.

下面为项目主要源码:

1. 定义AutoSqlSession

session中包括两大主要对象AutoConfiguration, IExecutor

public class AutoSqlSession {

    private AutoConfiguration configuration;
    private IExecutor executor;

    public AutoSqlSession(AutoConfiguration configuration) throws ClassNotFoundException {
        this.configuration = configuration;
        this.executor = new BaseExecutor(this);
        //生成session时要初始化config配置项
        configuration.initMappedStatement();
    }

    public AutoSqlSession(AutoConfiguration configuration, IExecutor executor) throws ClassNotFoundException {
        this.configuration = configuration;
        this.executor = executor;
        configuration.initMappedStatement();
    }

    public <T> T GetMapper(Class clazz) {
        configuration.addMappedStatement(clazz);
        MapperProxy proxy = new MapperProxy(this);
        return (T) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{clazz}, proxy);
    }

    //stateMent 为namespace+id
    public <T> T SelectOne(String stateMent, Object parameter) {
        return this.executor.doQuery(stateMent, parameter);
    }
}
2. 定义配置类configuration

该类包含
List< String> scanClasses,用来模拟存储Mapper接口类, 以便读取Sql注解.
Map< String, MappedStatement> mappedStatementMap, 模拟存储接口方法对应的一席配置项


public class AutoConfiguration {

    //用来模拟存储Mapper接口类, 以便读取Sql注解
    private List<String> scanClasses = new ArrayList<>();

    //key=namespace+方法名,
    private Map<String, MappedStatement> mappedStatementMap = new HashMap<>();

    //扫描scanClasses中的类, 初始化 mappedStatementMap
    public void initMappedStatement() throws ClassNotFoundException {
        for (String clazz : scanClasses) {
            if (!"".equals(clazz)) {
                Class claxx = Class.forName(clazz);
                if (claxx != null) {
                    addMappedStatement(claxx);
                }
            }
        }
    }

    public void addMappedStatement(Class clazz) {
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            try {
                if (!method.isBridge()) {
                    try {
                        String id = clazz.getName() + "." + method.getName();
                        if (mappedStatementMap.containsKey(id)) {
                            return;
                        }
                        Annotation selectA = method.getAnnotation(Select.class);
                        if (selectA == null) {
                            return;
                        }
                        //从注解中提取Sql语句, 并存在MappedStatement对象中.
                        final String[] strings = (String[]) selectA.getClass().getMethod("value").invoke(selectA);
                        MappedStatement satement = new MappedStatement(id, strings[0], method.getReturnType());
                        this.mappedStatementMap.put(id, satement);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            } catch (IncompleteElementException e) {
                e.printStackTrace();
            }
        }
    }

    public MappedStatement getMappedStatement(String id) {
        return (MappedStatement) mappedStatementMap.get(id);
    }
}
3. 定义用户Mapper接口
public interface IUserMapper {

    @Select("select *from User where id=?")
    UserBean selectByPrimaryKey(Integer userId);

    @Select("select *from User where name=?")
    UserBean selectByName(String StrName);
}

public class UserBean {
    private int id;
    private String name;
    private int age;
    private boolean sex;
}
4. 定义MappedStatement

MappedStatement 用来缓存Mapper定义的方法相关的所有配置信息, 如sql, 方法返回类型, 方法参数(本次未模拟)等.

public class MappedStatement {
    private String sql;
    //存储nameSpace+methdNa
    private String id;
    private Class<?> returnType;

    public MappedStatement(String id, String sql, Class<?> type) {
        this.sql = sql;
        this.id = id;
        this.returnType = type;
    }
}
5. 定义MapperProxy

MapperProxy用来生成动态代理

public class MapperProxy implements InvocationHandler {

    private AutoSqlSession session;

    public MapperProxy(AutoSqlSession session) {
        this.session = session;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        String strId = method.getDeclaringClass().getName() + '.' + method.getName();
        if (session.getConfiguration().getMappedStatement(strId) != null) {
            return session.SelectOne(strId,args[0]);
        } else if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
        } else {
            return null;
        }

    }
}
6. 生成 IExecutor

IExecutor 实现类用来真正完成SqlSession提交的任务.

public interface IExecutor {
    public <T> T doQuery(String stateMent, Object parameter);
}

public class BaseExecutor implements IExecutor {

    private AutoSqlSession session;
    public BaseExecutor(AutoSqlSession session) {
        this.session = session;
    }
    @Override
    public <T> T doQuery(String stateMent, Object parameter) {

        MappedStatement mappedStatement = session.getConfiguration().getMappedStatement(stateMent);
        AutoSimpleStatementHandler handler = new AutoSimpleStatementHandler(session.getConfiguration());
        return (T) handler.query(stateMent, parameter);
    }
}
7. 生成 StatementHandler

AutoSimpleStatementHandler 用来模拟实现 Executor提交来的任务

public class AutoSimpleStatementHandler {
private AutoConfiguration configuration;
private AutoResultSetHandler autoResultSetHandler;

public AutoSimpleStatementHandler(AutoConfiguration configuration, AutoResultSetHandler autoResultSetHandler) {
    this.configuration = configuration;
    this.autoResultSetHandler = autoResultSetHandler;
}

public AutoSimpleStatementHandler(AutoConfiguration configuration) {
    this(configuration, new AutoResultSetHandler(configuration));
}

public <E> E query(String strStatement, Object parameter) {
    try {
        //JDBC
        Connection conn = getConnection();
        MappedStatement mappedStatement = configuration.getMappedStatement(strStatement);
        //TODO ParameterHandler

        String strSql = mappedStatement.getSql();
        PreparedStatement pstmt = conn.prepareStatement(strSql);

        if(parameter.getClass().getName().equals("java.lang.String")){
            pstmt.setString(1,parameter.toString());
        }
        if(parameter.getClass().getName().equals("java.lang.Integer")){
            pstmt.setInt(1,Integer.valueOf(parameter.toString()));
        }
        pstmt.execute();

        //ResultSetHandler
        return (E) autoResultSetHandler.handle(pstmt.getResultSet(), mappedStatement.getReturnType());
    } catch (Exception e) {
        e.printStackTrace();
    }

    return null;
}


public Connection getConnection() throws SQLException {
    String driver = "com.mysql.jdbc.Driver";
    String url = "jdbc:mysql://x.x.x.x:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC";
    String username = "root";
    String password = "xxx";
    Connection conn = null;
    try {
        Class.forName(driver); //classLoader,加载对应驱动
        conn = DriverManager.getConnection(url, username, password);
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (SQLException e) {
        e.printStackTrace();
    }
    return conn;
}

}

8. ResultSetHandler

AutoResultSetHandler 用来模拟对生成的结果进行封装.


public class AutoResultSetHandler {
    private final AutoConfiguration configuration;

    public AutoResultSetHandler(AutoConfiguration configuration) {
        this.configuration = configuration;
    }

    public <E> E handle(ResultSet rs, Class type) throws SQLException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Object resultObj = new DefaultObjectFactory().create(type);
        if (rs.next()) {
            int i = 0;
            for (Field field : resultObj.getClass().getDeclaredFields()) {
                setValue(resultObj, field, rs ,i);
            }
        }
        return (E) resultObj;
    }

    private void setValue(Object resultObj, Field field, ResultSet rs, int i) throws NoSuchMethodException, SQLException, InvocationTargetException, IllegalAccessException {
        Method setMethod = resultObj.getClass().getMethod("set" + upperCapital(field.getName()), field.getType());
        setMethod.invoke(resultObj, getResult(field,rs));
    }

    private Object getResult(Field field, ResultSet rs) throws SQLException {
        //TODO type handles
        //bean属性的名字必须要和数据库column的名字一样
        Class<?> type = field.getType();
//        if(Integer.class == type){
        if("int".equals(type.toString())){
            return rs.getInt(field.getName());
        }
        if(String.class == type){
            return rs.getString(field.getName());
        }
        if("boolean".equals(type.toString())){
            return rs.getBoolean(field.getName());
        }
        return rs.getString(field.getName());
    }

    private String upperCapital(String name) {
        String first = name.substring(0, 1);
        String tail = name.substring(1);
        return first.toUpperCase() + tail;
    }
}

9. 使用代码测试
public class TestDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        AutoConfiguration configuration = new AutoConfiguration();
        configuration.addScanClass(IUserMapper.class.getName());

        AutoSqlSession session = new AutoSqlSession(configuration);

        //直接使用Session.selectOne方法测试
        UserBean beanByName = session.SelectOne("com.feilong.cn.mybatis.autoV2.mappers.IUserMapper.selectByName", "name1");
        System.out.println(beanByName.getName());

        //调用Mapper测试
        IUserMapper mapper = session.GetMapper(IUserMapper.class);
        UserBean bean = mapper.selectByPrimaryKey(1);
        System.out.println(bean.getName());
    }
}

项目源码路径:
https://gitee.com/yangxulong/autoMyBaits

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值