Mybatis源码学习

文章目录

  • Mybatis源码学习
  • 一、Mybatis架构设计
  • 二、源码剖析
    • 1.如何解析的全局配置文件
      • 解析配置文件源码流程
    • 2.如何解析的映射配置文件
    • Select
    • insert, update 和 delete
    • 动态sql
      • 解析映射配置文件源码流程
    • 3.SqlSource创建流程
      • 相关类及对象
      • SqlSource创建流程
    • 4.SqlSession执行主流程
      • 相关类与接口
    • 5.如何设置的参数
    • 6.结果集映射流程
    • 7.获取Mapper代理对象流程
    • 8.invoke方法

一、Mybatis架构设计

mybatis架构四层作用

  • Api接口层:提供API 增加、删除、修改、查询等接口,通过API接口对数据库进行操作。
  • 数据处理层:主要负责SQL的 查询、解析、执行以及结果映射的处理,主要作用解析sql根据调用请求完成一次数据库操作.
  • 框架支撑层:负责通用基础服务支撑,包含事务管理、连接池管理、缓存管理等共用组件的封装,为上层提供基础服务支撑.
  • 引导层:引导层是配置和启动MyBatis 配置信息的方式

主要组件

  • SqlSession:是Mybatis对外暴露的核心API,提供了对数据库的DRUD操作接口。
  • Executor:执行器,由SqlSession调用,负责数据库操作以及Mybatis两级缓存的维护
  • StatementHandler:封装了JDBC Statement操作,负责对Statement的操作,例如PrepareStatement参数的设置以及结果集的处理。
  • ParameterHandler:是StatementHandler内部一个组件,主要负责对ParameterStatement参数的设置
  • ResultSetHandler:是StatementHandler内部一个组件,主要负责对ResultSet结果集的处理,封装成目标对象返回
  • TypeHandler:用于Java类型与JDBC类型之间的数据转换,ParameterHandler和ResultSetHandler会分别使用到它的类型转换功能
  • MappedStatement:是对Mapper配置文件或Mapper接口方法上通过注解申明SQL的封装
  • Configuration:Mybatis所有配置都统一由Configuration进行管理,内部由具体对象分别管理各自的小功能模块

二、源码剖析

1.如何解析的全局配置文件

全局配置文件可配置参数

  • Configuration对象
