一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎全部的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可使用简单的 XML 或注解来配置和映射原生类型、接口和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。(摘抄至官网)html
前言:若是您对MyBatis的底层感兴趣,想知道发起一条Sql语句执行,底层走了什么操做,那您能够花点时间,认真阅读下本篇文章,相信会给你很多收获的。本篇文章步骤条例很清晰的。java
public static void main(String[] args) throws IOException { String configName = "mybatis_config.xml"; Reader reader = Resources.getResourceAsReader(configName); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); }
如图所示
web
建立SqlSessionFactory对象实例
sql
解析 mybatis-config.xml文件数据库
public class XMLConfigBuilder extends BaseBuilder { /* 标记是否已经解析过配置文件 */ private boolean parsed; /* 解析器 */ private final XPathParser parser; /** * 数据源,SqlSessionFactoryBuilder.build(Reader reader, String environment, Properties properties) * 不指定为空 */ private String environment; public XMLConfigBuilder(XPathParser parser, String environment, Properties props) { /* 初始化 Configuration */ super(new Configuration()); ErrorContext.instance().resource("SQL Mapper Configuration"); /* 设置格外的属性 */ this.configuration.setVariables(props); /* 标记初始化为 false */ this.parsed = false; this.environment = environment; this.parser = parser; } }
建立 Configuration 实例apache
public class Configuration { /** * 类型别名注册 * 好比 <dataSource type="POOLED"> * 其中的 type="POOLED" 会使用 PooledDataSourceFactory类建立数据源链接池 */ protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry(); protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry(); public Configuration() { /** * JDBC 对应使用的 事务工厂类 * <transactionManager type="JDBC"></transactionManager> */ typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class); /** * MANAGED 对应使用的 事务工厂类 */ typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class); /** * JNDI 对应使用的 数据源 */ typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class); /** * POOLED 对应使用的 数据源 * <dataSource type="POOLED"> */ typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class); /** * UNPOOLED 对应使用的 数据源 */ typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class); /** * 缓存策略 */ typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class); typeAliasRegistry.registerAlias("FIFO", FifoCache.class); typeAliasRegistry.registerAlias("LRU", LruCache.class); typeAliasRegistry.registerAlias("SOFT", SoftCache.class); typeAliasRegistry.registerAlias("WEAK", WeakCache.class); typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class); typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class); typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class); /** * 日志 */ typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class); typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class); typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class); typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class); typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class); typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class); typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class); typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class); typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class); languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class); languageRegistry.register(RawLanguageDriver.class); } }
解析 mybatis-config.xml
数组
private void parseConfiguration(XNode root) { try { //issue #117 read properties first /** * 解析 <properties resource="my.properties" /> 配置文件 */ propertiesElement(root.evalNode("properties")); /** * 解析settings配置文件 * <settings> * <setting name="logImpl" value="STDOUT_LOGGING"/> * </settings> */ Properties settings = settingsAsProperties(root.evalNode("settings")); loadCustomVfs(settings); loadCustomLogImpl(settings); /** * 解析 typeAliases配置文件 */ typeAliasesElement(root.evalNode("typeAliases")); /** * 解析 plugins 配置文件 * 这个是插件,能够动态的拦截sql执行 */ pluginElement(root.evalNode("plugins")); objectFactoryElement(root.evalNode("objectFactory")); objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); reflectorFactoryElement(root.evalNode("reflectorFactory")); settingsElement(settings); // read it after objectFactory and objectWrapperFactory issue #631 environmentsElement(root.evalNode("environments")); databaseIdProviderElement(root.evalNode("databaseIdProvider")); typeHandlerElement(root.evalNode("typeHandlers")); /** * 加载 mapper.xml 文件 */ mapperElement(root.evalNode("mappers")); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); } }
解析mapper.xml文件,以及接口方法的注解缓存
public class XMLConfigBuilder extends BaseBuilder { /** * mapper隐射文件解析 * @param parent * @throws Exception */ private void mapperElement(XNode parent) throws Exception { if (parent != null) { for (XNode child : parent.getChildren()) { /** * 解析 <package * <mappers> * <package name="com.mapper"/> * </mappers> */ if ("package".equals(child.getName())) { String mapperPackage = child.getStringAttribute("name"); /** * 添加全部包下的接口 * 实际调用 configuration.addMapper(mapperInterface); */ configuration.addMappers(mapperPackage); } else { String resource = child.getStringAttribute("resource"); String url = child.getStringAttribute("url"); String mapperClass = child.getStringAttribute("class"); /** * 解析 <mapper resource * <mappers> * <mapper resource="mapper/UserMapper.xml"/> * </mappers> */ if (resource != null && url == null && mapperClass == null) { ErrorContext.instance().resource(resource); InputStream inputStream = Resources.getResourceAsStream(resource); /** * mapper.xml 解析器 */ XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments()); /** * 开始解析 mapper.xml文件 * 重点分析这个 一样也会调用 configuration.addMapper(mapperInterface); 这个方法 */ mapperParser.parse(); } else if (resource == null && url != null && mapperClass == null) { /** * 解析 <mapper url */ ErrorContext.instance().resource(url); InputStream inputStream = Resources.getUrlAsStream(url); XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments()); mapperParser.parse(); } else if (resource == null && url == null && mapperClass != null) { /** * 解析 <mapper class */ Class<?> mapperInterface = Resources.classForName(mapperClass); configuration.addMapper(mapperInterface); } else { /** * 如下代码能够看出 url resource class 三个属性只能选择一个,不然就会报错 */ throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one."); } } } } } }
个人UserMapper.xml文件内容为:bash
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.mapper.UserMapper"> <select id="findById" resultType="com.entity.User"> select * from `user` where userId = #{userId} </select> </mapper>
UserMapper接口内容为:session
package com.mapper; import com.entity.User; import org.apache.ibatis.annotations.Select; public interface UserMapper { @Select("select * from `user` where userId = 2") User findById(int userId); }
疑问?UserMapper.xml有<select id=“findById”,而在接口中的findById方法我又加了一个@Select注解;那么执行会选择哪一条Sql执行仍是报错呢?
以UserMapper.xml为例子解析,能够看到 resource = mapper/UserMapper.xml
public class XMLMapperBuilder extends BaseBuilder { public void parse() { if (!configuration.isResourceLoaded(resource)) { /** * 解析 mapper.xml文件内容 */ configurationElement(parser.evalNode("/mapper")); configuration.addLoadedResource(resource); /** * 解析 mapper.xml的<mapper namespace="com.mapper.UserMapper"> * namespace指定的{ UserMapper}接口的注解信息 */ bindMapperForNamespace(); } parsePendingResultMaps(); parsePendingCacheRefs(); parsePendingStatements(); } }
解析 mapper.xml文件内容
public class XMLMapperBuilder extends BaseBuilder { private void configurationElement(XNode context) { try { /** * namespace属性 */ String namespace = context.getStringAttribute("namespace"); if (namespace == null || namespace.equals("")) { /** * 不指定 namespace会报错哦 由此得知 namespace属性是必须指定的 */ throw new BuilderException("Mapper's namespace cannot be empty"); } builderAssistant.setCurrentNamespace(namespace); /** * 解析 cache-ref */ cacheRefElement(context.evalNode("cache-ref")); /** * 解析 cache */ cacheElement(context.evalNode("cache")); /** * 解析 parameterMap */ parameterMapElement(context.evalNodes("/mapper/parameterMap")); /** * 解析 resultMap */ resultMapElements(context.evalNodes("/mapper/resultMap")); /** * 解析 sql */ sqlElement(context.evalNodes("/mapper/sql")); /** * 解析 sql语句 select|insert|update|delete * 重点分析这里,这里的解析会关联到 mapper接口的执行方法 sql语句映射 */ buildStatementFromContext(context.evalNodes("select|insert|update|delete")); } catch (Exception e) { throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e); } } }
解析 sql语句 select|insert|update|delete;list参数内容是select|insert|update|delete的Sql语句
XMLStatementBuilder Sql语句的解析器
builderAssistant.addMappedStatement,并非添加一个mapper.xml文件隐射的实例,而是为每个Sql语句建立一个实例
public class XMLStatementBuilder extends BaseBuilder { private final MapperBuilderAssistant builderAssistant; public void parseStatementNode() { /** * 此处省略一大推代码... */ /** * <select id="findById" resultType="com.entity.User"> * select * from `user` where userId = #{userId} * </select> * 参数解析 * * id:标签指定的id = findById * sqlSource:Sql语句,Sql参数占位 * statementType:sql执行类型 参考{ @link StatementType} * STATEMENT: 直接操做sql,不进行预编译 ${ } * PREPARED: 预处理,参数,进行预编译 #{} * CALLABLE: 执行存储过程 * sqlCommandType:sql语句类型 参考{ @link SqlCommandType} * UNKNOWN:未知,INSERT:新增,UPDATE:修改,DELETE:删除,SELECT:查询,FLUSH:刷新 * * 其余参数可查看官网:https://mybatis.org/mybatis-3/zh/sqlmap-xml.html */ builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets); } }
建立一个MappedStatement实例添加到 Configuration.mappedStatements的Map集合中
public class XMLStatementBuilder extends BaseBuilder { public MappedStatement addMappedStatement() { /** * 此处省略一大推代码... */ MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType) /** * 建造者模式 * 用于设置 MappedStatement的属性 * 此处省略一大推代码... */ /** * 设置参数入参类型 parameterType属性 * <select id="findById" parameterType="int" resultType="com.entity.User"> * select * from `user` where userId = #{userId} * </select> */ ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id); if (statementParameterMap != null) { statementBuilder.parameterMap(statementParameterMap); } /** * 建立一个 { @link MappedStatement} 实例 */ MappedStatement statement = statementBuilder.build(); /** * MappedStatement实例添加到 { @link #configuration.mappedStatements} Map集合中 * MappedStatement 是对应一个Sql语句的实例对象 * * configuration.mappedStatements 存放全部的MappedStatement实例,后面会详细介绍 */ configuration.addMappedStatement(statement); return statement; } }
开始解析接口注解,并添加一个MapperProxyFactory代理工厂的对象到configuration.mapperRegistry.knownMappers;key是Mapper接口
public class XMLMapperBuilder extends BaseBuilder { private void bindMapperForNamespace() { String namespace = builderAssistant.getCurrentNamespace(); if (namespace != null) { Class<?> boundType = null; try { /** * java 反射 Class.classForName */ boundType = Resources.classForName(namespace); } catch (ClassNotFoundException e) { /** * 这里并无抛出异常,说明 namespace 能够指定一个不存在的接口 */ //ignore, bound type is not required 忽略,绑定类型不是必需的 } if (boundType != null) { if (!configuration.hasMapper(boundType)) { // Spring may not know the real resource name so we set a flag // to prevent loading again this resource from the mapper interface // look at MapperAnnotationBuilder#loadXmlResource configuration.addLoadedResource("namespace:" + namespace); /** * 添加一个Mapper接口的代理工厂对象到configuration.mapperRegistry.knownMappers集合中 * 参考 { @link Configuration#mapperRegistry}, * { @link MapperRegistry#knownMappers} */ configuration.addMapper(boundType); } } } } }
public class Configuration { protected final MapperRegistry mapperRegistry = new MapperRegistry(this); public <T> void addMapper(Class<T> type) { /** * mapperRegistry = { @link MapperRegistry} mapper接口注册器,存放全部的mapper接口信息 */ mapperRegistry.addMapper(type); } }
mapperRegistry.addMapper(type);
为Mapper接口建立一个代理工厂,方便后期使用Mapper接口时建立代理类
解析mapper接口的注解信息
public class MapperRegistry { public <T> void addMapper(Class<T> type) { if (type.isInterface()) { if (hasMapper(type)) { throw new BindingException("Type " + type + " is already known to the MapperRegistry."); } boolean loadCompleted = false; try { /** * { @link MapperProxyFactory} 代理接口工厂 */ knownMappers.put(type, new MapperProxyFactory<>(type)); // It's important that the type is added before the parser is run // otherwise the binding may automatically be attempted by the // mapper parser. If the type is already known, it won't try. /** * { @link MapperAnnotationBuilder} mapper接口注解解析器 */ MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type); /* 开始解析 */ parser.parse(); loadCompleted = true; } finally { if (!loadCompleted) { knownMappers.remove(type); } } } } }
解析mapper接口的注解信息,parseStatement(method)主要在这个方法中完成
public class MapperAnnotationBuilder { public void parse() { String resource = type.toString(); if (!configuration.isResourceLoaded(resource)) { loadXmlResource(); configuration.addLoadedResource(resource); assistant.setCurrentNamespace(type.getName()); /** * 解析 @CacheNamespace 注解 */ parseCache(); /** * 解析 CacheNamespaceRef 注解 */ parseCacheRef(); Method[] methods = type.getMethods(); for (Method method : methods) { try { // issue #237 if (!method.isBridge()) { /** * 解析Sql相关注解 列如 @Select|@Update 之类的注解 * * 重点关注 */ parseStatement(method); } } catch (IncompleteElementException e) { configuration.addIncompleteMethod(new MethodResolver(this, method)); } } } /** * 解析待定方法 */ parsePendingMethods(); } }
建立一个MappedStatement实例添加到 Configuration.mappedStatements的Map集合中
public class MapperAnnotationBuilder { private final MapperBuilderAssistant assistant; void parseStatement(Method method) { /** * 此处省略一大推代码... */ assistant.addMappedStatement( mappedStatementId, sqlSource, statementType, sqlCommandType, fetchSize, timeout, // ParameterMapID null, parameterTypeClass, resultMapId, getReturnType(method), resultSetType, flushCache, useCache, // TODO gcode issue #577 false, keyGenerator, keyProperty, keyColumn, // DatabaseID null, languageDriver, // ResultSets options != null ? nullOrEmpty(options.resultSets()) : null); } }
这里能够参考1.4.6:builderAssistant.addMappedStatement();如出一辙的操做
都调用了configuration.addMappedStatement(statement);
这里重点分析Configuration.addMappedStatement(statement);在作什么操做,而且解决 1.4.2留下的疑点;UserMapper.xml和UserMapper接口都有findById的Sql语句定义
public class Configuration { public void addMappedStatement(MappedStatement ms) { mappedStatements.put(ms.getId(), ms); } }
mappedStatements.put(ms.getId(), ms); 实际调用 Configuration.StrictMap.put()方法
Configuration.StrictMap是一个重写的HashMap,put方法会先校验key是否存在
public class Configuration { /** * mappedStatements Sql语句的对象 * * Configuration.StrictMap 实现了HashMap */ protected final Map<String, MappedStatement> mappedStatements = new Configuration.StrictMap<MappedStatement>("Mapped Statements collection") .conflictMessageProducer((savedValue, targetValue) -> ". please check " + savedValue.getResource() + " and " + targetValue.getResource()); protected static class StrictMap<V> extends HashMap<String, V> { @Override @SuppressWarnings("unchecked") public V put(String key, V value) { /** * key 是否存在 存在就抛出异常 * * 由此得知,方法映射Sql语句只能定义一个,要么在 mapper.xml中定义,要么就注解定义 */ if (containsKey(key)) { throw new IllegalArgumentException(name + " already contains value for " + key + (conflictMessageProducer == null ? "" : conflictMessageProducer.apply(super.get(key), value))); } if (key.contains(".")) { final String shortKey = getShortName(key); if (super.get(shortKey) == null) { super.put(shortKey, value); } else { super.put(shortKey, (V) new org.apache.ibatis.session.Configuration.StrictMap.Ambiguity(shortKey)); } } return super.put(key, value); } } }
debug调试,key已经存在,就会报错。由此得知,方法映射Sql语句只能定义一个,要么在 mapper.xml中定义,要么就注解定义
public class Main { public static void main(String[] args) throws IOException { String configName = "mybatis_config.xml"; Reader reader = Resources.getResourceAsReader(configName); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); /** * 获取一个会话链接 */ SqlSession sqlSession = sqlSessionFactory.openSession(); /** * 拿到 UserMapper 的代理类 */ UserMapper userMapper = sqlSession.getMapper(UserMapper.class); /** * 执行Sql查询 */ User user = userMapper.findById(1); System.out.println(user); } }
输出结果:User{userId=1, username=‘张三’, sex=‘男’, age=12}
一行代码的查询,底层既然走了那么多流程;
调用DefaultSqlSessionFactory.openSessionFromDataSource();
public class DefaultSqlSessionFactory implements SqlSessionFactory { private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; try { /** * mybatis_config.xml配置的 * <environment>数据源<environment/> */ final Environment environment = configuration.getEnvironment(); /** * transactionManager 配置的事务管理器工厂 type="JDBC" { @link JdbcTransactionFactory} *<environments default="developmentss"> * <environment id="developmentss"> * <transactionManager type="JDBC"></transactionManager> * </environment> * </environments> */ final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); /** * 建立事务管理器,因为上面指定的事务管理器工厂是 { @link JdbcTransactionFactory} * 因此建立的事务管理器是 { @link JdbcTransaction} * * @param level 事务隔离级别 * @param autoCommit 是否自动提交事务 */ tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); /** * 建立Sql执行器 * * @param execType 建立执行器类型 defaultExecutorType若是不指定 默认就是 SIMPLE * <settings> * <setting name="defaultExecutorType" value="SIMPLE"/> * </settings> */ final Executor executor = configuration.newExecutor(tx, execType); /** * 建立一个默认的 SqlSession实例 */ 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(); } } }
public class Configuration { public Executor newExecutor(Transaction transaction, ExecutorType executorType) { executorType = executorType == null ? defaultExecutorType : executorType; executorType = executorType == null ? ExecutorType.SIMPLE : executorType; Executor executor; if (ExecutorType.BATCH == executorType) { /** * 执行器不只重用语句还会执行批量更新 */ executor = new BatchExecutor(this, transaction); /** * 执行器会重用预处理语句(PreparedStatement) */ } else if (ExecutorType.REUSE == executorType) { executor = new ReuseExecutor(this, transaction); } else { /** * 普通的执行器 也是默认的执行器 */ executor = new SimpleExecutor(this, transaction); } /** * 若是开启了二级缓存 cacheEnabled,建立一个CachingExecutor缓存执行器 * cacheEnabled 默认为true * <settings> * <setting name="cacheEnabled" value="true"/> * </settings> */ if (cacheEnabled) { executor = new CachingExecutor(executor); } executor = (Executor) interceptorChain.pluginAll(executor); return executor; } }
获取Mapper接口代理类实例
public class DefaultSqlSession implements SqlSession { @Override public <T> T getMapper(Class<T> type) { /** * 用 Configuration 类的 getMapper方法 */ return configuration.getMapper(type, this); } }
public class Configuration { public <T> T getMapper(Class<T> type, SqlSession sqlSession) { /** * 调用 MapperRegistry Mapper接口注册器 */ return mapperRegistry.getMapper(type, sqlSession); } }
public class MapperRegistry { private final Configuration config; private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>(); public <T> T getMapper(Class<T> type, SqlSession sqlSession) { /** * knownMappers 从缓存中获取 MapperProxyFactory Mapper接口代理工厂 * 若是没有找到就会抛出异常, * 说明获取Mapper接口代理实例时,须要事先定义好 --> 至关于Spring的扫包 */ 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); } } }
InvocationHandler是MapperProxy
public class MapperProxyFactory<T> { private final Class<T> mapperInterface; /** * MapperMethod 缓存 */ private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<>(); public MapperProxyFactory(Class<T> mapperInterface) { this.mapperInterface = mapperInterface; } @SuppressWarnings("unchecked") protected T newInstance(MapperProxy<T> mapperProxy) { /** * JDK 生产代理类 */ return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); } public T newInstance(SqlSession sqlSession) { /** * 代理类回调接口 */ final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); } }
会走代理类 MapperProxy.invoke
public class MapperProxy<T> implements InvocationHandler, Serializable { private static final long serialVersionUID = -6424540398559729838L; private final SqlSession sqlSession; private final Class<T> mapperInterface; private final Map<Method, MapperMethod> methodCache; public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) { this.sqlSession = sqlSession; this.mapperInterface = mapperInterface; this.methodCache = methodCache; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this, args); } else if (method.isDefault()) { return invokeDefaultMethod(proxy, method, args); } } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } /** * 根据方法全限名 获取一个MapperMethod实例,而且缓存 * * 注意:这里的 methodCache 只是一个引用,缓存的全部对象都在 { @link MapperProxyFactory#methodCache}中 */ final MapperMethod mapperMethod = cachedMapperMethod(method); /** * 开始执行 */ return mapperMethod.execute(sqlSession, args); } /** * 添加到缓存 methodCache { @link MapperProxyFactory#methodCache}中 * computeIfAbsent HashMap 存在就获取,不存在就新增 * @param method * @return */ private MapperMethod cachedMapperMethod(Method method) { return methodCache.computeIfAbsent(method, k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration())); } }
执行Sql语句查询,因为个人返回结果是一个 User对象,因此会走到
result = sqlSession.selectOne(command.getName(), param);这一行,查询一条记录
实际走到 DefaultSqlSession.selectOne()
public class MapperMethod { private final org.apache.ibatis.binding.MapperMethod.SqlCommand command; private final org.apache.ibatis.binding.MapperMethod.MethodSignature method; public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) { this.command = new org.apache.ibatis.binding.MapperMethod.SqlCommand(config, mapperInterface, method); this.method = new org.apache.ibatis.binding.MapperMethod.MethodSignature(config, mapperInterface, method); } /** * 开始执行Sql查询 */ public Object execute(SqlSession sqlSession, Object[] args) { Object result; switch (command.getType()) { case INSERT: { // 省略代码... 执行 insert语句 <insert>/@Insert break; } case UPDATE: { // 省略代码... 执行 insert语句 <update>/@Update break; } case DELETE: { // 省略代码... 执行 delete语句 <delete>/@Delete break; } case SELECT: // 执行 select语句 <select>/@Select if (method.returnsVoid() && method.hasResultHandler()) { // 返回类型是否为空 通常状况作Sql操做都要有返回结果的 executeWithResultHandler(sqlSession, args); result = null; } else if (method.returnsMany()) { /** * 是否返回多个结果集 { @link Collection}集合/数组 */ result = executeForMany(sqlSession, args); } else if (method.returnsMap()) { /** * 返回类型是否为 Map 集合 */ result = executeForMap(sqlSession, args); } else if (method.returnsCursor()) { /** * 返回类型是不是 游标 { @link org.apache.ibatis.cursor.Cursor} */ result = executeForCursor(sqlSession, args); } else { /** * 将参数转换为Sql命令参数 */ Object param = method.convertArgsToSqlCommandParam(args); /** * 发起查询 调用的是 sqlSession中的方法 */ result = sqlSession.selectOne(command.getName(), param); if (method.returnsOptional() && (result == null || !method.getReturnType().equals(result.getClass()))) { result = Optional.ofNullable(result); } } break; case FLUSH: // 刷新 result = sqlSession.flushStatements(); break; default: throw new BindingException("Unknown execution method for: " + command.getName()); } if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) { throw new BindingException("Mapper method '" + command.getName() + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ")."); } return result; } }
DefaultSqlSession.selectOne()能够看出实际是调用selectList(),并且若是返回了多个结果集就会报错。错误信息以下
Expected one result (or null) to be returned by selectOne(), but found: 2
查询多结果集
public class DefaultSqlSession implements SqlSession { /** * * @param statement 方法全限名 好比:com.mapper.UserMapper.findById * @param parameter 参数 * @param rowBounds 分页 * @param <E> * @return */ @Override public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { try { /** * 根据方法全限名在 configuration.mappedStatements缓存集合中拿到方法对应的Sql Statement对象实例 */ MappedStatement ms = configuration.getMappedStatement(statement); /** * 使用执行器执行 由当前设置的执行器执行 * <setting name="defaultExecutorType" value="SIMPLE"/> * * <setting name="cacheEnabled" value="true"/> * 注意:cacheEnabled因为开启二级缓存默认为true,会先使用 { @link CachingExecutor} 缓存执行器查询 */ return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); } catch (Exception e) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } } }
执行器的建立查看 2.2:configuration.newExecutor(tx, execType);建立执行器
/** * 二级缓存执行器 */ public class CachingExecutor implements Executor { @Override public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { /** * 获取指定的二级缓存 * mapper.xml 指定的 <cache type="com.domain.something.MyCustomCache"/> * Mapper接口的 @CacheNamespace */ Cache cache = ms.getCache(); if (cache != null) { /* 是否须要刷新二级缓存 */ flushCacheIfRequired(ms); if (ms.isUseCache() && resultHandler == null) { ensureNoOutParams(ms, boundSql); @SuppressWarnings("unchecked") /** * 获取二级缓存 */ List<E> list = (List<E>) tcm.getObject(cache, key); if (list == null) { /** * 若是没有数据查询Sql */ list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); /** * 设置缓存 */ tcm.putObject(cache, key, list); // issue #578 and #116 } return list; } } /** * delegate = SimpleExecutor */ return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); } }
由于我没有指定二级缓存,因此直接走向delegate.query, 由于SimpleExecutor继承了BaseExecutor可是没有重写query方法,因此走的是BaseExecutor.query()
public abstract class BaseExecutor implements Executor { @Override public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId()); if (closed) { throw new ExecutorException("Executor was closed."); } /** * 是否须要清空一级缓存 flushCache设置为true生效 * <select flushCache="true"></select> */ if (queryStack == 0 && ms.isFlushCacheRequired()) { clearLocalCache(); } List<E> list; try { // 防止多线程重复调用处理 queryStack++; /** * localCache.getObject(key) 获取一级缓存 * { @link BaseExecutor.localCache} 类型 org.apache.ibatis.cache.impl.PerpetualCache */ list = resultHandler == null ? (List<E>) localCache.getObject(key) : null; if (list != null) { handleLocallyCachedOutputParameters(ms, key, parameter, boundSql); } else { /** * 发起数据库查询 */ list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); } } finally { queryStack--; } if (queryStack == 0) { for (org.apache.ibatis.executor.BaseExecutor.DeferredLoad deferredLoad : deferredLoads) { deferredLoad.load(); } // issue #601 deferredLoads.clear(); if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) { // issue #482 clearLocalCache(); } } return list; } }
数据库查询 会执行到子类的doQuery()方法,这里的字类是SimpleExecutor 因此执行 SimpleExecutor.doQuery()
public abstract class BaseExecutor implements Executor { private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { List<E> list; /** * 一级缓存占位 */ localCache.putObject(key, EXECUTION_PLACEHOLDER); try { /** * 调用子类的 doQuery()方法 * * 这里的字类是SimpleExecutor 因此执行 SimpleExecutor.doQuery() */ list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql); } finally { localCache.removeObject(key); } /** * 添加一级缓存 */ localCache.putObject(key, list); if (ms.getStatementType() == StatementType.CALLABLE) { localOutputParameterCache.putObject(key, parameter); } return list; } }
执行数据库查询
public class SimpleExecutor extends BaseExecutor { public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { Statement stmt = null; try { Configuration configuration = ms.getConfiguration(); /** * 建立 RoutingStatementHandler Sql语句执行类型处理器 */ StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); /** * 建立 java.sql.Statement */ stmt = prepareStatement(handler, ms.getStatementLog()); /** * 发起数据库查询 */ return handler.query(stmt, resultHandler); } finally { closeStatement(stmt); } } }
建立 RoutingStatementHandler Sql语句执行类型处理器
public class RoutingStatementHandler implements StatementHandler { private final StatementHandler delegate; public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { switch (ms.getStatementType()) { case STATEMENT: /** * 简单的Sql执行处理器 */ delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); break; case PREPARED: /** * 预编译的Sql执行处理器 * 这是默认的 */ delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); break; case CALLABLE: /** * 存储过程执行的Sql执行处理器 */ delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); break; default: throw new ExecutorException("Unknown statement type: " + ms.getStatementType()); } } }
建立 java.sql.Statement
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Statement stmt; Connection connection = getConnection(statementLog); stmt = handler.prepare(connection, transaction.getTimeout()); handler.parameterize(stmt); return stmt; }
delegate 参考2.9.2:configuration.newStatementHandler()
public class RoutingStatementHandler implements StatementHandler { private final StatementHandler delegate; @Override public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { /** * 当前的 delegate = PreparedStatementHandler */ return delegate.query(statement, resultHandler); } }
执行 PreparedStatementHandler.query()
public class PreparedStatementHandler extends BaseStatementHandler { @Override public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; /** * 发起数据库查询 */ ps.execute(); /** * 封装结果集 resultSetHandler = DefaultResultSetHandler */ return resultSetHandler.handleResultSets(ps); } }
封装结果集
public class DefaultResultSetHandler implements ResultSetHandler { /** * 封装结果集 * @param stmt * @return * @throws SQLException */ @Override public List<Object> handleResultSets(Statement stmt) throws SQLException { ErrorContext.instance().activity("handling results").object(mappedStatement.getId()); final List<Object> multipleResults = new ArrayList<>(); int resultSetCount = 0; /** * 包装 ResultSet */ ResultSetWrapper rsw = getFirstResultSet(stmt); List<ResultMap> resultMaps = mappedStatement.getResultMaps(); int resultMapCount = resultMaps.size(); validateResultMapsCount(rsw, resultMapCount); while (rsw != null && resultMapCount > resultSetCount) { ResultMap resultMap = resultMaps.get(resultSetCount); handleResultSet(rsw, resultMap, multipleResults, null); rsw = getNextResultSet(stmt); cleanUpAfterHandlingResultSet(); resultSetCount++; } String[] resultSets = mappedStatement.getResultSets(); if (resultSets != null) { while (rsw != null && resultSetCount < resultSets.length) { ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]); if (parentMapping != null) { String nestedResultMapId = parentMapping.getNestedResultMapId(); ResultMap resultMap = configuration.getResultMap(nestedResultMapId); handleResultSet(rsw, resultMap, null, parentMapping); } rsw = getNextResultSet(stmt); cleanUpAfterHandlingResultSet(); resultSetCount++; } } /** * 包装成 List */ return collapseSingleResultList(multipleResults); } }
到这里MyBatis的Sql查询就结束了,同理添加,修改,删除也是一样的流程。
能够本身跟着以上步骤,断点调试追踪下源码,一切迷雾都将会拨开的。
以为对您有帮助,就点个赞呗。😀