Giter Club home page Giter Club logo

minimybatis's Introduction

minimybatis

手写迷你mybatis框架,里面使用了mybatis设计模式和框架 手写思路和框架图详细解释:https://blog.csdn.net/kuailebuzhidao/article/details/88355236

###################### 正文 ####################################### 继上篇手写spring后(https://blog.csdn.net/kuailebuzhidao/article/details/87869279),感觉有必要继续把mybatis框架也手写出来,供深入理解。

网上已经有很多手写框架的博客,但是很多只是按照mybatis流程,面向过程地写:解析xml->代理反射mapper->调用JDBC获取结果。虽然这样理解是对的,但是失去了理解mybatis源码意义。我遵循mybatis源码整体框架和设计,使用源码包名和类名,配合工厂模式和代理模式,写了精简版的mybatis框架。能看懂我这个minmybatis,就能更加容易地理解庞大的源码体系。手写框架的源码已经上传到github上,欢迎下载点星,感谢! github地址:https://github.com/SeasonPanPan/myspring

先看以下spring源码中的类关系图:(CSDN原地址查看)

这个图很清晰地画出了mybatis重要组件类和流程:

从MyBatis源码实现的角度来看,MyBatis的主要的核心部件有以下几个:

Configuration:MyBatis所有的配置信息都维持在Configuration对象之中,核心类;

SqlSession:作为MyBatis工作的主要顶层API,表示和数据库交互的会话,完成必要数据库增删改查功能;

Executor:MyBatis执行器,是MyBatis 调度的核心,负责SQL语句的生成和查询缓存的维护;

StatementHandler:封装了JDBC Statement操作,负责对JDBC statement 的操作,如设置参数、将Statement结果集转换成List集合。

ParameterHandler:负责对用户传递的参数转换成JDBC Statement 所需要的参数;

ResultSetHandler:负责将JDBC返回的ResultSet结果集对象转换成List类型的集合;

MappedStatement:MappedStatement维护了一条<select|update|delete|insert>节点的封装;

MapperProxy和MapperProxyFactory:Mapper代理,使用原生的Proxy执行mapper里的方法。

我从测试类main函数说说整体手写过程。

public static void main(String[] args) {

SqlSessionFactory factory = new SqlSessionFactoryBuilder().build("conf.properties");
SqlSession session = factory.openSession();

UserMapper userMapper = session.getMapper(UserMapper.class);
User user = userMapper.getUser("1");
System.out.println("******* " + user);
System.out.println("*******all " + userMapper.getAll());

} SqlSessionFactory factory = new SqlSessionFactoryBuilder().build("conf.properties");

解释:在SqlSessionFactoryBuilder中加载properties文件配置返回一个默认SqlSessionFactory

public SqlSessionFactory build(InputStream inputStream) { try { Configuration.PROPS.load(inputStream);

}
catch (IOException e)
{
    e.printStackTrace();
}
return new DefaultSqlSessionFactory(new Configuration());

} 最后一句默认sqlSession工厂中扫描mapper.xml将解析后的节点信息存到configuration中。

下面的loadMappersInfo方法中使用dom4j解析xml。

public DefaultSqlSessionFactory(Configuration configuration) { this.configuration = configuration; loadMappersInfo(Configuration.getProperty(Constant.MAPPER_LOCATION).replaceAll("\.", "/")); }

