Mybatis源码分析

 

Mybatis源码分析

目录

一、 架构图 

二、核心组件

三、源码分析 

1、mybatis-config.xml获取sqlSessionFactory对象 

2、根据sqlSessionFactory获取SqlSession对象

3、获取Mapper

4、mybatis执行SQL语句源码分析


一、 架构图 

二、核心组件

  • Configuration:用于描述 MyBatis 主配置文件信息,MyBatis 框架在启动时会加载主配置文件,将配置信息转换为 Configuration 对象。
  • SqlSession:面向用户的 API,是 MyBatis 与数据库交互的接口。
  • Executor:SQL 执行器,用于和数据库交互。SqlSession 可以理解为 Executor 组件的外观(外观模式),真正执行 SQL 的是 Executor 组件。
  • MappedStatement:用于描述 SQL 配置信息,MyBatis 框架启动时,XML 文件或者注解配置的 SQL 信息会被转换为 MappedStatement 对象注册到 Configuration 组件中。
  • StatementHandler:封装了对 JDBC 中 Statement 对象的操作,包括为 Statement 参数占位符设置值,通过 Statement 对象执行 SQL 语句。
  • TypeHandler:类型处理器,用于 Java 类型与 JDBC 类型之间的转换。
  • ParameterHandler:用于处理 SQL 中的参数占位符,为参数占位符设置值。
  • ResultSetHandler:封装了对 ResultSet 对象的处理逻辑,将结果集转换为 Java 实体对象

三、源码分析 

1、mybatis-config.xml获取sqlSessionFactory对象 

代码入口:

FileReader fr=new FileReader("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(fr);
SqlSession sqlSession = factory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//mapper.
userMapper.findById(3);

进入到SqlSessionFactoryBuilder.build方法可以看出是通过XMLConfigBuilder 来解析xml文件,XMLConfigBuilder.parse 方法将xml的解析的信息放入Configuration对象中,parse方法调用parseConfiguration来解析 mybatis配置文件信息

public Configuration parse() {
//防止parse()方法被一个实例多次调用
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
// XPathParser.evalNode():创建表示configuration节点的XNode对象
//parseConfiguration():对XNode进行处理
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}

            

private void parseConfiguration(XNode root) {
try {
// 解析<properties>节点
propertiesElement(root.evalNode("properties"));
// 解析<settings>节点
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
// 解析<typeAliases>节点
typeAliasesElement(root.evalNode("typeAliases"));
// 解析<plugins>节点
pluginElement(root.evalNode("plugins"));
// 解析<objectFactory>节点
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
// 解析<reflectorFactory>节点
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// 解析<environments>节点
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
// 解析<mappers>节点
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}}

2、根据sqlSessionFactory获取SqlSession对象

获取sqlSessionFactory对象之后,调用openSessionFromDataSource方法返回sqlSession对象, 该方法的重要操作有两步,第一步是根据我们设置的Environment获取到对应的事物,第二步是根据事物和 executor的type来构造一个Executor

/*
*execType 执行器类型 默认传入了ExecutorType.SIMPLE;
*level 事物的隔离级别 默认传null
* autoCommit 事物是否自动提交 默认为false
**/
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level,
boolean autoCommit) {
Transaction tx = null;
try {
//获取到配置中设置的当前环境 里面包含了环境id 数据库信息 和事物信息
//我们在构建的时候已经构建了一个environment
final Environment environment = configuration.getEnvironment();
//根据环境获取其事物级别,如果环境为空或者其事物级别为空则返回默认的ManagedTransactionFactory,
//我们使用SPring集成 所以会使用Spring的事物管理器 SpringManagedTransactionFactory
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
//根据数据源,事物隔离级别,是否自动提交生成事物管理器
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//根据事物和执行器类别生成执行器
//由于二级缓存默认打开 所以这儿的SimpleExecutor会被包装为CacheExecutor
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}

3、获取Mapper

DefaultSqlSession#getMapper

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

Configuration#getMapper

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
 return mapperRegistry.getMapper(type, sqlSession);
}

mapperRegistry.getMapper是从MapperRegistry的knownMappers里面取的,knownMappers里面存的是接口类型(interface mapper.UserMapper)和工厂类 MapperProxyFactory 

从knownMappers的Map里根据接口类型(interface mapper.UserMapper)取出对应的工厂类

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
 final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>)
knownMappers.get(type);
 if (mapperProxyFactory == null) {
 throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
 }
try {
 return mapperProxyFactory.newInstance(sqlSession);
 } catch (Exception e) {
 throw new BindingException("Error getting mapper instance. Cause: " + e, e);
 }
}

MapperProxyFactory#newInstance

