【Mybatis 】- 配置初始化过程
时间: 2019-04-02来源:OSCHINA
前景提要
Mybatis 配置初始化过程
测试代码
SqlMapConfig.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <properties resource="db.properties"/> <typeAliases> <package name="com.zhiwei.entity"/> </typeAliases> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.user}"/> <property name="password" value="${jdbc.password}"/> </dataSource> </environment> </environments> <mappers> <mapper resource="advanced/StudentMapper.xml"/> </mappers> </configuration>
StudentMapper.xml <?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.zhiwei.advanced.mapper.StudentMapper"> <resultMap type="com.zhiwei.entity.Student" id="studentResultMap"> <id column="sid" property="sid"/> <result column="stuname" property="stuName"/> <result column="stupasswd" property="stuPasswd"/> <result column="stuage" property="stuAge"/> <result column="stugender" property="stuGender" javaType="java.lang.String"/> <result column="stuscore" property="stuScore" javaType="java.lang.Integer"/> </resultMap> <resultMap type="com.zhiwei.advanced.pojo.StudentCustomer" id="studentCustomerResultMap"> <id column="sid" property="sid"/> <result column="sid" property="uid"/> <result column="stupasswd" property="passwd"/> <result column="stuname" property="stuName"/> <result column="stupasswd" property="stuPasswd"/> <result column="stuage" property="stuAge"/> <result column="stugender" property="stuGender" javaType="java.lang.String"/> <result column="stuscore" property="stuScore" javaType="java.lang.Integer"/> </resultMap> <select id="findStudentById" parameterType="int" resultMap="studentResultMap"> select * from student where sid = #{sid} </select> </mapper>
Student package com.zhiwei.entity; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.ToString; @Data @NoArgsConstructor @AllArgsConstructor @ToString public class Student { private int sid; private String stuName; private String stuPasswd; private int stuAge; private String stuGender; private float stuScore; }
StudentMapper package com.zhiwei.advanced.mapper; import com.zhiwei.advanced.pojo.StudentCustomer; import com.zhiwei.advanced.pojo.StudentQueryVo; import com.zhiwei.entity.Student; import java.util.List; public interface StudentMapper { public Student findStudentById(Integer sid); }
启动类 sqlSession.getMapper(StudentMapper.class).findStudentById(id);
工作流程
解析XML映射为org.apache.ibatis.session.Configuration对象,并以此为基础创建SessionFactory, SessionFactory创建的Session与Executor绑定,交由Executor执行
配置加载过程
SessionFactory构建代码 Inputstream is = Resources.getResourceAsStream("SqlMapConfig.xml"); sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
SessionFactory构建
org.apache.ibatis.session.SqlSessionFactoryBuilder#build(java.io.InputStream, java.lang.String, java.util.Properties) XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); return build(parser.parse());
XMLConfigBuilder 工具类:主要解析SqlMapConfig.xml 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; }
new Configuration(): 默认填充配置:类似缓存别名映射类 public Configuration() { typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class); typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class); typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class); typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class); 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); }
XML解析:parser.parse()
parsed解析标志,底层调用 parseConfiguration(parser.evalNode("/configuration")); 按照Xpath语法解析SqlMapConfig.xml根节点 private void parseConfiguration(XNode root) { try { // 解析SqlMapConfig.xml的properties标签, configuration.setVariables(defaults)解析properties文件属性到configuration缓存 propertiesElement(root.evalNode("properties")); // setting配置: configuration.setVfsImpl(vfsImpl) Properties settings = settingsAsProperties(root.evalNode("settings")); loadCustomVfs(settings); //别名配置:configuration.typeAliasRegistry //dtd约束:mybatis-3-config.dtd //package: key:类简单名驼峰命名法,class:类全限定名 //typeAlias: key: alias自定义别名 type:类全限定名 //注意:最好不用直接引用package形式,否则多个包存在相同名的类会出现冲突 typeAliasesElement(root.evalNode("typeAliases")); //插件配置:相当于拦截器可自定义操作,例如分页、CRUD操作拦截,类似AOP:configuration.interceptorChain pluginElement(root.evalNode("plugins")); //设置对象工厂:哦、configuration.objectFactory objectFactoryElement(root.evalNode("objectFactory")); //社会组obectMapper工厂:configuration.objectWrapperFactory objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); //设置反射器工厂:configuration.reflectorFactory,反射机制操作 reflectorFactoryElement(root.evalNode("reflectorFactory")); //mybatis全局配置:若无自定义则使用默认配置 settingsElement(settings); //设置环境:configuration.environment,本质缓存当前工作数据库信息 //注意:Environment可设置多套数据库环境,通过default属性指定具体数据库 environmentsElement(root.evalNode("environments")); //明确指定mybatis工作的数据库ID:数据库产品名称 databaseIdProviderElement(root.evalNode("databaseIdProvider")); //类型处理器:解析具体的Mapper数据类型: configuration.typeHandlerRegistry //作用:数据库操作Mapper的解析和转换工作 typeHandlerElement(root.evalNode("typeHandlers")); //mapper处理:configuration.mapperRegistry mapperElement(root.evalNode("mappers")); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); } }
Mapper标签解析 private void mapperElement(XNode parent) throws Exception { if (parent != null) { for (XNode child : parent.getChildren()) { //package形式批量配置:常用 if ("package".equals(child.getName())) { String mapperPackage = child.getStringAttribute("name"); configuration.addMappers(mapperPackage); //mapper单个独立配置 } 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."); } } } } }
mapperParser.parse(); 解析 //解析流程 public void parse() { //判断StudentMapper.xml是否已加载 if (!configuration.isResourceLoaded(resource)) { configurationElement(parser.evalNode("/mapper")); configuration.addLoadedResource(resource); bindMapperForNamespace(); } //XML解析可能出现问题,重复处理,例如子ResultMap先于父ResultMap加载 parsePendingResultMaps(); parsePendingCacheRefs(); parsePendingStatements(); }
configurationElement(parser.evalNode("/mapper")); 解析Mapper标签 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")); //解析parameterMap标签:configuration.parameterMaps parameterMapElement(context.evalNodes("/mapper/parameterMap")); //解析resultMap标签:configuration.resultMaps resultMapElements(context.evalNodes("/mapper/resultMap")); //解析sql标签:configuration.sqlFragments sqlElement(context.evalNodes("/mapper/sql")); //解析:CRUD标签映射为MapperStatement //分析:构建sql原材料ParameterMaps、ResuldMap、sql片段、namespace准备后开始构建 buildStatementFromContext(context.evalNodes("select|insert|update|delete")); } catch (Exception e) { throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e); } }
构建MapperStatement 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); } } }
statementParser.parseStatementNode(); 解析成MapperStatement public void parseStatementNode() { String id = context.getStringAttribute("id"); String databaseId = context.getStringAttribute("databaseId"); if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) { return; } Integer fetchSize = context.getIntAttribute("fetchSize"); Integer timeout = context.getIntAttribute("timeout"); String parameterMap = context.getStringAttribute("parameterMap"); String parameterType = context.getStringAttribute("parameterType"); Class<?> parameterTypeClass = resolveClass(parameterType); String resultMap = context.getStringAttribute("resultMap"); String resultType = context.getStringAttribute("resultType"); String lang = context.getStringAttribute("lang"); LanguageDriver langDriver = getLanguageDriver(lang); Class<?> resultTypeClass = resolveClass(resultType); String resultSetType = context.getStringAttribute("resultSetType"); StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString())); ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType); String nodeName = context.getNode().getNodeName(); SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH)); boolean isSelect = sqlCommandType == SqlCommandType.SELECT; boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect); boolean useCache = context.getBooleanAttribute("useCache", isSelect); boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false); //填充include refid引用sql片段 XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant); includeParser.applyIncludes(context.getNode()); //解析mybatis selectkey主键生成策略标签 processSelectKeyNodes(id, parameterTypeClass, langDriver); // 解析sql SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass); String resultSets = context.getStringAttribute("resultSets"); String keyProperty = context.getStringAttribute("keyProperty"); String keyColumn = context.getStringAttribute("keyColumn"); KeyGenerator keyGenerator; //MapperStatementId生成策略:名称空间 + id + !操作类型key String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX; keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true); if (configuration.hasKeyGenerator(keyStatementId)) { keyGenerator = configuration.getKeyGenerator(keyStatementId); } else { keyGenerator = context.getBooleanAttribute("useGeneratedKeys", configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType)) ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE; } builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets); }
configuration添加MapperStatement public MappedStatement addMappedStatement( String id,SqlSource sqlSource,StatementType statementType,SqlCommandType sqlCommandType,Integer fetchSize,Integer timeout,String parameterMap,Class<?> parameterType,String resultMap,Class<?> resultType,ResultSetType resultSetType,boolean flushCache,boolean useCache,boolean resultOrdered,KeyGenerator keyGenerator,String keyProperty,String keyColumn,String databaseId,LanguageDriver lang, String resultSets) { if (unresolvedCacheRef) { throw new IncompleteElementException("Cache-ref not yet resolved"); } id = applyCurrentNamespace(id, false); boolean isSelect = sqlCommandType == SqlCommandType.SELECT; MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType) .resource(resource) .fetchSize(fetchSize) .timeout(timeout) .statementType(statementType) .keyGenerator(keyGenerator) .keyProperty(keyProperty) .keyColumn(keyColumn) .databaseId(databaseId) .lang(lang) .resultOrdered(resultOrdered) .resultSets(resultSets) .resultMaps(getStatementResultMaps(resultMap, resultType, id)) .resultSetType(resultSetType) .flushCacheRequired(valueOrDefault(flushCache, !isSelect)) .useCache(valueOrDefault(useCache, isSelect)) .cache(currentCache); ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id); if (statementParameterMap != null) { statementBuilder.parameterMap(statementParameterMap); } MappedStatement statement = statementBuilder.build(); configuration.addMappedStatement(statement); return statement; }
绑定命名空间: bindMapperForNamespace();
org.apache.ibatis.builder.xml.XMLMapperBuilder#bindMapperForNamespace
本质:命名空间和Mapper接口绑定,方便后续通过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)) { // 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); //mapperRegistry:保存代理工厂类 configuration.addMapper(boundType); } } } }
至此MapperStatement初始化工作完成

科技资讯:

科技学院:

科技百科:

科技书籍:

网站大全:

软件大全:

热门排行