手写mybatis框架

我这个需求的底层逻辑是什么?顶层设计在哪?最终交付价值是什么?过程的抓手在哪里?如何保证结果的闭环?我比别人的亮点在哪里?优势在哪里?我的思考和沉淀在哪里?我有形成自己的方法论么?我这个需求换别人来写是否会一样?我是如何做到对齐拉通的?我是如何赋能的?

一,需求分析

1)它需要实现对连接资源的自动管理,也就是把创建 Connection、Statement、关闭 Connection、Statement、ResultSet 这些操作封装到底层的对象中,不需要在应用层手动调用。

2)它需要把 SQL 语句抽离出来实现集中管理,开发人员不用在业务代码里面写 SQL语句。

3)它需要实现对结果集的转换,也就是我们指定了映射规则之后,这个框架会自动帮我们把 ResultSet 映射成实体类对象。

4)需要提供一个 API 来给我们操作数据库,这里面封装了对数据库的操作的常用的方法。

二,概要设计

核心对象

1、存放参数和结果映射关系、存放 SQL 语句,我们需要定义一个配置类;

2、执行对数据库的操作,处理参数和结果集的映射,创建和释放资源,我们需要定义一个执行器;

3、有了这个执行器以后,我们不能直接调用它,而是定义一个给应用层使用的 API,它可以根据 SQL 的 id 找到 SQL 语句,交给执行器执行;

4、直接使用 id 查找 SQL 语句太麻烦了,我们干脆把存放 SQL 的命名空间定义成一个接口,把 SQL 的 id 定义成方法,这样只要调用接口方法就可以找到要执行的 SQL。这个时候我们需要引入一个代理类。

流程分析

在这里插入图片描述

1、定义接口 Mapper 和方法,用来调用数据库操作。Mapper 接口操作数据库需要通过代理类。

2、定义配置类对象 Configuration。

3、定义应用层的 API SqlSession。它有一个 getMapper()方法,我们会从配置类Configuration 里面使用 Proxy.newProxyInatance()拿到一个代理对象 MapperProxy。

4、有了代理对象 MapperProxy 之后,我们调用接口的任意方法,就是调用代理对象的 invoke()方法。

5、代理对象 MapperProxy 的 invoke()方法调用了 SqlSession 的 selectOne()。

6、SqlSession 只是一个 API,还不是真正的 SQL 执行者,所以接下来会调用执行器 Executor 的 query()方法。

7、执行器 Executor 的 query()方法里面就是对 JDBC 底层的 Statement 的封装,最终实现对数据库的操作,和结果的返回。

三,代码

/**
 * @author yhd
 * @createtime 2021/2/14 16:08
 */
public class SqlSession {

    private Configuration configuration;

    private Executor executor;

    public SqlSession(Configuration configuration, Executor executor) {
        this.configuration = configuration;
        this.executor = executor;
    }

    public <T> T selectOne(String statementId, Object... parameters)throws Exception {
        String sql = Configuration.sqlMappings.getString(statementId);
        if (null != sql && !"".equals(sql)) {
            return executor.query(sql, parameters);
        }
        return null;
    }

    public <T> T getMapper(Class clazz) {
        return (T) configuration.getMapper(clazz, this);
    }


}
/**
 * @author yhd
 * @createtime 2021/2/14 16:09
 */
public class Configuration {

    public static final ResourceBundle sqlMappings;

    static {
        sqlMappings = ResourceBundle.getBundle("application");
    }

    public <T> T getMapper(Class clazz, SqlSession sqlSession) {
        return (T) Proxy.newProxyInstance(
                this.getClass().getClassLoader()
                , new Class[]{clazz}
                , new MapperProxy(sqlSession));
    }
}
/**
 * @author yhd
 * @createtime 2021/2/14 16:15
 */
public class MapperProxy implements InvocationHandler {

    private SqlSession sqlSession;

    public MapperProxy(SqlSession sqlSession){
        this.sqlSession=sqlSession;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String mapperInterface = method.getDeclaringClass().getName();
        String methodName=method.getName();
        String statementId=mapperInterface+"."+methodName;
        return sqlSession.selectOne(statementId,args);
    }
}
/**
 * @author yhd
 * @createtime 2021/2/14 16:10
 */
public class Executor {

    public <T> T query(String sql, Object[] parameters)throws Exception {
        Connection conn= JDBCUtils.getConnection();
        Statement statement = conn.createStatement();
        ResultSet result =statement.executeQuery(String.format(sql,parameters));
        // JDBC 的业务逻辑....
        return (T) result;
    }
}
com.yhd.mebatis.mapper.BlogMapper.selectBlogById=select * from blog where id = %d

四,结果评估

1、在 Executor 中,对参数、语句和结果集的处理是耦合的,没有实现职责分离;

2、参数:没有实现对语句的预编译,只有简单的格式化(format),效率不高,还存在 SQL 注入的风险;

3、语句执行:数据库连接硬编码;

4、结果集:还只能处理 Blog 类型,没有实现根据实体类自动映射。确实有点搓,拿不出手。

五,功能迭代

支持参数预编译;

支持结果集的自动处理(通过反射);

对 Executor 的职责进行细化。

在方法上使用注解配置 SQL;

查询带缓存功能;

支持自定义插件。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值