public class Configuration {protected Environment environment;// 允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为false。默认为falseprotected boolean safeRowBoundsEnabled;// 允许在嵌套语句中使用分页(ResultHandler)。如果允许使用则设置为falseprotected boolean safeResultHandlerEnabled = true;// 是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN// 到经典 Java 属性名 aColumn 的类似映射。默认falseprotected boolean mapUnderscoreToCamelCase;// 当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载。默认值false (true in ≤3.4.1)protected boolean aggressiveLazyLoading;// 是否允许单一语句返回多结果集(需要兼容驱动)。protected boolean multipleResultSetsEnabled = true;// 允许 JDBC 支持自动生成主键,需要驱动兼容。这就是insert时获取mysql自增主键/oracle sequence的开关。// 注:一般来说,这是希望的结果,应该默认值为true比较合适。protected boolean useGeneratedKeys;// 使用列标签代替列名,一般来说,这是希望的结果protected boolean useColumnLabel = true;// 是否启用缓存protected boolean cacheEnabled = true;// 指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,// 这对于有 Map.keySet() 依赖或 null 值初始化的时候是有用的。protected boolean callSettersOnNulls;// 允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的工程必须采用Java 8编译,// 并且加上-parameters选项。(从3.4.1开始)protected boolean useActualParamName = true;//当返回行的所有列都是空时,MyBatis默认返回null。 当开启这个设置时,MyBatis会返回一个空实例。// 请注意,它也适用于嵌套的结果集 (i.e. collectioin and association)。(从3.4.2开始)// 注:这里应该拆分为两个参数比较合适, 一个用于结果集,一个用于单记录。// 通常来说,我们会希望结果集不是null,单记录仍然是nullprotected boolean returnInstanceForEmptyRow;protected boolean shrinkWhitespacesInSql;// 指定 MyBatis 增加到日志名称的前缀。protected String logPrefix;// 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。一般建议指定为slf4j或log4jprotected Class<? extends Log> logImpl;// 指定VFS的实现, VFS是mybatis提供的用于访问AS内资源的一个简便接口protected Class<? extends VFS> vfsImpl;protected Class<?> defaultSqlProviderType;// MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。// 默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。// 若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlSession 的不同调用将不会共享数据。protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;// 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。 某些驱动需要指定列的 JDBC 类型,// 多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。protected JdbcType jdbcTypeForNull = JdbcType.OTHER;// 指定对象的哪个方法触发一次延迟加载。protected Set<String> lazyLoadTriggerMethods = new HashSet<>(Arrays.asList("equals", "clone", "hashCode", "toString"));// 设置超时时间,它决定驱动等待数据库响应的秒数。默认不超时protected Integer defaultStatementTimeout;// 为驱动的结果集设置默认获取数量。protected Integer defaultFetchSize;// SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(prepared statements);// BATCH 执行器将重用语句并执行批量更新。protected ResultSetType defaultResultSetType;// 默认执行器类型protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;// 指定 MyBatis 应如何自动映射列到字段或属性。// NONE 表示取消自动映射;// PARTIAL 只会自动映射没有定义嵌套结果集映射的结果集。// FULL 会自动映射任意复杂的结果集(无论是否嵌套)。protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;// 指定发现自动映射目标未知列(或者未知属性类型)的行为。这个值应该设置为WARNING比较合适protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior = AutoMappingUnknownColumnBehavior.NONE;// settings下的properties属性protected Properties variables = new Properties();// 默认的反射器工厂,用于操作属性、构造器方便protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory();// 对象工厂, 所有的类resultMap类都需要依赖于对象工厂来实例化protected ObjectFactory objectFactory = new DefaultObjectFactory();// 对象包装器工厂,主要用来在创建非原生对象,比如增加了某些监控或者特殊属性的代理类protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();// 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态protected boolean lazyLoadingEnabled = false;// 指定 Mybatis 创建具有延迟加载能力的对象所用到的代理工具。MyBatis 3.3+使用JAVASSISTprotected ProxyFactory proxyFactory = new JavassistProxyFactory(); // #224 Using internal Javassist instead of OGNL// MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId 属性。protected String databaseId;/*** Configuration factory class.* Used to create Configuration for loading deserialized unread properties.** @see <a href='https://github.com/mybatis/old-google-code-issues/issues/300'>Issue 300 (google code)</a>*/protected Class<?> configurationFactory;protected final MapperRegistry mapperRegistry = new MapperRegistry(this);// mybatis插件列表protected final InterceptorChain interceptorChain = new InterceptorChain();protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry(this);// 类型注册器, 用于在执行sql语句的出入参映射以及mybatis-config文件里的各种配置// 比如<transactionManager type="JDBC"/><dataSource type="POOLED">时使用简写protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection").conflictMessageProducer((savedValue, targetValue) ->". please check " + savedValue.getResource() + " and " + targetValue.getResource());protected final Map<String, Cache> caches = new StrictMap<>("Caches collection");protected final Map<String, ResultMap> resultMaps = new StrictMap<>("Result Maps collection");protected final Map<String, ParameterMap> parameterMaps = new StrictMap<>("Parameter Maps collection");protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<>("Key Generators collection");protected final Set<String> loadedResources = new HashSet<>();protected final Map<String, XNode> sqlFragments = new StrictMap<>("XML fragments parsed from previous mappers");protected final Collection<XMLStatementBuilder> incompleteStatements = new LinkedList<>();protected final Collection<CacheRefResolver> incompleteCacheRefs = new LinkedList<>();protected final Collection<ResultMapResolver> incompleteResultMaps = new LinkedList<>();protected final Collection<MethodResolver> incompleteMethods = new LinkedList<>();/** A map holds cache-ref relationship. The key is the namespace that* references a cache bound to another namespace and the value is the* namespace which the actual cache is bound to.*/protected final Map<String, String> cacheRefMap = new HashMap<>();public Configuration(Environment environment) {this();this.environment = environment;}

解析配置文件源码流程

入口:SqlSessionFactoryBuilder#build

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {try {// XMLConfigBuilder:用来解析XML配置文件// 使用构建者模式XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);// parser.parse():使用XPATH解析XML配置文件,将配置文件封装为Configuration对象// 返回DefaultSqlSessionFactory对象,该对象拥有Configuration对象(封装配置文件信息)return build(parser.parse());} catch (Exception e) {throw ExceptionFactory.wrapException("Error building SqlSession.", e);} finally {ErrorContext.instance().reset();try {inputStream.close();} catch (IOException e) {// Intentionally ignore. Prefer previous error.}}}

创建XMLConfigBuilder对象,这个类是BaseBuilder的子类,BaseBuilder类图:

看到这些子类基本上都是以Builder结尾,这里使用的是Builder建造者设计模式

2.如何解析的映射配置文件

  • Select

select 元素允许你配置很多属性来配置每条语句的行为细节

<selectid="select"parameterType="int"parameterMap="deprecated"resultType="hashmap"resultMap="personResultMap"flushCache="false"useCache="true"timeout="10"fetchSize="256"statementType="PREPARED"resultSetType="FORWARD_ONLY">
  • insert, update 和 delete

数据变更语句 insert,update 和 delete 的实现非常接近

<insertid="insert"parameterType="com.itheima.pojo.User"flushCache="true"statementType="PREPARED"keyProperty=""keyColumn=""useGeneratedKeys=""timeout="20"><updateid="update"parameterType="com.itheima.pojo.User"flushCache="true"statementType="PREPARED"timeout="20"><deleteid="delete"parameterType="com.itheima.pojo.User"flushCache="true"statementType="PREPARED"timeout="20">
  • 动态sql

借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类

  • if
  • choose (when, otherwise) MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句
<select id="findActiveBlogLike"resultType="Blog">SELECT * FROM BLOG WHERE state = ‘ACTIVE’<choose><when test="title != null">AND title like #{title}</when><when test="author != null and author.name != null">AND author_name like #{author.name}</when><otherwise>AND featured = 1</otherwise></choose>
</select>

解析映射配置文件源码流程

入口:XMLConfigBuilder#mapperElement

解析全局配置文件中的标签

/*** 解析<mappers>标签* @param parent  mappers标签对应的XNode对象* @throws Exception*/private void mapperElement(XNode parent) throws Exception {if (parent != null) {// 获取<mappers>标签的子标签for (XNode child : parent.getChildren()) {// <package>子标签if ("package".equals(child.getName())) {// 获取mapper接口和mapper映射文件对应的package包名String mapperPackage = child.getStringAttribute("name");// 将包下所有的mapper接口以及它的代理对象存储到一个Map集合中,key为mapper接口类型,value为代理对象工厂configuration.addMappers(mapperPackage);} else {// <mapper>子标签// 获取<mapper>子标签的resource属性String resource = child.getStringAttribute("resource");// 获取<mapper>子标签的url属性String url = child.getStringAttribute("url");// 获取<mapper>子标签的class属性String mapperClass = child.getStringAttribute("class");// 它们是互斥的if (resource != null && url == null && mapperClass == null) {ErrorContext.instance().resource(resource);InputStream inputStream = Resources.getResourceAsStream(resource);// 专门用来解析mapper映射文件XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());// 通过XMLMapperBuilder解析mapper映射文件mapperParser.parse();} else if (resource == null && url != null && mapperClass == null) {ErrorContext.instance().resource(url);InputStream inputStream = Resources.getUrlAsStream(url);XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());// 通过XMLMapperBuilder解析mapper映射文件mapperParser.parse();} else if (resource == null && url == null && mapperClass != null) {Class<?> mapperInterface = Resources.classForName(mapperClass);// 将指定mapper接口以及它的代理对象存储到一个Map集合中,key为mapper接口类型,value为代理对象工厂configuration.addMapper(mapperInterface);} else {throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");}}}}}

3.SqlSource创建流程

相关类及对象

  • XMLLanguageDriver
  • XMLScriptBuilder
  • SqlSource接口
  • SqlSourceBuilder
  • DynamicSqlSource:主要是封装动态SQL标签解析之后的SQL语句和带有${}的SQL语句
  • RawSqlSource:主要封装带有#{}的SQL语句
  • StaticSqlSource:是BoundSql中要存储SQL语句的一个载体,上面两个SqlSource的SQL语句,最终都会存储到该SqlSource实现类

<select id="findActiveBlogLike"resultType="Blog">SELECT * FROM BLOG WHERE state = #{ACTIVE}<choose><when test="title != null">AND title like #{title}</when><when test="author != null and author.name != null">AND author_name like #{author.name}</when><otherwise>AND featured = 1</otherwise></choose>
</select>

SqlSource创建流程

入口:XMLLanguageDriver#createSqlSource

创建SqlSource,解析SQL,封装SQL语句(未参数绑定)和入参信息

@Overridepublic SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {// 初始化了动态SQL标签处理器XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);// 解析动态SQLreturn builder.parseScriptNode();}

4.SqlSession执行主流程

相关类与接口

  • DefaultSqlSession:SqlSession接口的默认实现类
  • Executor接口

  • BaseExecutor:基础执行器,封装了子类的公共方法,包括一级缓存、延迟加载、回滚、关闭等功能;

  • SimpleExecutor:简单执行器,每执行一条 sql,都会打开一个 Statement,执行完成后关闭;

  • ReuseExecutor:重用执行器,相较于 SimpleExecutor 多了 Statement 的缓存功能,其内部维护一个 Map<String, Statement>,每次编译完成的 Statement 都会进行缓存,不会关闭;

  • BatchExecutor:批量执行器,基于 JDBC 的 addBatch、executeBatch 功能,并且在当前 sql 和上一条 sql 完全一样的时候,重用 Statement,在调用 doFlushStatements 的时候,将数据刷新到数据库;

  • CachingExecutor:缓存执行器,装饰器模式,在开启缓存的时候。会在上面三种执行器的外面包上 CachingExecutor;

  • StatementHandler接口:

  • RoutingStatementHandler:路由。Mybatis实际使用的类,拦截的StatementHandler实际就是它。它会根据Exector类型创建对应的StatementHandler,保存到属性delegate中
  • PreparedStatementHandler:预编译Statement
  • ResultSetHandler接口:处理Statement执行后产生的结果集,生成结果列表;处理存储过程执行后的输出参数
  • DefaultResultSetHandler:ResultSetHandler的 默认实现类

5.如何设置的参数

入口:PreparedStatementHandler#parameterize方法

设置PreparedStatement的参数

 @Overridepublic void parameterize(Statement statement) throws SQLException {// 通过ParameterHandler处理参数parameterHandler.setParameters((PreparedStatement) statement);}

DefaultParameterHandler#setParameters

   @Overridepublic void setParameters(PreparedStatement ps) {ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());// 获取要设置的参数映射信息List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();if (parameterMappings != null) {for (int i = 0; i < parameterMappings.size(); i++) {ParameterMapping parameterMapping = parameterMappings.get(i);// 只处理入参if (parameterMapping.getMode() != ParameterMode.OUT) {Object value;// 获取属性名称String propertyName = parameterMapping.getProperty();if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional paramsvalue = boundSql.getAdditionalParameter(propertyName);} else if (parameterObject == null) {value = null;} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {value = parameterObject;} else {MetaObject metaObject = configuration.newMetaObject(parameterObject);value = metaObject.getValue(propertyName);}// 获取每个参数的类型处理器,去设置入参和获取返回值TypeHandler typeHandler = parameterMapping.getTypeHandler();// 获取每个参数的JdbcTypeJdbcType jdbcType = parameterMapping.getJdbcType();if (value == null && jdbcType == null) {jdbcType = configuration.getJdbcTypeForNull();}try {// 给PreparedStatement设置参数typeHandler.setParameter(ps, i + 1, value, jdbcType);} catch (TypeException e) {throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);} catch (SQLException e) {throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);}}}}}

6.结果集映射流程

入口:DefaultResultSetHandler#handleResultSets

从rsw结果集参数中获取查询结果,再根据resultMap映射信息,将查询结果映射到multipleResults中

@Overridepublic List<Object> handleResultSets(Statement stmt) throws SQLException {ErrorContext.instance().activity("handling results").object(mappedStatement.getId());// <select>标签的resultMap属性,可以指定多个值,多个值之间用逗号(,)分割final List<Object> multipleResults = new ArrayList<>();int resultSetCount = 0;// 这里是获取第一个结果集,将传统JDBC的ResultSet包装成一个包含结果列元信息的ResultSetWrapper对象ResultSetWrapper rsw = getFirstResultSet(stmt);// 这里是获取所有要映射的ResultMap(按照逗号分割出来的)List<ResultMap> resultMaps = mappedStatement.getResultMaps();// 要映射的ResultMap的数量int resultMapCount = resultMaps.size();validateResultMapsCount(rsw, resultMapCount);// 循环处理每个ResultMap,从第一个开始处理while (rsw != null && resultMapCount > resultSetCount) {// 得到结果映射信息ResultMap resultMap = resultMaps.get(resultSetCount);// 处理结果集// 从rsw结果集参数中获取查询结果,再根据resultMap映射信息,将查询结果映射到multipleResults中handleResultSet(rsw, resultMap, multipleResults, null);rsw = getNextResultSet(stmt);cleanUpAfterHandlingResultSet();resultSetCount++;}// 对应<select>标签的resultSets属性,一般不使用该属性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++;}}// 如果只有一个结果集合,则直接从多结果集中取出第一个return collapseSingleResultList(multipleResults);}

7.获取Mapper代理对象流程

入口:DefaultSqlSession#getMapper

从Configuration对象中,根据Mapper接口,获取Mapper代理对象

    @Overridepublic <T> T getMapper(Class<T> type) {// 从Configuration对象中,根据Mapper接口,获取Mapper代理对象return configuration.<T>getMapper(type, this);}

Configuration#getMapper

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

MapperRegistry#getMapper

通过代理对象工厂,获取代理对象:

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {// 根据Mapper接口的类型,从Map集合中获取Mapper代理对象工厂final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);if (mapperProxyFactory == null) {throw new BindingException("Type " + type + " is not known to the MapperRegistry.");}try {// 通过MapperProxyFactory生产MapperProxy,通过MapperProxy产生Mapper代理对象return mapperProxyFactory.newInstance(sqlSession);} catch (Exception e) {throw new BindingException("Error getting mapper instance. Cause: " + e, e);}}

MapperProxyFactory#newInstance

调用JDK的动态代理方式,创建Mapper代理

//1protected T newInstance(MapperProxy<T> mapperProxy) {// 使用JDK动态代理方式,生成代理对象return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);}//2
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {// 根据Mapper接口的类型,从Map集合中获取Mapper代理对象工厂final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);if (mapperProxyFactory == null) {throw new BindingException("Type " + type + " is not known to the MapperRegistry.");}try {// 通过MapperProxyFactory生产MapperProxy,通过MapperProxy产生Mapper代理对象return mapperProxyFactory.newInstance(sqlSession);} catch (Exception e) {throw new BindingException("Error getting mapper instance. Cause: " + e, e);}}

8.invoke方法

// 通过JDK动态代理生成并获取代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 代理对象对象调用方法,底层执行invoke方法
List<User> allUser = userMapper.findAllUser();

在动态代理返回了示例后,我们就可以直接调用mapper类中的方法了,但代理对象调用方法,执行是在MapperProxy中的invoke方法,该类实现InvocationHandler接口,并重写invoke()方法。

入口:MapperProxy#invoke

 @Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {// 如果是 Object 定义的方法,直接调用if (Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, args);} else if (isDefaultMethod(method)) {return invokeDefaultMethod(proxy, method, args);}} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}// 获得 MapperMethod 对象final MapperMethod mapperMethod = cachedMapperMethod(method);// 重点在这:MapperMethod最终调用了执行的方法return mapperMethod.execute(sqlSession, args);}

MapperMethod

public Object execute(SqlSession sqlSession, Object[] args) {Object result;//判断mapper中的方法类型,最终调用的还是SqlSession中的方法switch (command.getType()) {case INSERT: {// 转换参数Object param = method.convertArgsToSqlCommandParam(args);// 执行 INSERT 操作// 转换 rowCountresult = rowCountResult(sqlSession.insert(command.getName(), param));break;}case UPDATE: {// 转换参数Object param = method.convertArgsToSqlCommandParam(args);// 转换 rowCountresult = rowCountResult(sqlSession.update(command.getName(), param));break;}case DELETE: {// 转换参数Object param = method.convertArgsToSqlCommandParam(args);// 转换 rowCountresult = rowCountResult(sqlSession.delete(command.getName(), param));break;}case SELECT:// 无返回,并且有 ResultHandler 方法参数,则将查询的结果,提交给 ResultHandler 进行处理if (method.returnsVoid() && method.hasResultHandler()) {executeWithResultHandler(sqlSession, args);result = null;// 执行查询,返回列表} else if (method.returnsMany()) {result = executeForMany(sqlSession, args);// 执行查询,返回 Map} else if (method.returnsMap()) {result = executeForMap(sqlSession, args);// 执行查询,返回 Cursor} else if (method.returnsCursor()) {result = executeForCursor(sqlSession, args);// 执行查询,返回单个对象} else {// 转换参数Object param = method.convertArgsToSqlCommandParam(args);// 查询单条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());}// 返回结果为 null ,并且返回类型为基本类型,则抛出 BindingException 异常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;}

【博学谷学习记录】超强总结,用心分享 | 架构师 Mybatis源码学习总结相关推荐

  1. [博学谷学习记录]超强总结,用心分享|架构 Nacos入门

    提示:学习笔记 欢迎指点 文章目录 前言 一.Nacos安装 二.Nacos服务注册与发现 1.服务提供者Provider 2.服务消费者Consumer 三.Nacos作为配置中心 前言 Nacos ...

  2. [博学谷学习记录]超强总结,用心分享|架构 敏捷 - 开发管理之道

    提示:学习笔记 欢迎指点 文章目录 1.敏捷开发思想之道 2.面向对象开发之道 3.敏捷学习之道 1.敏捷开发思想之道 一名敏捷开发者,敏捷思想的掌握自然首当其冲.在敏捷开发实施的过程中,我们虽然不是 ...

  3. [博学谷学习记录]超强总结,用心分享|第07节 常用的API-----笔记篇

    目录 1.API 1.1 API概述-帮助文档的使用 1.2 键盘录入字符串 2. String类 2.1 String概述 2.2 String类的构造方法 2.4 创建字符串对象的区别对比 2.5 ...

  4. {博学谷学习记录} 超强总结,用心分享|狂野架构师-前置互联网架构演变过程

    本章以系统架构,数据架构,两种维度来进行讲解 目录 1 系统架构 1,1 单体架构 1.2 中台战略 2 数据库架构 2,1 单体架构 2.2 主从读写 2.3 分库分表 3 总结 1 系统架构 1, ...

  5. [博学谷学习记录]超强总结,用心分享|第16节 集合续-----笔记篇

    目录 1.HashSet集合 1.1HashSet集合概述和特点[应用] 1.2HashSet集合的基本应用[应用] 1.3哈希值[理解] 1.4哈希表结构[理解] 1.5HashSet集合存储学生对 ...

  6. 系统架构设计师教程-学习-记录(1)系统架构师知识结构

    系统架构设计师教程-学习-记录(1)系统架构师知识结构 初 记 系统架构师知识结构 系统架构师具备的能力 初 欢迎进qq群交流:546496965 学习<系统架构设计师教程>,初心是为了学 ...

  7. MyBatis源码学习笔记(从设计模式看源码)

    文章目录 1.源码分析概述 ①.Mybatis架构分析 ②.门面模式 ③.设计模式的原则 2.日志模块分析 ①.适配器模型 ②.动态代理 ③.日志模块分析 3.数据源模块分析 ①.工厂模式 ②.数据源 ...

  8. Mybatis源码学习(三)SqlSession详解

    前言 上一章节我们学习了SqlSessionFactory的源码,SqlSessionFactory中的方法都是围绕着SqlSession来的.,那么SqlSession又是什么东东呢?这一章节我们就 ...

  9. JAVA计算机毕业设计音乐资源分享网站系统Mybatis+源码+数据库+lw文档+系统+调试部署

    JAVA计算机毕业设计音乐资源分享网站系统Mybatis+源码+数据库+lw文档+系统+调试部署 JAVA计算机毕业设计音乐资源分享网站系统Mybatis+源码+数据库+lw文档+系统+调试部署 本源 ...

最新文章

  1. 小学生python-小学生都能学会的python(函数)
  2. 【EPS精品教程】史上最牛EPS说明文档大全(23个PDF文档)
  3. Winodows10 安全登录(Administrator账户与Microsoft Account关联
  4. 转:大型网站架构系列:负载均衡详解(2)
  5. SVPullToRefresh问题解决
  6. kafka与Spring的集成
  7. Python 移动文件 文件转移 文件批量移动小工具
  8. 批量下载微软官网上的桌面壁纸图片
  9. Java是什么?Java到底能干嘛?
  10. 如何用es6 set实现交差并集
  11. 苹果手机一直显示搜索服务器,苹果手机safari浏览器搜索页面没有了
  12. python文件操作(1)
  13. SQL获取当前时间| 日期| SQL时间格式| SQL时间截取| getdate()用法
  14. 遇到的问题--docker---gitlab---k8s---error pulling image configuration: Get https://production.cloudflare.
  15. 支撑 100Gbit/s K8s 集群的未来网络数据平面
  16. Keras深度学习实战——信用预测
  17. 修改“IP属地“,我们说不
  18. git登陆用户的配置
  19. word2vec and glove优缺点
  20. 1000瓶水和1瓶毒水还有10只老鼠

热门文章

  1. android车载支持格式,安卓全面屏适配攻略(适配超长车载主机)
  2. Flink / Scala 实战- 4.BroadCast 广播流数据先到再处理 Source 数据
  3. 无法打开Win10计算机管理,Win10右键管理无法打开怎么办 Win10右键管理无法打开解决方法...
  4. fiddler捕获本地数据包、手机数据包以及其他主机数据包
  5. 企业微信三方开发(四):发送消息
  6. Jmeter识别登录验证码_使用百度AI图片识别技术
  7. 【数据分析干货】全网最全!各行业常见的业务指标整理(三)-广告行业
  8. Django url末尾斜杆 / 的重定向问题
  9. AI | 人工智能靠什么走向大众
  10. html模板引擎的作用,如何用javascript实现一个模板引擎