案例研究之聊聊 Mybatis 源码 (一)
一直以来想看下Mybatis源码,但是一直没时间看或者说自己本身没有去花时间做这件事情。随着自己的成长,觉得技术这个事情还是需要花时间去搞源码的。所以花时间来看看前辈的优秀的作品,从而也给自己的未来编程提高些能力。
有些事情现在不去做,可能以后也不会去做了,如果一直有想法,那么趁现在就去做吧!
----刘晓成@小诚信驿站
本次源码分为七个阶段(可以按照序列阅读学习,由于篇幅问题只讲第一个阶段和第二个阶段,其余可看下一篇):
第一个阶段是全局核心重点图(本节)
第二个阶段是构建加载初始化源码分析(本节)
第三个阶段是执行查询源码分析(点该链接)
第四个阶段是执行更新源码分析(点该链接)
第五个阶段是Mybatis源码中的设计模式(点该链接)
第六个阶段是Mybatis源码中的借鉴知识点和面试考察点(点该链接)
第一个阶段、全局核心重点图
首先需要明确Mybatis在应用程序中的位置
Mybatis源码包的逻辑架构图
Mybatis应用的逻辑架构图
Mybatis核心加载图
Mybatis核心执行图
Mybatis核心加载执行交互图
小结
第一节就主要是我梳理的核心图,希望能够有够大概意识和指导思想的意识,方便之后的源码学习。
第二个阶段、聊聊源码mybatis(构建加载初始化源码分析)
该节内容主要将Mybatis初始化内容,回顾下我们第一节的核心重点图。
举个case:Mybatis初始化的加载过程时序图
3.1、XML的配置解析示例
3.1.1、mybatis-config.xml的使用示例
//1、xml的协议头<?xml version="1.0" encoding="UTF-8" ?>//2、mybatis对于xml,Configuration配置文件的约束协议 <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">//3、全局配置对象<configuration> //3.1.1、数据源配置1:通过属性注入数据源 <properties resource="org/apache/ibatis/databases/blog/blog-derby.properties"/> //3.1.2、数据源配置2:通过直接配置 //3.2、全局配置对象的属性 <settings> //是否开启缓存 <setting name="cacheEnabled" value="true"/> //是否开启懒加载 <setting name="lazyLoadingEnabled" value="false"/> //是否允许返回多个结果集 <setting name="multipleResultSetsEnabled" value="true"/> //是否使用驼峰标示属性 <setting name="useColumnLabel" value="true"/> //是否使用生成主键策略 <setting name="useGeneratedKeys" value="false"/> //默认的执行器类型 <setting name="defaultExecutorType" value="SIMPLE"/> //默认的会话超时时间单位s <setting name="defaultStatementTimeout" value="25"/> </settings> //3.3、实体对象别名映射 <typeAliases> <typeAlias alias="Author" type="org.apache.ibatis.domain.blog.Author"/> </typeAliases> //3.3、指定类型处理器 <typeHandlers> <typeHandler javaType="String" jdbcType="VARCHAR" handler="org.apache.ibatis.builder.CustomStringTypeHandler"/> </typeHandlers> //3.4、对象工厂 <objectFactory type="org.apache.ibatis.builder.ExampleObjectFactory"> <property name="objectFactoryProperty" value="100"/> </objectFactory> //3.5、配置会话拦截插件 <plugins> <plugin interceptor="org.apache.ibatis.builder.ExamplePlugin"> <property name="pluginProperty" value="100"/> </plugin> </plugins> //3.6、配置环境变量: MyBatis 系统的核心设置,包括获取数据库连接实例的数据源(DataSource)以及决定事务作用域和控制方式的事务管理器(TransactionManager) <environments default="development"> //3.6.1、环境配置变量可以通过ID来区分dev-uat-pre-pro <environment id="development"> //3.6.1.1、事务管理器 <transactionManager type="JDBC"> <property name="" value=""/> </transactionManager> //3.6.1.2、数据源配置 <dataSource type="UNPOOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments> //3.7、mapper接口配置,包含了 SQL 代码和映射定义信息 <mappers> <mapper resource="org/apache/ibatis/builder/AuthorMapper.xml"/> </mappers></configuration>
3.1.2、Mapper.xml的使用示例
//1、xml的定义<?xml version="1.0" encoding="UTF-8" ?>//2、mapper协议的约束<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">//3、映射类路径的mapper,实际上是和对应的java类绑定<mapper namespace="org.apache.ibatis.domain.blog.mappers.AuthorMapper">//4、mapper接口方法:根据主键ID查询返回结果map的 <parameterMap id="selectAuthor" type="org.apache.ibatis.domain.blog.Author"> <parameter property="id" /> </parameterMap>//5、mapper接口方法:对于实体对象和数据库字段的映射情况 <resultMap id="selectAuthor" type="org.apache.ibatis.domain.blog.Author"> //字段列和对象属性的映射 <id column="id" property="id" /> <result property="username" column="username" /> <result property="favouriteSection" column="favourite_section" /> </resultMap>//6、mapper接口方法:包含嵌套的对象属性字段映射情况 <resultMap id="selectImmutableAuthor" type="org.apache.ibatis.domain.blog.ImmutableAuthor"> //参数和Java类型映射 <constructor> <idArg column="id" javaType="_int" /> <arg column="username" javaType="string" /> <arg column="favourite_section" javaType="org.apache.ibatis.domain.blog.Section" /> </constructor> </resultMap>//7、mapper接口方法:查询所有结果 <select id="selectAllAuthors" resultType="org.apache.ibatis.domain.blog.Author"> select * from author </select>//8、mapper接口方法:查询返回set集 <select id="selectAllAuthorsSet" resultType="org.apache.ibatis.domain.blog.Author"> select * from author </select>//9、mapper接口方法:查询返回list集合 <select id="selectAllAuthorsLinkedList" resultType="org.apache.ibatis.domain.blog.Author"> select * from author </select>//10、mapper接口方法:查询返回数组 <select id="selectAllAuthorsArray" resultType="org.apache.ibatis.domain.blog.Author"> select * from author </select>//11、mapper接口方法:查询只返回部分参数 <select id="selectAuthorLinkedHashMap" resultType="java.util.LinkedHashMap"> select id, username from author where id = #{value} </select>//12、mapper接口方法:查询绑定外部传入参数 <select id="selectAuthor" parameterMap="selectAuthor" resultMap="selectAuthor"> select id, username, password, email, bio, favourite_section from author where id = ? </select>//13、mapper接口方法:查询指定入参类型的参数 <select id="selectAuthorWithInlineParams" parameterType="int" resultType="org.apache.ibatis.domain.blog.Author"> select * from author where id = #{id} </select>//14、mapper接口方法:插入实体对象映射 <insert id="insertAuthor" parameterType="org.apache.ibatis.domain.blog.Author"> insert into Author (id,username,password,email,bio) values (#{id},#{username},#{password},#{email},#{bio}) </insert>//15、mapper接口方法:更新实体对象映射 <update id="updateAuthor" parameterType="org.apache.ibatis.domain.blog.Author"> update Author set username=#{username, javaType=String}, password=#{password}, email=#{email}, bio=#{bio} where id=#{id} </update>//16、mapper接口方法:删除实体根据主键ID <delete id="deleteAuthor" parameterType="int"> delete from Author where id = #{id} </delete>//17、mapper接口方法:有选择更新字段 <update id="updateAuthorIfNecessary" parameterType="org.apache.ibatis.domain.blog.Author"> update Author <set> <if test="username != null">username=#{username},</if> <if test="password != null">password=#{password},</if> <if test="email != null">email=#{email},</if> <if test="bio != null">bio=#{bio}</if> </set> where id=#{id} </update>//18、mapper接口方法:根据指定的条件进行查询 <select id="selectWithOptions" resultType="org.apache.ibatis.domain.blog.Author" fetchSize="200" timeout="10" statementType="PREPARED" resultSetType="SCROLL_SENSITIVE" flushCache="false" useCache="false"> select * from author </select></mapper>
3.1.3、代码中XML的配置解析使用示例
3.1.3.1、从 XML 中构建 SqlSessionFactory
根据Mybatis核心设计图其实可以观察到,整个Mybatis的核心在于一个SqlSession的一系列操作:实体对象和SQL的绑定,执行会话,结果集处理。而SqlSession的管理则来自于SqlSessionFactory,SqlSessionFactory是可以通过SqlSessionFactoryBuilder获得构建。如何构建出符合用户的SqlSessionFactory,则依赖于我们上面提到的Configuration 实例(Mybatis-config.xml)。
⚠️:强调下重点:每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的,SqlSessionFactory 的实例是通过SqlSessionFactoryBuilder从Configuration 实例(Mybatis-config.xml)构建出来的。
从 XML 文件中构建 SqlSessionFactory 的实例非常简单,可以使用类路径下的资源文件进行配置。也可以使用任意的输入流(InputStream)实例,比如用文件路径字符串或 file:// URL 构造的输入流。MyBatis 包含一个名叫 Resources 的工具类可以通过它来获取资源输入流(可以参考之后的源码明细篇)。
//我们上面3.1.1节提到的配置文件全路径String resource = "org/mybatis/example/mybatis-config.xml";//通过Mybatis提供的Resources工具类获取输入流InputStream inputStream = Resources.getResourceAsStream(resource);//根据Mybatis提供的SqlSessionFactoryBuilder构建器构建用户定义的Configuration特有的SqlSessionFactorySqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
3.1.3.2、构建我们真正干活的SqlSession
//SqlSessionFactory 中获取 SqlSession SqlSession sqlSession = sqlSessionFactory.openSession() //执行上面我们3.1.2节提到的配置文件mapper中的方法,因为xml配置好的映射关系所以我们直接是通过sqlSession就可以拿到映射的mapper,怎么拿到的呢?依然是需要看下第二章中我提到的Mybatis核心设计图,通过动态代理的方式从Configuration对象中找到对应的mapper,method,parameter。所以从这里看Configuration就是整个一个仓库拥有所有你需要的东西。 //从会话中获取绑定好的mapper接口信息 AuthorMapperWithAnnotation mapper = sqlSession.getMapper(AuthorMapperWithAnnotation.class); //执行mapper接口的实现方法,而这段具体分析就到第四章和第五章可以学习源码,第三章主要将全局初始化这个过程。 Author author = mapper.selectAuthorInfoById(101);
3.2、注解的配置解析示例
3.2.1、Mybatis-config.xml的使用示例
当然这里也可以使用properties文件属性等内容,或者springboot的yml文件等配置。
//1、xml的协议约束<?xml version="1.0" encoding="UTF-8" ?>//2、Configuration的协议约束<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">//3、全局配置类的约束<configuration> //3.1、全局配置的属性和事务管理器 <environments default="development"> <environment id="development"> <transactionManager type="JDBC"> <property name="" value=""/> </transactionManager> <dataSource type="UNPOOLED"> <property name="driver" value="org.hsqldb.jdbcDriver"/> <property name="url" value="jdbc:hsqldb:mem:automapping"/> <property name="username" value="sa"/> </dataSource> </environment> </environments></configuration>
3.2.2、Mapper接口的使用示例
根据我们接口常用的CRUD操作这里分别提供了注解的实现方法形式。
public interface AuthorMapperWithAnnotation { //使用select注解直接查询 @Select("select id, username, password, email, bio, favourite_section from author where id = #{id}") Author selectAuthorInfoById(int id); //使用SelectProvider注解直接查询 @SelectProvider(type =AuthorProvider.class,method = "selectAuthorWithPlaceholder") Author selectAuthorWithPlaceholder(Author author);//使用Insert注解直接查询 @Insert("insert into author (id,username,password,email,bio) values (#{id},#{username},#{password},#{email},#{bio})") int insertAuthor(Author author);//使用InsertProvider注解直接查询 @InsertProvider(type =AuthorProvider.class,method = "insertAuthorWithPlaceholder") int insertAuthorWithPlaceholder(Author author);//使用Update注解直接查询 @Update("update author set username=#{username} where id =#{id}") int updateAuthor(Author author);//使用UpdateProvider注解直接查询 @UpdateProvider(type =AuthorProvider.class,method = "updateAuthorWithPlaceholder") void updateAuthorWithPlaceholder(Author author);//使用Delete注解直接查询 @Delete("delete from author where id = #{id}") int deleteAuthor(Author author);//使用DeleteProvider注解直接查询 @DeleteProvider(type =AuthorProvider.class,method = "deleteAuthorWithPlaceholder") int deleteAuthorWithPlaceholder(Author author);//提供provider注解类的对应方法 class AuthorProvider{ //selectAuthorWithPlaceholder方法 public String selectAuthorWithPlaceholder(Author author){ return new SQL(){{ SELECT("id,username,password,email,bio").FROM("author").WHERE("id=#{id}"); }}.toString(); } //insertAuthorWithPlaceholder方法 public String insertAuthorWithPlaceholder(Author author){ return new SQL(){{ INSERT_INTO("Author").VALUES("id,username,password,email,bio","#{id},#{username},#{password},#{email},#{bio}"); }}.toString(); } //updateAuthorWithPlaceholder方法 public String updateAuthorWithPlaceholder(Author author){ return new SQL(){{ UPDATE("Author").SET("id=#{id}","username=#{username}","password=#{password}","email=#{email}","bio=#{bio}").WHERE("id=#{id}"); }}.toString(); } //deleteAuthorWithPlaceholder方法 public String deleteAuthorWithPlaceholder(Author author){ return new SQL(){{ DELETE_FROM("Author").WHERE("id=#{id}"); }}.toString(); } }}
3.3.3、代码中注解的解析使用示例
//上述提到的输入流信息我们这里先以xml为例子final String resource = "org/apache/ibatis/builder/Mybatis-config.xml";final Reader reader = Resources.getResourceAsReader(resource);//直接代码构建SqlSessionFactorySqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);//初始化全局配置---相当于Mybatis-config.xml中的绑定configuration标签 Configuration configuration = new Configuration(sqlMapper.getConfiguration().getEnvironment());//为全局配置添加需要的Mapper接口---相当于Mybatis-config.xml中的绑定Mapper.xml文件的标签 configuration.addMapper(AuthorMapperWithAnnotation.class); //创建SQL会话工厂---利用SqlSessionFactory实现Bean实例化 SqlSessionFactory sqlMapperWithAnnotation = new DefaultSqlSessionFactory(configuration); //获取SQL会话链接---所有Mybatis的核心API接口 SqlSession sqlSession = sqlMapperWithAnnotation.openSession(); //从会话中获取绑定好的mapper接口信息---利用反射获取对应的mapper AuthorMapperWithAnnotation mapper = sqlSession.getMapper(AuthorMapperWithAnnotation.class); //执行插入会话---Mybatis核心设计图中动态代理执行过程(这里的源码分析可以参考第四章、第五章) Author expected = new Author(500, "wolf:\"test\"", "******", "liuxiaocheng@somewhere.com", "Something...", null); mapper.insertAuthorWithPlaceholder(expected);
3.3、核心类和核心方法
SqlSessionFactoryBuilder:SqlSessionFactory的构造器,可以自己解析配置,也可以通过提前构建好的配置对象构建。
SqlSessionFactory:管理数据库会话、聚合Configuration的属性。
Configuration:是全局所有的运行数据配置,相当于一个大的仓库。
XMLConfigBuilder:解析XML的Configuration构建器。
XMLMapperBuilder:解析XML的Mapper构建器。
根据时序图我们可以看到实际上分为三个过程【创建】-》【解析】-》【构建】
3.3.1、SqlSessionFactoryBuilder源码分析
public class SqlSessionFactoryBuilder { //根据字符流构建会话工厂 public SqlSessionFactory build(Reader reader) { return build(reader, null, null); } ...省略本次无关内容... //根据字符流和环境和属性构建会话工厂 public SqlSessionFactory build(Reader reader, String environment, Properties properties) { try { //利用xml配置构建器 XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties); return build(parser.parse()); } catch (Exception e) { throw ExceptionFactory.wrapException("Error building SqlSession.", e); } finally { ErrorContext.instance().reset(); try { reader.close(); } catch (IOException e) { // Intentionally ignore. Prefer previous error. } } } ...省略本次无关内容... //根据全局配置构建会话工厂,走默认的会话工厂 public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config); }}
3.3.2、SqlSessionFactory源码分析
3.3.2.1、SqlSessionFactory源码分析
/* *从连接或数据源创建SqlSession */public interface SqlSessionFactory { //打开会话 SqlSession openSession(); //打开会话是否自动提交 SqlSession openSession(boolean autoCommit); //打开链接的session SqlSession openSession(Connection connection); //打开事务隔离级别的session SqlSession openSession(TransactionIsolationLevel level); //打开执行器类型的session SqlSession openSession(ExecutorType execType); //打开执行器类型,是否自动提交的session SqlSession openSession(ExecutorType execType, boolean autoCommit); //打开执行器和事务隔离级别机制的session SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level); //打开执行器,链接的session SqlSession openSession(ExecutorType execType, Connection connection); //获取全局配置 Configuration getConfiguration();}
3.3.2.2、DefaultSqlSessionFactory源码分析
/** * 默认的会话工厂 * @author Clinton Begin */public class DefaultSqlSessionFactory implements SqlSessionFactory { //全局配置 private final Configuration configuration; //构造函数 public DefaultSqlSessionFactory(Configuration configuration) { this.configuration = configuration; } ...省略本次无关内容...}
3.3.3、Configuration源码分析
/** * 抽象构建器 * @author Clinton Begin */public abstract class BaseBuilder { //全局配置 protected final Configuration configuration; //类型别名注册器 protected final TypeAliasRegistry typeAliasRegistry; //类型处理器注册器 protected final TypeHandlerRegistry typeHandlerRegistry; //构造函数 public BaseBuilder(Configuration configuration) { this.configuration = configuration; this.typeAliasRegistry = this.configuration.getTypeAliasRegistry(); this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry(); } //获取全局配置 public Configuration getConfiguration() { return configuration; } //解析正则表达式 protected Pattern parseExpression(String regex, String defaultValue) { return Pattern.compile(regex == null ? defaultValue : regex); } //获取boolean值 protected Boolean booleanValueOf(String value, Boolean defaultValue) { return value == null ? defaultValue : Boolean.valueOf(value); } //获取int值 protected Integer integerValueOf(String value, Integer defaultValue) { return value == null ? defaultValue : Integer.valueOf(value); } //获取set值 protected Set<String> stringSetValueOf(String value, String defaultValue) { value = value == null ? defaultValue : value; return new HashSet<>(Arrays.asList(value.split(","))); } //解析jdbc类型 protected JdbcType resolveJdbcType(String alias) { if (alias == null) { return null; } try { return JdbcType.valueOf(alias); } catch (IllegalArgumentException e) { throw new BuilderException("Error resolving JdbcType. Cause: " + e, e); } } //解析结果集类型 protected ResultSetType resolveResultSetType(String alias) { if (alias == null) { return null; } try { return ResultSetType.valueOf(alias); } catch (IllegalArgumentException e) { throw new BuilderException("Error resolving ResultSetType. Cause: " + e, e); } } //解析参数风格 protected ParameterMode resolveParameterMode(String alias) { if (alias == null) { return null; } try { return ParameterMode.valueOf(alias); } catch (IllegalArgumentException e) { throw new BuilderException("Error resolving ParameterMode. Cause: " + e, e); } } //创建实例 protected Object createInstance(String alias) { Class<?> clazz = resolveClass(alias); if (clazz == null) { return null; } try { return clazz.getDeclaredConstructor().newInstance(); } catch (Exception e) { throw new BuilderException("Error creating instance. Cause: " + e, e); } } //根据别名解析类 protected <T> Class<? extends T> resolveClass(String alias) { if (alias == null) { return null; } try { return resolveAlias(alias); } catch (Exception e) { throw new BuilderException("Error resolving class. Cause: " + e, e); } } //解析类型处理器 protected TypeHandler<?> resolveTypeHandler(Class<?> javaType, String typeHandlerAlias) { if (typeHandlerAlias == null) { return null; } Class<?> type = resolveClass(typeHandlerAlias); if (type != null && !TypeHandler.class.isAssignableFrom(type)) { throw new BuilderException("Type " + type.getName() + " is not a valid TypeHandler because it does not implement TypeHandler interface"); } @SuppressWarnings("unchecked") // already verified it is a TypeHandler Class<? extends TypeHandler<?>> typeHandlerType = (Class<? extends TypeHandler<?>>) type; return resolveTypeHandler(javaType, typeHandlerType); } //解析类型处理器 protected TypeHandler<?> resolveTypeHandler(Class<?> javaType, Class<? extends TypeHandler<?>> typeHandlerType) { if (typeHandlerType == null) { return null; } // javaType ignored for injected handlers see issue #746 for full detail TypeHandler<?> handler = typeHandlerRegistry.getMappingTypeHandler(typeHandlerType); if (handler == null) { // not in registry, create a new one handler = typeHandlerRegistry.getInstance(javaType, typeHandlerType); } return handler; } //解析别名 protected <T> Class<? extends T> resolveAlias(String alias) { return typeAliasRegistry.resolveAlias(alias); }}
3.3.4.1、XMLConfigBuilder源码分析
/** * xml配置构建器 * @author Clinton Begin * @author Kazuki Shimizu */public class XMLConfigBuilder extends BaseBuilder { //是否解析 private boolean parsed; //解析器 private final XPathParser parser; //环境 private String environment; //本地反射工厂 private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory(); //构造函数 public XMLConfigBuilder(Reader reader) { this(reader, null, null); } //构造函数 public XMLConfigBuilder(Reader reader, String environment) { this(reader, environment, null); } //构造函数 public XMLConfigBuilder(Reader reader, String environment, Properties props) { this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props); } //构造函数 public XMLConfigBuilder(InputStream inputStream) { this(inputStream, null, null); } //构造函数 public XMLConfigBuilder(InputStream inputStream, String environment) { this(inputStream, environment, null); } //构造函数 public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) { this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props); } //私有构造函数 private XMLConfigBuilder(XPathParser parser, String environment, Properties props) { super(new Configuration()); ErrorContext.instance().resource("SQL Mapper Configuration"); this.configuration.setVariables(props); this.parsed = false; this.environment = environment; this.parser = parser; } //解析全局配置对象 public Configuration parse() { if (parsed) { throw new BuilderException("Each XMLConfigBuilder can only be used once."); } parsed = true; parseConfiguration(parser.evalNode("/configuration")); return configuration; } //解析全局配置对象 private void parseConfiguration(XNode root) { try { //issue #117 read properties first //解析属性变量节点 propertiesElement(root.evalNode("properties")); //解析setting节点字段 Properties settings = settingsAsProperties(root.evalNode("settings")); //加载用户自定义的VFS解析文件类 loadCustomVfs(settings); //加载用户自定义的日志实现 loadCustomLogImpl(settings); //类型别名元素绑定 typeAliasesElement(root.evalNode("typeAliases")); //插件元素 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")); //数据库ID databaseIdProviderElement(root.evalNode("databaseIdProvider")); //类型处理器元素 typeHandlerElement(root.evalNode("typeHandlers")); //mapper接口元素 mapperElement(root.evalNode("mappers")); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); } } //设置属性 private Properties settingsAsProperties(XNode context) { if (context == null) { return new Properties(); } Properties props = context.getChildrenAsProperties(); //检查所有的setting设置是在配置类中已知的 // Check that all settings are known to the configuration class MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory); for (Object key : props.keySet()) { if (!metaConfig.hasSetter(String.valueOf(key))) { throw new BuilderException("The setting " + key + " is not known. Make sure you spelled it correctly (case sensitive)."); } } return props; } //加载定制vfs解析实现类 private void loadCustomVfs(Properties props) throws ClassNotFoundException { String value = props.getProperty("vfsImpl"); if (value != null) { String[] clazzes = value.split(","); for (String clazz : clazzes) { if (!clazz.isEmpty()) { @SuppressWarnings("unchecked") Class<? extends VFS> vfsImpl = (Class<? extends VFS>)Resources.classForName(clazz); configuration.setVfsImpl(vfsImpl); } } } } //加载定制实现类 private void loadCustomLogImpl(Properties props) { Class<? extends Log> logImpl = resolveClass(props.getProperty("logImpl")); configuration.setLogImpl(logImpl); } //类型别名元素 private void typeAliasesElement(XNode parent) { if (parent != null) { for (XNode child : parent.getChildren()) { if ("package".equals(child.getName())) { String typeAliasPackage = child.getStringAttribute("name"); configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage); } else { String alias = child.getStringAttribute("alias"); String type = child.getStringAttribute("type"); try { Class<?> clazz = Resources.classForName(type); if (alias == null) { typeAliasRegistry.registerAlias(clazz); } else { typeAliasRegistry.registerAlias(alias, clazz); } } catch (ClassNotFoundException e) { throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e); } } } } } //插件元素 private void pluginElement(XNode parent) throws Exception { if (parent != null) { for (XNode child : parent.getChildren()) { String interceptor = child.getStringAttribute("interceptor"); Properties properties = child.getChildrenAsProperties(); Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor().newInstance(); interceptorInstance.setProperties(properties); configuration.addInterceptor(interceptorInstance); } } } //对象工厂元素 private void objectFactoryElement(XNode context) throws Exception { if (context != null) { String type = context.getStringAttribute("type"); Properties properties = context.getChildrenAsProperties(); ObjectFactory factory = (ObjectFactory) resolveClass(type).getDeclaredConstructor().newInstance(); factory.setProperties(properties); configuration.setObjectFactory(factory); } } //对象包装工厂元素 private void objectWrapperFactoryElement(XNode context) throws Exception { if (context != null) { String type = context.getStringAttribute("type"); ObjectWrapperFactory factory = (ObjectWrapperFactory) resolveClass(type).getDeclaredConstructor().newInstance(); configuration.setObjectWrapperFactory(factory); } } //反射工厂元素 private void reflectorFactoryElement(XNode context) throws Exception { if (context != null) { String type = context.getStringAttribute("type"); ReflectorFactory factory = (ReflectorFactory) resolveClass(type).getDeclaredConstructor().newInstance(); configuration.setReflectorFactory(factory); } }//属性元素 private void propertiesElement(XNode context) throws Exception { if (context != null) { Properties defaults = context.getChildrenAsProperties(); String resource = context.getStringAttribute("resource"); String url = context.getStringAttribute("url"); if (resource != null && url != null) { throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other."); } if (resource != null) { defaults.putAll(Resources.getResourceAsProperties(resource)); } else if (url != null) { defaults.putAll(Resources.getUrlAsProperties(url)); } Properties vars = configuration.getVariables(); if (vars != null) { defaults.putAll(vars); } parser.setVariables(defaults); configuration.setVariables(defaults); } } //设置属性 private void settingsElement(Properties props) { configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL"))); configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE"))); configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true)); configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory"))); configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false)); configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), false)); configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true)); configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true)); configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false)); configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE"))); configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null)); configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null)); configuration.setDefaultResultSetType(resolveResultSetType(props.getProperty("defaultResultSetType"))); configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false)); configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false)); configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION"))); configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER"))); configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString")); configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true)); configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage"))); configuration.setDefaultEnumTypeHandler(resolveClass(props.getProperty("defaultEnumTypeHandler"))); configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false)); configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true)); configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false)); configuration.setLogPrefix(props.getProperty("logPrefix")); configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory"))); } //环境元素 private void environmentsElement(XNode context) throws Exception { if (context != null) { if (environment == null) { environment = context.getStringAttribute("default"); } for (XNode child : context.getChildren()) { String id = child.getStringAttribute("id"); if (isSpecifiedEnvironment(id)) { TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager")); DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource")); DataSource dataSource = dsFactory.getDataSource(); Environment.Builder environmentBuilder = new Environment.Builder(id) .transactionFactory(txFactory) .dataSource(dataSource); configuration.setEnvironment(environmentBuilder.build()); } } } } //数据库ID服务元素 private void databaseIdProviderElement(XNode context) throws Exception { DatabaseIdProvider databaseIdProvider = null; if (context != null) { String type = context.getStringAttribute("type"); // awful patch to keep backward compatibility if ("VENDOR".equals(type)) { type = "DB_VENDOR"; } Properties properties = context.getChildrenAsProperties(); databaseIdProvider = (DatabaseIdProvider) resolveClass(type).getDeclaredConstructor().newInstance(); databaseIdProvider.setProperties(properties); } Environment environment = configuration.getEnvironment(); if (environment != null && databaseIdProvider != null) { String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource()); configuration.setDatabaseId(databaseId); } } //事务处理的元素 private TransactionFactory transactionManagerElement(XNode context) throws Exception { if (context != null) { String type = context.getStringAttribute("type"); Properties props = context.getChildrenAsProperties(); TransactionFactory factory = (TransactionFactory) resolveClass(type).getDeclaredConstructor().newInstance(); factory.setProperties(props); return factory; } throw new BuilderException("Environment declaration requires a TransactionFactory."); } //数据源处理的元素 private DataSourceFactory dataSourceElement(XNode context) throws Exception { if (context != null) { String type = context.getStringAttribute("type"); Properties props = context.getChildrenAsProperties(); DataSourceFactory factory = (DataSourceFactory) resolveClass(type).getDeclaredConstructor().newInstance(); factory.setProperties(props); return factory; } throw new BuilderException("Environment declaration requires a DataSourceFactory."); } //类型处理器的元素 private void typeHandlerElement(XNode parent) { if (parent != null) { for (XNode child : parent.getChildren()) { if ("package".equals(child.getName())) { String typeHandlerPackage = child.getStringAttribute("name"); typeHandlerRegistry.register(typeHandlerPackage); } else { String javaTypeName = child.getStringAttribute("javaType"); String jdbcTypeName = child.getStringAttribute("jdbcType"); String handlerTypeName = child.getStringAttribute("handler"); Class<?> javaTypeClass = resolveClass(javaTypeName); JdbcType jdbcType = resolveJdbcType(jdbcTypeName); Class<?> typeHandlerClass = resolveClass(handlerTypeName); if (javaTypeClass != null) { if (jdbcType == null) { typeHandlerRegistry.register(javaTypeClass, typeHandlerClass); } else { typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass); } } else { typeHandlerRegistry.register(typeHandlerClass); } } } } } //mapper文件解析元素 private void mapperElement(XNode parent) throws Exception { if (parent != null) { for (XNode child : parent.getChildren()) { if ("package".equals(child.getName())) { String mapperPackage = child.getStringAttribute("name"); configuration.addMappers(mapperPackage); } else { String resource = child.getStringAttribute("resource"); String url = child.getStringAttribute("url"); String mapperClass = child.getStringAttribute("class"); if (resource != null && url == null && mapperClass == null) { ErrorContext.instance().resource(resource); InputStream inputStream = Resources.getResourceAsStream(resource); XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments()); 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()); mapperParser.parse(); } else if (resource == null && url == null && mapperClass != null) { Class<?> mapperInterface = Resources.classForName(mapperClass); configuration.addMapper(mapperInterface); } else { throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one."); } } } } } //是否指定环境 private boolean isSpecifiedEnvironment(String id) { if (environment == null) { throw new BuilderException("No environment specified."); } else if (id == null) { throw new BuilderException("Environment requires an id attribute."); } else if (environment.equals(id)) { return true; } return false; }}
3.3.4.2、XMLMapperBuilder源码分析
/** * XML的mapper接口构建器 */public class XMLMapperBuilder extends BaseBuilder { //解析器 private final XPathParser parser; //接口助手 private final MapperBuilderAssistant builderAssistant; //sql节点片段 private final Map<String, XNode> sqlFragments; //资源 private final String resource; //不建议使用,未来废弃 @Deprecated public XMLMapperBuilder(Reader reader, Configuration configuration, String resource, Map<String, XNode> sqlFragments, String namespace) { this(reader, configuration, resource, sqlFragments); this.builderAssistant.setCurrentNamespace(namespace); } //不建议使用,未来废弃 @Deprecated public XMLMapperBuilder(Reader reader, Configuration configuration, String resource, Map<String, XNode> sqlFragments) { this(new XPathParser(reader, true, configuration.getVariables(), new XMLMapperEntityResolver()), configuration, resource, sqlFragments); } //构造函数 public XMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource, Map<String, XNode> sqlFragments, String namespace) { this(inputStream, configuration, resource, sqlFragments); this.builderAssistant.setCurrentNamespace(namespace); } //构造函数 public XMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource, Map<String, XNode> sqlFragments) { this(new XPathParser(inputStream, true, configuration.getVariables(), new XMLMapperEntityResolver()), configuration, resource, sqlFragments); } //构造函数 private XMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, Map<String, XNode> sqlFragments) { super(configuration); this.builderAssistant = new MapperBuilderAssistant(configuration, resource); this.parser = parser; this.sqlFragments = sqlFragments; this.resource = resource; } //解析mapper public void parse() { if (!configuration.isResourceLoaded(resource)) { configurationElement(parser.evalNode("/mapper")); configuration.addLoadedResource(resource); //将mapper和命名空间绑定 bindMapperForNamespace(); } //解析等待的结果映射 parsePendingResultMaps(); //解析等待的缓存引用 parsePendingCacheRefs(); //解析等待的会话 parsePendingStatements(); } //获取sql片段根据引用ID public XNode getSqlFragment(String refid) { return sqlFragments.get(refid); } //配置全局配置 private void configurationElement(XNode context) { try { String namespace = context.getStringAttribute("namespace"); if (namespace == null || namespace.equals("")) { throw new BuilderException("Mapper's namespace cannot be empty"); } //绑定当前的命名空间 builderAssistant.setCurrentNamespace(namespace); //缓存引用 cacheRefElement(context.evalNode("cache-ref")); //缓存 cacheElement(context.evalNode("cache")); //参数map parameterMapElement(context.evalNodes("/mapper/parameterMap")); //结果map resultMapElements(context.evalNodes("/mapper/resultMap")); //sql sqlElement(context.evalNodes("/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); } } //构建会话从上下文 private void buildStatementFromContext(List<XNode> list) { if (configuration.getDatabaseId() != null) { buildStatementFromContext(list, configuration.getDatabaseId()); } buildStatementFromContext(list, null); } //构建会话从上下文 private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) { for (XNode context : list) { final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId); try { statementParser.parseStatementNode(); } catch (IncompleteElementException e) { configuration.addIncompleteStatement(statementParser); } } } //解析等待的结果映射 private void parsePendingResultMaps() { Collection<ResultMapResolver> incompleteResultMaps = configuration.getIncompleteResultMaps(); synchronized (incompleteResultMaps) { Iterator<ResultMapResolver> iter = incompleteResultMaps.iterator(); while (iter.hasNext()) { try { iter.next().resolve(); iter.remove(); } catch (IncompleteElementException e) { // ResultMap is still missing a resource... } } } } //解析等待的缓存引用 private void parsePendingCacheRefs() { Collection<CacheRefResolver> incompleteCacheRefs = configuration.getIncompleteCacheRefs(); synchronized (incompleteCacheRefs) { Iterator<CacheRefResolver> iter = incompleteCacheRefs.iterator(); while (iter.hasNext()) { try { iter.next().resolveCacheRef(); iter.remove(); } catch (IncompleteElementException e) { // Cache ref is still missing a resource... } } } } //解析等待的会话 private void parsePendingStatements() { Collection<XMLStatementBuilder> incompleteStatements = configuration.getIncompleteStatements(); synchronized (incompleteStatements) { Iterator<XMLStatementBuilder> iter = incompleteStatements.iterator(); while (iter.hasNext()) { try { iter.next().parseStatementNode(); iter.remove(); } catch (IncompleteElementException e) { // Statement is still missing a resource... } } } } //缓存引用元素 private void cacheRefElement(XNode context) { if (context != null) { configuration.addCacheRef(builderAssistant.getCurrentNamespace(), context.getStringAttribute("namespace")); CacheRefResolver cacheRefResolver = new CacheRefResolver(builderAssistant, context.getStringAttribute("namespace")); try { cacheRefResolver.resolveCacheRef(); } catch (IncompleteElementException e) { configuration.addIncompleteCacheRef(cacheRefResolver); } } } //缓存元素 private void cacheElement(XNode context) { if (context != null) { String type = context.getStringAttribute("type", "PERPETUAL"); Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type); String eviction = context.getStringAttribute("eviction", "LRU"); Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction); Long flushInterval = context.getLongAttribute("flushInterval"); Integer size = context.getIntAttribute("size"); boolean readWrite = !context.getBooleanAttribute("readOnly", false); boolean blocking = context.getBooleanAttribute("blocking", false); Properties props = context.getChildrenAsProperties(); builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props); } } //参数映射元素 private void parameterMapElement(List<XNode> list) { for (XNode parameterMapNode : list) { String id = parameterMapNode.getStringAttribute("id"); String type = parameterMapNode.getStringAttribute("type"); Class<?> parameterClass = resolveClass(type); List<XNode> parameterNodes = parameterMapNode.evalNodes("parameter"); List<ParameterMapping> parameterMappings = new ArrayList<>(); for (XNode parameterNode : parameterNodes) { String property = parameterNode.getStringAttribute("property"); String javaType = parameterNode.getStringAttribute("javaType"); String jdbcType = parameterNode.getStringAttribute("jdbcType"); String resultMap = parameterNode.getStringAttribute("resultMap"); String mode = parameterNode.getStringAttribute("mode"); String typeHandler = parameterNode.getStringAttribute("typeHandler"); Integer numericScale = parameterNode.getIntAttribute("numericScale"); ParameterMode modeEnum = resolveParameterMode(mode); Class<?> javaTypeClass = resolveClass(javaType); JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType); Class<? extends TypeHandler<?>> typeHandlerClass = resolveClass(typeHandler); ParameterMapping parameterMapping = builderAssistant.buildParameterMapping(parameterClass, property, javaTypeClass, jdbcTypeEnum, resultMap, modeEnum, typeHandlerClass, numericScale); parameterMappings.add(parameterMapping); } builderAssistant.addParameterMap(id, parameterClass, parameterMappings); } } //结果映射 private void resultMapElements(List<XNode> list) throws Exception { for (XNode resultMapNode : list) { try { resultMapElement(resultMapNode); } catch (IncompleteElementException e) { // ignore, it will be retried } } } //结果映射 private ResultMap resultMapElement(XNode resultMapNode) throws Exception { return resultMapElement(resultMapNode, Collections.emptyList(), null); } //结果映射 private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings, Class<?> enclosingType) throws Exception { ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier()); String type = resultMapNode.getStringAttribute("type", resultMapNode.getStringAttribute("ofType", resultMapNode.getStringAttribute("resultType", resultMapNode.getStringAttribute("javaType")))); Class<?> typeClass = resolveClass(type); if (typeClass == null) { typeClass = inheritEnclosingType(resultMapNode, enclosingType); } Discriminator discriminator = null; List<ResultMapping> resultMappings = new ArrayList<>(); resultMappings.addAll(additionalResultMappings); List<XNode> resultChildren = resultMapNode.getChildren(); for (XNode resultChild : resultChildren) { if ("constructor".equals(resultChild.getName())) { processConstructorElement(resultChild, typeClass, resultMappings); } else if ("discriminator".equals(resultChild.getName())) { discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings); } else { List<ResultFlag> flags = new ArrayList<>(); if ("id".equals(resultChild.getName())) { flags.add(ResultFlag.ID); } resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags)); } } String id = resultMapNode.getStringAttribute("id", resultMapNode.getValueBasedIdentifier()); String extend = resultMapNode.getStringAttribute("extends"); Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping"); ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping); try { return resultMapResolver.resolve(); } catch (IncompleteElementException e) { configuration.addIncompleteResultMap(resultMapResolver); throw e; } } //继承封装类型 protected Class<?> inheritEnclosingType(XNode resultMapNode, Class<?> enclosingType) { if ("association".equals(resultMapNode.getName()) && resultMapNode.getStringAttribute("resultMap") == null) { String property = resultMapNode.getStringAttribute("property"); if (property != null && enclosingType != null) { MetaClass metaResultType = MetaClass.forClass(enclosingType, configuration.getReflectorFactory()); return metaResultType.getSetterType(property); } } else if ("case".equals(resultMapNode.getName()) && resultMapNode.getStringAttribute("resultMap") == null) { return enclosingType; } return null; } //执行构造元素 private void processConstructorElement(XNode resultChild, Class<?> resultType, List<ResultMapping> resultMappings) throws Exception { List<XNode> argChildren = resultChild.getChildren(); for (XNode argChild : argChildren) { List<ResultFlag> flags = new ArrayList<>(); flags.add(ResultFlag.CONSTRUCTOR); if ("idArg".equals(argChild.getName())) { flags.add(ResultFlag.ID); } resultMappings.add(buildResultMappingFromContext(argChild, resultType, flags)); } } //执行鉴别器元素 private Discriminator processDiscriminatorElement(XNode context, Class<?> resultType, List<ResultMapping> resultMappings) throws Exception { String column = context.getStringAttribute("column"); String javaType = context.getStringAttribute("javaType"); String jdbcType = context.getStringAttribute("jdbcType"); String typeHandler = context.getStringAttribute("typeHandler"); Class<?> javaTypeClass = resolveClass(javaType); Class<? extends TypeHandler<?>> typeHandlerClass = resolveClass(typeHandler); JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType); Map<String, String> discriminatorMap = new HashMap<>(); for (XNode caseChild : context.getChildren()) { String value = caseChild.getStringAttribute("value"); String resultMap = caseChild.getStringAttribute("resultMap", processNestedResultMappings(caseChild, resultMappings, resultType)); discriminatorMap.put(value, resultMap); } return builderAssistant.buildDiscriminator(resultType, column, javaTypeClass, jdbcTypeEnum, typeHandlerClass, discriminatorMap); } //sql元素 private void sqlElement(List<XNode> list) { if (configuration.getDatabaseId() != null) { sqlElement(list, configuration.getDatabaseId()); } sqlElement(list, null); } //sql元素 private void sqlElement(List<XNode> list, String requiredDatabaseId) { for (XNode context : list) { String databaseId = context.getStringAttribute("databaseId"); String id = context.getStringAttribute("id"); id = builderAssistant.applyCurrentNamespace(id, false); if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId)) { sqlFragments.put(id, context); } } } //数据库ID匹配当前要求 private boolean databaseIdMatchesCurrent(String id, String databaseId, String requiredDatabaseId) { if (requiredDatabaseId != null) { return requiredDatabaseId.equals(databaseId); } if (databaseId != null) { return false; } if (!this.sqlFragments.containsKey(id)) { return true; } // skip this fragment if there is a previous one with a not null databaseId XNode context = this.sqlFragments.get(id); return context.getStringAttribute("databaseId") == null; } //从上下文构建结果映射 private ResultMapping buildResultMappingFromContext(XNode context, Class<?> resultType, List<ResultFlag> flags) throws Exception { String property; if (flags.contains(ResultFlag.CONSTRUCTOR)) { property = context.getStringAttribute("name"); } else { property = context.getStringAttribute("property"); } String column = context.getStringAttribute("column"); String javaType = context.getStringAttribute("javaType"); String jdbcType = context.getStringAttribute("jdbcType"); String nestedSelect = context.getStringAttribute("select"); String nestedResultMap = context.getStringAttribute("resultMap", processNestedResultMappings(context, Collections.emptyList(), resultType)); String notNullColumn = context.getStringAttribute("notNullColumn"); String columnPrefix = context.getStringAttribute("columnPrefix"); String typeHandler = context.getStringAttribute("typeHandler"); String resultSet = context.getStringAttribute("resultSet"); String foreignColumn = context.getStringAttribute("foreignColumn"); boolean lazy = "lazy".equals(context.getStringAttribute("fetchType", configuration.isLazyLoadingEnabled() ? "lazy" : "eager")); Class<?> javaTypeClass = resolveClass(javaType); Class<? extends TypeHandler<?>> typeHandlerClass = resolveClass(typeHandler); JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType); return builderAssistant.buildResultMapping(resultType, property, column, javaTypeClass, jdbcTypeEnum, nestedSelect, nestedResultMap, notNullColumn, columnPrefix, typeHandlerClass, flags, resultSet, foreignColumn, lazy); } //执行嵌套结果映射 private String processNestedResultMappings(XNode context, List<ResultMapping> resultMappings, Class<?> enclosingType) throws Exception { if ("association".equals(context.getName()) || "collection".equals(context.getName()) || "case".equals(context.getName())) { if (context.getStringAttribute("select") == null) { validateCollection(context, enclosingType); ResultMap resultMap = resultMapElement(context, resultMappings, enclosingType); return resultMap.getId(); } } return null; } //校验集合 protected void validateCollection(XNode context, Class<?> enclosingType) { if ("collection".equals(context.getName()) && context.getStringAttribute("resultMap") == null && context.getStringAttribute("javaType") == null) { MetaClass metaResultType = MetaClass.forClass(enclosingType, configuration.getReflectorFactory()); String property = context.getStringAttribute("property"); if (!metaResultType.hasSetter(property)) { throw new BuilderException( "Ambiguous collection type for property '" + property + "'. You must specify 'javaType' or 'resultMap'."); } } } //构建命名空间的mapper private void bindMapperForNamespace() { String namespace = builderAssistant.getCurrentNamespace(); if (namespace != null) { Class<?> boundType = null; try { boundType = Resources.classForName(namespace); } catch (ClassNotFoundException e) { //ignore, bound type is not required } if (boundType != null) { if (!configuration.hasMapper(boundType)) { configuration.addLoadedResource("namespace:" + namespace); configuration.addMapper(boundType); } } } }}
小结
Mybatis构建加载初始化源码分析这节到这里就结束了,可以再回顾下核心重点图。
版权声明: 本文为 InfoQ 作者【小诚信驿站】的原创文章。
原文链接:【http://xie.infoq.cn/article/6ebeb6934a9b623823f5ac91a】。文章转载请联系作者。
小诚信驿站
小胜靠智,大胜靠德 2019.06.27 加入
历经创业、京东、腾讯、滴滴公司,希望能够有些东西一起分享。公众号:小诚信驿站,微信/CSDN搜索:wolf_love666
评论