一、加載xml的入口
mybatis配置xml路徑的類,一般都在SqlSessionFactoryBean。怎么加載它呢?有兩種方式:
- xml形式:
<bean id="xxxSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"
p:dataSource-ref="xxxDataSource" p:mapperLocations="classpath*:mapper/*.xml">
<property name="configuration">
<bean class="org.apache.ibatis.session.Configuration">
<property name="mapUnderscoreToCamelCase" value="true" />
</bean>
</property>
</bean>
- @Configuration注解形式
@Bean(name = "xxxSqlSessionFactory")
public SqlSessionFactory xxxSqlSessionFactory(@Qualifier("xxxDataSource") DataSource dataSource){
SqlSessionFactory sqlSessionFactory = null;
try {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean() ;
sqlSessionFactoryBean.setMapperLocations(applicationContext.getResources("classpath*:mapper/crt/*.xml"));
sqlSessionFactoryBean.setDataSource(dataSource);
sqlSessionFactory = sqlSessionFactoryBean.getObject();
sqlSessionFactory.getConfiguration().setMapUnderscoreToCamelCase(true);
} catch (Exception e) {
LOGGER.error(e.getMessage(),e);
throw new CouponBizException(CouponBizCodeEnum.SYSTEM_ERROR) ;
}
return sqlSessionFactory ;
}
不管哪種方式,最終都會(huì)執(zhí)行到sqlSessionFactoryBean.getObject()方法,得到一個(gè)SqlSessionFactory接口的實(shí)現(xiàn)類,具體是哪個(gè)實(shí)現(xiàn)類,下面會(huì)講到。好了,入口知道了,我們就開始繼續(xù)深入吧~
二、加載Configuration
應(yīng)用在啟動(dòng)的時(shí)候,Spring容器會(huì)觸發(fā)sqlSessionFactoryBean.getObject()方法得到單例對(duì)象,以便放到容器中。看下getObject方法:
@Override
public SqlSessionFactory getObject() throws Exception {
if (this.sqlSessionFactory == null) {
afterPropertiesSet();
}
return this.sqlSessionFactory;
}
第一次必然會(huì)調(diào)用afterPropertiesSet()。繼續(xù)深入看下源碼:
@Override
public void afterPropertiesSet() throws Exception {
notNull(dataSource, "Property 'dataSource' is required");
notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
"Property 'configuration' and 'configLocation' can not specified with together");
this.sqlSessionFactory = buildSqlSessionFactory();
}
sqlSessionFactory接口的實(shí)現(xiàn)類,就在buildSqlSessionFactory()里面。繼續(xù)深入:
protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
Configuration configuration;
//說明了mapper接口的配置,默認(rèn)用xml來實(shí)現(xiàn)
XMLConfigBuilder xmlConfigBuilder = null;
//上面的xml配置,我們已經(jīng)傳入configuration了,這里必然會(huì)進(jìn)來
if (this.configuration != null) {
configuration = this.configuration;
//property字段,我們并沒有配置多余其他字段,所以這里都不會(huì)進(jìn)來
if (configuration.getVariables() == null) {
configuration.setVariables(this.configurationProperties);
} else if (this.configurationProperties != null) {
configuration.getVariables().putAll(this.configurationProperties);
}
} else if (this.configLocation != null) {
xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
configuration = xmlConfigBuilder.getConfiguration();
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
}
configuration = new Configuration();
if (this.configurationProperties != null) {
configuration.setVariables(this.configurationProperties);
}
}
//xml的配置未配置該<property />標(biāo)簽,無(wú)需進(jìn)來
if (this.objectFactory != null) {
configuration.setObjectFactory(this.objectFactory);
}
//xml的配置未配置該<property />標(biāo)簽,無(wú)需進(jìn)來
if (this.objectWrapperFactory != null) {
configuration.setObjectWrapperFactory(this.objectWrapperFactory);
}
//xml的配置未配置該<property />標(biāo)簽,無(wú)需進(jìn)來
if (this.vfs != null) {
configuration.setVfsImpl(this.vfs);
}
//xml的配置未配置該<property />標(biāo)簽,無(wú)需進(jìn)來
if (hasLength(this.typeAliasesPackage)) {
String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
for (String packageToScan : typeAliasPackageArray) {
configuration.getTypeAliasRegistry().registerAliases(packageToScan,
typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Scanned package: '" + packageToScan + "' for aliases");
}
}
}
//xml的配置未配置該<property />標(biāo)簽,無(wú)需進(jìn)來
if (!isEmpty(this.typeAliases)) {
for (Class<?> typeAlias : this.typeAliases) {
configuration.getTypeAliasRegistry().registerAlias(typeAlias);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Registered type alias: '" + typeAlias + "'");
}
}
}
//xml的配置未配置該<property />標(biāo)簽,無(wú)需進(jìn)來
if (!isEmpty(this.plugins)) {
for (Interceptor plugin : this.plugins) {
configuration.addInterceptor(plugin);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Registered plugin: '" + plugin + "'");
}
}
}
//xml的配置未配置該<property />標(biāo)簽,無(wú)需進(jìn)來
if (hasLength(this.typeHandlersPackage)) {
String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
for (String packageToScan : typeHandlersPackageArray) {
configuration.getTypeHandlerRegistry().register(packageToScan);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Scanned package: '" + packageToScan + "' for type handlers");
}
}
}
//xml的配置未配置該<property />標(biāo)簽,無(wú)需進(jìn)來
if (!isEmpty(this.typeHandlers)) {
for (TypeHandler<?> typeHandler : this.typeHandlers) {
configuration.getTypeHandlerRegistry().register(typeHandler);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Registered type handler: '" + typeHandler + "'");
}
}
}
//xml的配置未配置該<property />標(biāo)簽,無(wú)需進(jìn)來
if (this.databaseIdProvider != null) {//fix #64 set databaseId before parse mapper xmls
try {
configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
} catch (SQLException e) {
throw new NestedIOException("Failed getting a databaseId", e);
}
}
//xml的配置未配置該<property />標(biāo)簽,無(wú)需進(jìn)來
if (this.cache != null) {
configuration.addCache(this.cache);
}
//xmlConfigBuilder尚未初始化,無(wú)需進(jìn)來
if (xmlConfigBuilder != null) {
try {
xmlConfigBuilder.parse();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Parsed configuration file: '" + this.configLocation + "'");
}
} catch (Exception ex) {
throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
} finally {
ErrorContext.instance().reset();
}
}
//xml的配置未配置該<property />標(biāo)簽,所以必為null,需要進(jìn)來
if (this.transactionFactory == null) {
//默認(rèn)的transactionFactory是SpringManagedTransactionFactory
this.transactionFactory = new SpringManagedTransactionFactory();
}
//創(chuàng)建Environment,其實(shí)就是為了封裝dataSource和transactionFactory
configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));
//xml的配置了mapperLocations,所以不為null,需要進(jìn)來
if (!isEmpty(this.mapperLocations)) {
//遍歷每個(gè)mapper文件
for (Resource mapperLocation : this.mapperLocations) {
if (mapperLocation == null) {
continue;
}
try {
//為每個(gè)xml文件,初始化一個(gè)xmlMapperBuilder。它含有configuration,目的就是容納下面parse xml得到的結(jié)果
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
configuration, mapperLocation.toString(), configuration.getSqlFragments());
//開始解析xml文件,并把解析出來的所有標(biāo)簽,放到configuration對(duì)應(yīng)的字段上
xmlMapperBuilder.parse();
} catch (Exception e) {
throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
} finally {
ErrorContext.instance().reset();
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'");
}
}
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found");
}
}
return this.sqlSessionFactoryBuilder.build(configuration);
}
有點(diǎn)長(zhǎng)。。。
看完里面的注釋,也就都明白了。加載configuration的核心就在xmlMapperBuilder.parse()方法里面。
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
bindMapperForNamespace();
}
parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
然后進(jìn)入configurationElement方法:
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"));
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
resultMapElements(context.evalNodes("/mapper/resultMap"));
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);
}
}
可以看到先解析<mapper> .... </mapper>標(biāo)簽里面的東東,然后遞歸繼續(xù)解析mapper里面的<resultMap>....</resultMap>,以及<sql> ....</sql>。我們來看一下,mapper的xml標(biāo)簽格式好了。
<?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.xxx.xxx.xxxMapper">
<resultMap id="xxxMap" type="com.xxx.xxx">
<id column="id" property="id"/>
<result column="uid" property="uid" />
<result column="created_time" property="createdTime" />
<result column="updated_time" property="updatedTime" />
....
</resultMap>
<sql id="Base_Column_List">
id, uid....
</sql>
<insert id="insert" parameterType="com.xxx.xxx">
insert into xxx values()
</insert>
<select id="getById" parameterType="java.util.Map" resultType="com.xxx.xxx">
select <include refid="Base_Column_List" /> from xxx where id=#{id}
</select>
<update id="updateStatusById" parameterType="java.util.Map">
update xxxx
set status= #{targetStatus, jdbcType=NUMERIC}
where id = #{id} and status = #{sourceStatus}
</update>
</mapper>
可以看到解析的入口就在<mapper>標(biāo)簽,而它恰好就是mapper xml文件的格式。然后依次按照規(guī)范解析其余標(biāo)簽,以及標(biāo)簽里面的屬性信息,放到Configuration對(duì)應(yīng)的字段。
下面我們以解析<select>標(biāo)簽為例,解析源碼:
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 Fragments before parsing
XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
includeParser.applyIncludes(context.getNode());
// Parse selectKey after includes and remove them.
processSelectKeyNodes(id, parameterTypeClass, langDriver);
// Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
String resultSets = context.getStringAttribute("resultSets");
String keyProperty = context.getStringAttribute("keyProperty");
String keyColumn = context.getStringAttribute("keyColumn");
KeyGenerator keyGenerator;
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);
}
獲取<select>標(biāo)簽上面的屬性,很多屬性是不是很陌生。但是最常見的幾個(gè)屬性,我們一定知道。比如:id, parameterMap, resultMap。其實(shí)這些屬性在mybatis的dtd描述文件里面就有的,不信我們點(diǎn)擊xml的<select>標(biāo)簽,是可以點(diǎn)擊進(jìn)去的??吹饺缦拢?/p>
<!ELEMENT select (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>
<!ATTLIST select
id CDATA #REQUIRED
parameterMap CDATA #IMPLIED
parameterType CDATA #IMPLIED
resultMap CDATA #IMPLIED
resultType CDATA #IMPLIED
resultSetType (FORWARD_ONLY | SCROLL_INSENSITIVE | SCROLL_SENSITIVE) #IMPLIED
statementType (STATEMENT|PREPARED|CALLABLE) #IMPLIED
fetchSize CDATA #IMPLIED
timeout CDATA #IMPLIED
flushCache (true|false) #IMPLIED
useCache (true|false) #IMPLIED
databaseId CDATA #IMPLIED
lang CDATA #IMPLIED
resultOrdered (true|false) #IMPLIED
resultSets CDATA #IMPLIED
>
id, parameterMap, resultMap等等每一個(gè)屬性都有定義好,只不過解析的時(shí)候拿出來就好了。最終所有字段解析完成后,會(huì)調(diào)用如下:
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
該方法會(huì)生成MappedStatement,它會(huì)接收<select>標(biāo)簽所有屬性,包括:真正的sql語(yǔ)句。但是我們的sql語(yǔ)句是動(dòng)態(tài)的,也就是說有條件的,只有在真正執(zhí)行的才能確定sql語(yǔ)句。那么xml的靜態(tài)的sql語(yǔ)句是怎么保存的呢?答案就在sqlSource字段。debug查看,如下:

內(nèi)容被切割為不同類型的對(duì)象了,比如:StaticTextSqlNode對(duì)象存放固定sql語(yǔ)句,如where前面的語(yǔ)句,它肯定是固定不變的,無(wú)需動(dòng)態(tài)生成。而IfSqlNode它有test字段String類型,又有contents字段,而contents又是一個(gè)StaticTextSqlNode屬于靜態(tài)不變的語(yǔ)句。
可以看出mybatis的動(dòng)態(tài)sql,會(huì)在初始化的時(shí)候生成sqlSource這種模板,后面再運(yùn)行的時(shí)候,會(huì)根據(jù)sql請(qǐng)求參數(shù),匹配這個(gè)sqlSource,最終生成要執(zhí)行的sql。
MappedStatement生成完成后,執(zhí)行configuration.addMappedStatement(statement),加入到configuration。
總結(jié):到這里我們就知道了xml的配置根據(jù)標(biāo)簽,一個(gè)一個(gè)去解析。解析完成后,最終會(huì)生成MappedStatement對(duì)象,然后把它添加到configuration。整個(gè)解析完成后,configuration是不是就擁有了所有xml的配置信息了,包括:sql語(yǔ)句,以及sql的返回值字段到對(duì)象的映射關(guān)系。這些sql執(zhí)行和sql結(jié)果映射需要的東西,全部都在configuration里面了。
下一節(jié)準(zhǔn)備分析:
- Mybatis中Sql解析執(zhí)行的原理是什么?
- Mybatis中Executor接口有幾種實(shí)現(xiàn)方式
未完待續(xù)。。。
三、Mybatis中Sql解析執(zhí)行的原理是什么?
3.1 先來介紹sql 解析的原理
可以看下面的這篇文章:
Mybatis解析動(dòng)態(tài)sql原理分析
其實(shí)上面講的更加透徹,從sqlNode的多個(gè)實(shí)現(xiàn)類來解釋。mybatis根據(jù)不同標(biāo)簽,把sql語(yǔ)句切分不同部分,然后對(duì)各個(gè)部分分別處理。如果是靜態(tài)文本,就用StaticTextSqlNode,如果是if標(biāo)簽用IfSqlNode,其他類推。就像上一節(jié)截圖那樣,最終sql語(yǔ)句會(huì)被解析問sqlSource,傳遞給configuration。
3.2 再來介紹sql 執(zhí)行的原理
當(dāng)然可以先看這個(gè)文章:mybatis調(diào)用過程
上面的文章是從mapper整個(gè)調(diào)用過程來切入的,這節(jié)我們只介紹sql具體執(zhí)行,是其中的一小塊內(nèi)容。首先我們從下面的源碼切入:
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameter);
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
第一步肯定就是生成動(dòng)態(tài)sql了,如下:
public BoundSql getBoundSql(Object parameterObject) {
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings == null || parameterMappings.isEmpty()) {
boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
}
// check for nested result maps in parameter mappings (issue #30)
for (ParameterMapping pm : boundSql.getParameterMappings()) {
String rmId = pm.getResultMapId();
if (rmId != null) {
ResultMap rm = configuration.getResultMap(rmId);
if (rm != null) {
hasNestedResultMaps |= rm.hasNestedResultMaps();
}
}
}
return boundSql;
}
繼續(xù)跟進(jìn)getBoundSql方法:
@Override
public BoundSql getBoundSql(Object parameterObject) {
DynamicContext context = new DynamicContext(configuration, parameterObject);
rootSqlNode.apply(context);
SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
for (Map.Entry<String, Object> entry : context.getBindings().entrySet()) {
boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());
}
return boundSql;
}
rootSqlNode.apply(context)一般情況是MixedSqlNode,進(jìn)入該類:
@Override
public boolean apply(DynamicContext context) {
for (SqlNode sqlNode : contents) {
sqlNode.apply(context);
}
return true;
}
會(huì)根據(jù)sql語(yǔ)句不同部分,分別調(diào)用apply。apply方法完成后,最終會(huì)成為一個(gè)如下的sql語(yǔ)句:
select count(1) from xxx e left join yyy on e.order_no = rff.biz_no
where e.sob_id = #{xxx} and e.deal_date >= #{yyy} and e.deal_date <= #{dealDateEnd} and e.active=1
但是參數(shù)值并沒有寫進(jìn)去,說明還不是最終要執(zhí)行的sql。然后由sqlSourceParse.parse方法,會(huì)把所有'#{‘開頭的匹配出來,變成'?',得到如下的sql:
select count(1) from xxx e left join yyyy rff on e.order_no = rff.biz_no
where e.sob_id = ? and e.deal_date >= ? and e.deal_date <= ? and e.active=1
and e.staff_id = ?
這個(gè)sql還是沒有參數(shù),還不是最終執(zhí)行的sql。但是它得到sqlSource變成了StaticSqlSource類型了,因?yàn)閯?dòng)態(tài)sql其實(shí)已經(jīng)構(gòu)造完成,它就變成了靜態(tài)的了,只需要綁定參數(shù)即可。
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();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
到這里傳遞進(jìn)來的BoundSql都是帶有"?"的sql語(yǔ)句,看下prepareStatement方法:
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;
}
參數(shù)的綁定就發(fā)生在 handler.parameterize(stmt)里面,它會(huì)匹配參數(shù)里面的key和查詢條件的key,然后把對(duì)應(yīng)的value構(gòu)造到sql中。
就不深入進(jìn)去了,太多了。。。
至于sql的執(zhí)行,mybatis解析完成動(dòng)態(tài)sql后,它的職責(zé)就完成了。剩下的就交給dataSource的Connection去真正執(zhí)行網(wǎng)絡(luò)請(qǐng)求,通過JDBC組件,構(gòu)建mysql應(yīng)用能夠識(shí)別的應(yīng)用層協(xié)議報(bào)文,發(fā)送給服務(wù)器。然后服務(wù)器查詢得到結(jié)果后,返回給mybatis。
JDBC是如何初始化連接的,如何握手驗(yàn)證密碼的,以及如何調(diào)用的,并不在本文討論范圍。
當(dāng)然JDBC里面有一堆控制,比如超時(shí),重試,事務(wù)等等控制,并不僅僅只有網(wǎng)絡(luò)傳輸。
四、Mybatis如果進(jìn)行ORM轉(zhuǎn)換,把數(shù)據(jù)庫(kù)返回的column-value對(duì)象,通過映射轉(zhuǎn)換為我們POJO呢?
我們?nèi)缦碌拇a:
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.<E> handleResultSets(ps);
}
ps.execute(),這里應(yīng)該是阻塞式調(diào)用。函數(shù)返回,表示ps已經(jīng)收到結(jié)果了。如果對(duì)JDBC是真正如何執(zhí)行sql的,強(qiáng)烈建議跟進(jìn)去execute方法,看看mysql廠商的驅(qū)動(dòng)是怎么構(gòu)造mysql應(yīng)用層協(xié)議的。然后協(xié)議的響應(yīng),你在客戶端肯定是看不著的,你得去研究mysql的源碼才能知道。
我們這里就不展開JDBC了。剩下的我們就開始處理返回的結(jié)果,mybatis如何映射為POJO?
很明顯轉(zhuǎn)換就在handleResultSets方法。
最后給調(diào)用handleRowValues方法:
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
if (resultMap.hasNestedResultMaps()) {
ensureNoRowBounds();
checkResultHandler();
handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
} else {
handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
}
}
最終到typeHandler.getResult(rsw.getResultSet(), columnName)方法:
typeHandler.getResult(rsw.getResultSet(), columnName)
typeHandler有很多實(shí)現(xiàn)類,比如:IntegerTypeHandler,ObjectTypeHandler等等,它會(huì)那映射關(guān)系,一個(gè)一個(gè)取出對(duì)應(yīng)列的值。
END