private void loadMappersInfo(String dirName) { //... XmlUtil.readMapperXml(file, this.configuration); //... }

/** XmlUtil类 **/ public static void readMapperXml(File fileName, Configuration configuration) { //解析xml步骤省略 //... //设置SQL的唯一ID String sqlId = namespace + "." + element.attributeValue(Constant.XML_ELEMENT_ID);

for (Iterator iterator = rootElement.elementIterator(); iterator.hasNext();)
{
    //...
    statement.setSqlId(sqlId);
    statement.setNamespace(namespace);
    statement.setSql(CommonUtis.stringTrim(element.getStringValue()));
    statements.add(statement);

    System.out.println(statement);
    configuration.addMappedStatement(sqlId, statement);

    //这里其实是在MapperRegistry中生产一个mapper对应的代理工厂
    configuration.addMapper(Class.forName(namespace));
}

}

/** MapperRegistry类 **/ public void addMapper(Class type) { this.knownMappers.put(type, new MapperProxyFactory(type)); //这里生成了代理工厂 } 记住上面代码的最后两句设置MappedStatement和Mapper,后面有用。

 

SqlSession session = factory.openSession();

解释:把上面解析的configuration放在DefaultSqlSession中备用。从下面代码中可以看出,configuration一直传递到执行器里。

public SqlSession openSession() { SqlSession session = new DefaultSqlSession(this.configuration); return session; }

public DefaultSqlSession(Configuration configuration) { this.configuration = configuration; this.executor = new SimpleExecutor(configuration);

}

public SimpleExecutor(Configuration configuration) { this.conf = configuration; } UserMapper userMapper = session.getMapper(UserMapper.class);

解释:重点来了,这里开始从session中获取mapper,真正的还是代理工厂返回了mapper实例,获取过程看下面的代码分析。

/** DefaultSqlSession类 **/ @Override public T getMapper(Class type) { return configuration. getMapper(type, this); }

/** Configuration类 **/ public T getMapper(Class type, SqlSession sqlSession) { return this.mapperRegistry.getMapper(type, sqlSession); }

/** MapperRegistry类 **/ public T getMapper(Class type, SqlSession sqlSession) { //前面解析xml做了addMapper操作,这里通过mapper类获取代理工厂 MapperProxyFactory mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);

return mapperProxyFactory.newInstance(sqlSession);

}

/** MapperProxyFactory类 **/ public T newInstance(SqlSession sqlSession) { MapperProxy mapperProxy = new MapperProxy(sqlSession, this.mapperInterface); return newInstance(mapperProxy); }

/**

  • 根据mapper代理返回实例
  • @param mapperProxy
  • @return 代理实例 */ protected T newInstance(MapperProxy mapperProxy) { return (T)Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[] {this.mapperInterface}, mapperProxy); } User user = userMapper.getUser("1");

解释:mapper中具体方法的功能,都是代理的invoke完成的。MapperProxy都实现了InvocationHandler接口。

public class MapperProxy implements InvocationHandler, Serializable { //... public Object invoke(Object proxy, Method method, Object[] args) { //... return this.execute(method, args); }

private Object execute(Method method,  Object[] args)
{
    String statementId = this.mapperInterface.getName() + "." + method.getName();
    MappedStatement ms = this.sqlSession.getConfiguration().getMappedStatement(statementId);
    
    Object result = null;
    switch (ms.getSqlCommandType())
    {
        case SELECT:
        {
            Class<?> returnType = method.getReturnType();
            
            // 如果返回的是list,应该调用查询多个结果的方法,否则只要查单条记录
            if (Collection.class.isAssignableFrom(returnType))
            {
                //ID为mapper类全名+方法名
                result = sqlSession.selectList(statementId, args);
            }
            else
            {
                result = sqlSession.selectOne(statementId, args);
            }
            break;
        }
        //...
    }   
    return result;
}

} 我们查询一条记录,所以使用sqlSession.selectOne方法。代码实现如下

/** DefaultSqlSession类 **/ public T selectOne(String statementId, Object parameter) { //这里跟源码保持一致,源码也是调用selectList,返回第一个结果的 List results = this. selectList(statementId, parameter); return CommonUtis.isNotEmpty(results) ? results.get(0) : null; }

public List selectList(String statementId, Object parameter) { MappedStatement mappedStatement = this.configuration.getMappedStatement(statementId); return this.executor. doQuery(mappedStatement, parameter); //调用执行器 } 执行器最终调用了原生的JDBC操作数据库

/** SimpleExecutor类 **/ public List doQuery(MappedStatement ms, Object parameter) { try { //1.获取数据库连接 Connection connection = getConnect();

    //2.获取MappedStatement信息,里面有sql信息
    MappedStatement mappedStatement = conf.getMappedStatement(ms.getSqlId());
    
    //3.实例化StatementHandler对象,
    StatementHandler statementHandler = new SimpleStatementHandler(mappedStatement);
    
    //4.通过StatementHandler和connection获取PreparedStatement
    PreparedStatement preparedStatement = statementHandler.prepare(connection);
    
    //5.实例化ParameterHandler,将SQL语句中?参数化
    ParameterHandler parameterHandler = new DefaultParameterHandler(parameter);
    parameterHandler.setParameters(preparedStatement);
    
    //6.执行SQL,得到结果集ResultSet
    ResultSet resultSet = statementHandler.query(preparedStatement);
    
    //7.实例化ResultSetHandler,通过反射将ResultSet中结果设置到目标resultType对象中
    ResultSetHandler resultSetHandler = new DefaultResultSetHandler(mappedStatement);
    return resultSetHandler.handleResultSets(resultSet);
}
catch (Exception e)
{
    e.printStackTrace();
}
return null;

} 执行器中我注释的很详细,大家都能明白,这里就不继续粘贴里面的代码了。想看全部的代码,请到github上下载。

现在代码写完了,我们测试以下结果:

整个mybatis精简版框架的写作流程结束,希望您看完可以有所收获,谢谢!

作者:kuailebuzhidao 来源:CSDN 原文:https://blog.csdn.net/kuailebuzhidao/article/details/88355236 版权声明:本文为博主原创文章,转载请附上博文链接!

欢迎大家关注公众号,有资料有技术文章

欢迎大家关注公众号,有资料有技术文章

minimybatis's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.