public T newInstance(SqlSession sqlSession) {
 final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
 return newInstance(mapperProxy);
}
//通过JDK动态代理返回代理对象MapperProxy
protected T newInstance(MapperProxy<T> mapperProxy) {
 return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, map

4、mybatis执行SQL语句源码分析

获取到DefaultSqlSession和Mapper,那么sql怎么执行呢,我们回到MapperProxy.invoke方法: MapperProxy#invoke():

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
	try {
        //首先判断方式是不是Object类里面的方法(如:toString,hashCode),如果是Object类里面的方法就直接执行
		if (Object.class.equals(method.getDeclaringClass())) {
			return method.invoke(this, args);
		}
    //如果是接口默认方法
		if (this.isDefaultMethod(method)) {
			return this.invokeDefaultMethod(proxy, method, args);
		}
	} catch (Throwable var5) {
		throw ExceptionUtil.unwrapThrowable(var5);
	}
//如果是接口里面的方法,会包装成MapperMethod,执行execute方法
	MapperMethod mapperMethod = this.cachedMapperMethod(method);
	return mapperMethod.execute(this.sqlSession, args);
}

mapper.execute 方法

public Object execute(SqlSession sqlSession, Object[] args) {
        Object result;
        Object param;
        switch(this.command.getType()) {
        case INSERT:
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
            break;
        case UPDATE:
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
            break;
        case DELETE:
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
            break;
        case SELECT:
            if (this.method.returnsVoid() && this.method.hasResultHandler()) {
                this.executeWithResultHandler(sqlSession, args);
                result = null;
            } else if (this.method.returnsMany()) {
                result = this.executeForMany(sqlSession, args);
            } else if (this.method.returnsMap()) {
                result = this.executeForMap(sqlSession, args);
            } else if (this.method.returnsCursor()) {
                result = this.executeForCursor(sqlSession, args);
            } else {
                param = this.method.convertArgsToSqlCommandParam(args);
                result = sqlSession.selectOne(this.command.getName(), param);
                if (this.method.returnsOptional() && (result == null || !this.method.getReturnType().equals(result.getClass()))) {
                    result = Optional.ofNullable(result);
                }
            }
            break;
        case FLUSH:
            result = sqlSession.flushStatements();
            break;
        default:
            throw new BindingException("Unknown execution method for: " + this.command.getName());
        }

        if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
            throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");
        } else {
            return result;
        }
    }

获得SQL语句的类型,比如说select,从selectOne 入口 

把传进来的args参数转换成我们能用的参数 

// 把传进来的args参数转换成我们能用的参数
public Object convertArgsToSqlCommandParam(Object[] args) {
            return this.paramNameResolver.getNamedParams(args);
}


public Object getNamedParams(Object[] args) {
        int paramCount = this.names.size();
        if (args != null && paramCount != 0) {
            if (!this.hasParamAnnotation && paramCount == 1) {
                return args[(Integer)this.names.firstKey()];
            } else {
                Map<String, Object> param = new ParamMap();
                int i = 0;

                for(Iterator var5 = this.names.entrySet().iterator(); var5.hasNext(); ++i) {
                    Entry<Integer, String> entry = (Entry)var5.next();
                    param.put((String)entry.getValue(), args[(Integer)entry.getKey()]);
                    String genericParamName = "param" + String.valueOf(i + 1);
                    if (!this.names.containsValue(genericParamName)) {
                        param.put(genericParamName, args[(Integer)entry.getKey()]);
                    }
                }

                return param;
            }
        } else {
            return null;
        }
    }

再执行DefaultSqlSession#selectOne(statement,parameter)

public <T> T selectOne(String statement, Object parameter) {
        List<T> list = this.selectList(statement, parameter);
        if (list.size() == 1) {
            return list.get(0);
        } else if (list.size() > 1) {
            throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
        } else {
            return null;
        }
 }

public <E> List<E> selectList(String statement, Object parameter) {
//RowBounds 实现简单逻辑分页, 对ResultSet结果集进行分页
        return this.selectList(statement, parameter, RowBounds.DEFAULT);
 }

public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
        List var5;
        try {
//从configuration中获得MappedStatement对象,作为参数传给Executor.query()方法
            MappedStatement ms = this.configuration.getMappedStatement(statement);
            var5 = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
        } catch (Exception var9) {
            throw ExceptionFactory.wrapException("Error querying database.  Cause: " + var9, var9);
        } finally {
            ErrorContext.instance().reset();
        }

        return var5;
}

Executor#query

public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
        BoundSql boundSql = ms.getBoundSql(parameter);
        CacheKey key = this.createCacheKey(ms, parameter, rowBounds, boundSql);
        return this.query(ms, parameter, rowBounds, resultHandler, key, boundSql);
 }

 public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        ....................
//从以及缓存里面获取数据,判断是否有数据,如果有数据,直接从缓存中间去获取数据
                if (list != null) {
                    this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
                } else {
//如果缓存中没有数据,就调用queryFromDatabase()方法,从数据库查询
                    list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
                }
            } finally {
                --this.queryStack;
            }

        ....................
            return list;
        }
    }

 private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        this.localCache.putObject(key, ExecutionPlaceholder.EXECUTION_PLACEHOLDER);

        List list;
        try {
            list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
        } finally {
            this.localCache.removeObject(key);
        }
     //将查询的结果放入缓存
        this.localCache.putObject(key, list);
        if (ms.getStatementType() == StatementType.CALLABLE) {
            this.localOutputParameterCache.putObject(key, parameter);
        }

        return list;
    }

最终调用SimpleExecutor的doQuery方法

    public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        Statement stmt = null; //原生JDBC对象

        List list;
        try {
            Configuration configuration = ms.getConfiguration();
//创建StatementHandler 对象,由StatementHandler生成Statement
            StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
            stmt = this.prepareStatement(handler, ms.getStatementLog());
            list= handler.query(stmt, resultHandler);
        } finally {
            this.closeStatement(stmt);
        }

        return list;
    }

好了,到这里,会继续交给StatementHandler去执行

//StatementHandler有2个实现类,BaseStatementHandler和RoutingStatementHandler
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
        StatementHandler statementHandler = (StatementHandler)this.interceptorChain.pluginAll(statementHandler);
        return statementHandler;
 }

  public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        switch(ms.getStatementType()) {
        case STATEMENT:
//管理 Statement 对象并向数据库中推送不需要预编译的SQL语句
            this.delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
        case PREPARED:
//管理 Statement 对象并向数据中推送需要预编译的SQL语句
            this.delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
        case CALLABLE:
//管理 Statement 对象并调用数据库中的存储过程
            this.delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
        default:
            throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
        }

    }

再来看看prepareStatement,如下

 private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
        Connection connection = this.getConnection(statementLog);
///
        Statement stmt = handler.prepare(connection, this.transaction.getTimeout());
        handler.parameterize(stmt);
        return stmt;
    }

//调用的是BaseStatementHandler.prepare 
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
        ErrorContext.instance().sql(this.boundSql.getSql());
        Statement statement = null;

        try {
//
            statement = this.instantiateStatement(connection);
            this.setStatementTimeout(statement, transactionTimeout);
            this.setFetchSize(statement);
            return statement;
        } catch (SQLException var5) {
            this.closeStatement(statement);
            throw var5;
        } catch (Exception var6) {
            this.closeStatement(statement);
            throw new ExecutorException("Error preparing statement.  Cause: " + var6, var6);
        }
    }


protected Statement instantiateStatement(Connection connection) throws SQLException {
        String sql = this.boundSql.getSql();
        if (this.mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
            String[] keyColumnNames = this.mappedStatement.getKeyColumns();
//最终是调用JDBC的connection.prepareStatement方法绑定执行sql并得到Statement
            return keyColumnNames == null ? connection.prepareStatement(sql, 1) : connection.prepareStatement(sql, keyColumnNames);
        } else {
            return this.mappedStatement.getResultSetType() == ResultSetType.DEFAULT ? connection.prepareStatement(sql) : connection.prepareStatement(sql, this.mappedStatement.getResultSetType().getValue(), 1007);
        }
    }

再回来看handler.parameterize(stmt);主要是把入参绑定到Statement里

@Override
public void parameterize(Statement statement) throws SQLException {
parameterHandler.setParameters((PreparedStatement) statement);
}

在这里其实是交给ParameterHandler的唯一实现类DefaultParameterHandler来做绑定参数的工作, DefaultParameterHandler.setParameters方法如下

 public void setParameters(PreparedStatement ps) {
       .......................
        List<ParameterMapping> parameterMappings = this.boundSql.getParameterMappings();
        if (parameterMappings != null) {
//在这里,通过boundSql获取到参数集合,然后循环集合,获取到TypeHandler,在通过对应的TypeHandler绑定入参
            for(int i = 0; i < parameterMappings.size(); ++i) {
                ParameterMapping parameterMapping = (ParameterMapping)parameterMappings.get(i);
                if (parameterMapping.getMode() != ParameterMode.OUT) {
                    .................
                    TypeHandler typeHandler = parameterMapping.getTypeHandler();
                    JdbcType jdbcType = parameterMapping.getJdbcType();
                    if (value == null && jdbcType == null) {
                        jdbcType = this.configuration.getJdbcTypeForNull();
                    }

                    try {
//参数绑定到PreparedStatement中
                        typeHandler.setParameter(ps, i + 1, value, jdbcType);
                    } catch (TypeException var10) {
                        throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + var10, var10);
                    } catch (SQLException var11) {
                        throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + var11, var11);
                    }
                }
            }
        }

    }

回到SimpleExecutor.doQuery,看一下StatementHandler.query,看实现类PreparedStatementHandler.query,如下

@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.handleResultSets(ps);
}

这里直接就执行了!!!然后由ResultHandler.handleResultSets处理执行结果,调用的是DefaultResultSetHandler

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

numun

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值