如果只是学会mybatis的使用,那么在之前的博客中对mybatis一些基本的使用已经做了比较详细的说明了。但是在开发中,对于很多东西我们需要知道原理,才能对源码进行修改,写出更好的代码,对mybatis理解的更加深入,做到知其然并知其所以然。之前的博客:http://blog.csdn.net/j903829182/article/details/73382280
接下来是对mybatis的底层设计和实现原理做一些研究。mybatis的运行分为两部分,第一部分是读取配置文件缓存到Configuration对象,用以创建SqlSessionFactory,第二部分是SqlSession的执行过程。相对而言,SqlSessionFactory的创建比较容易理解,而SqlSession的执行过程就不是那么简单了,它将包括许多辅助的技术,我们先讨论反射技术和动态代理技术,这是揭示mybatis底层架构的基础。
当我们掌握了mybatis的运行原理以后,我们就可以知道mybatis是怎么运行的,这是后面学习插件技术的基础。
一,涉及的技术难点简介
我们知道Mapper仅仅是一个接口,而不是包含逻辑的实现类,一个接口是没办法执行的,那么它是怎么运行的呢?其实很显然Mapper产生了代理类,这个代理类是Mybatis为我们创建的,为此先学习下代理。
首先,代理有一个代理模式。所谓的代理模式就是在原有的服务上多加一个占位,通过这个占位去控制服务的访问。一般而言,动态代理分为两种,一种是基于JDK反射机制提供的代理,另外一种是CGLIB代理。在JKD提供的代理,我们必须要提供接口,而CGLIB则不需要提供接口,在mybatis里面两种动态技术都已经使用了。在学习之前先学习下反射技术。
1,反射技术
在java中反射在很多地方都有用到,下面来实现一个简单反射的例子。
package com.jack.reflect;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* create by jack 2017/12/18
*/
public class ReflectService {
/**
* 服务方法
* @param name
*/
public void sayHello(String name){
System.out.println("hello , "+name);
}
public static void main(String[] args) {
try {
//通过反射创建ReflectService对象
Object service = Class.forName(ReflectService.class.getName()).newInstance();
//获取服务方法-sayHello
Method method = service.getClass().getMethod("sayHello",String.class);
//反射调用方法
method.invoke(service, "jack");
System.out.println(service.getClass());
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
上面的代码通过反射技术去创建ReflectService对象,获取方法后通过反射调用。
反射调用的最大好处是配置性大大提高了,就如同spring ioc容器一样,我们可以给很多配置设置参数,使得java应用程序能够顺利运行起来,大大提高了java的灵活性和可配置性,降低了模块之间的耦合。
2,JDK动态代理
JDK的动态代理,是由JDK的java.lang.reflect.*包提供支持的,我们需要完成下面两步:
1)编写服务的类和接口,这个是真正的服务提供者,在JDK代理中接口是必须的。
2)编写代理类,提供邦定和代理方法
JDK的代理最大的缺点是需要提供接口,而mybatis的Mapper就是一个接口,它采用的就是JDK的动态代理。下面先给出一个服务接口:
package com.jack.service;
/**
* create by jack 2017/12/18
*/
public interface HelloService {
public void sayHello(String name);
}
然后是一个实现类,代码如下:
package com.jack.impl;
import com.jack.service.HelloService;
/**
* create by jack 2017/12/18
*/
public class HelloServiceImpl implements HelloService{
public void sayHello(String name) {
System.out.println("hello , "+name);
}
}
下面我们编写一个代理类,提供真实对象的绑定和代理方法。代理类的要求是实现InvocationHandler接口的代理方法,当一个对象被绑定后,执行其方法的时候就会进入到代理方法里,代码如下:
package com.jack.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* create by jack 2017/12/18
*/
public class HelloServiceProxy implements InvocationHandler{
/**
* 真实服务对象
*/
private Object target;
/**
*通过代理对象调用方法首先进入这个方法
* @param proxy 代理对象
* @param method 被调用方法
* @param args 方法的参数
* @return
* @throws Throwable
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("-----------这是JDK动态代理---------------");
Object result = null;
//反射方法前调用
System.out.println("我准备说hello......");
//执行方法,相当于调用HelloServiceImpl类的sayHello方法
result = method.invoke(target, args);
//反射方法后调用
System.out.println("我说过hello了");
return result;
}
/**
*绑定一个委托对象并返回一个代理类
* @param target
* @return
*/
public Object bind(Object target) {
this.target = target;
//取得代理对象,jdk代理需要提供接口
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
}
//取得代理对象,jdk代理需要提供接口
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
上面这段代码让JDK产生了一个代理对象。这个代理对象有三个参数:第一个参数是类加载器,第二个参数是接口(代理对象挂在哪个接口下),第三个参数是this代表当前HelloServiceProxy类,换句话说是使用HelloServiceProxy的代理方法作为对象的代理执行者。
一旦绑定后,在进入代理对象方法调用的时候就会到HelloServiceProxy的代理方法上,代理方法有三个参数:第一个proxy是代理对象,第二是当前调用的那个方法,第三个是方法的参数。比如说,现在HelloServiceImpl对象(obj)用bind方法绑定后,返回其占位,我们再调用proxy.sayHello()方法,那么它就会进入到HelloServiceProxy的invoke()方法。而invoke参数中第一个便是代理对象proxy,方法便是sayHello。
我们已经用HelloServiceProxy类的属性target保存了真实的服务对象,那么我们可以通过反射技术调度真实对象的方法。
result = method.invoke(target,args);
这里我们演示了JDK动态代理的实现,并且在调用方法前后都可以加入我们想要的东西。mybatis在使用Mapper的时候也是这样做的。
下面我们测试下动态代理,代码如下:
package com.jack.test;
import com.jack.impl.HelloServiceImpl;
import com.jack.proxy.HelloServiceProxy;
import com.jack.service.HelloService;
/**
* create by jack 2017/12/18
*/
public class HelloServiceMain {
public static void main(String[] args) {
//创建实现代理接口对象
HelloServiceProxy HelloHandler = new HelloServiceProxy();
//获取代理对象
HelloService proxy = (HelloService) HelloHandler.bind(new HelloServiceImpl());
//方法调用
proxy.sayHello("jack");
}
}
运行测试代码,输出如下:
-----------这是JDK动态代理---------------
我准备说hello......
hello , jack
我说过hello了
Process finished with exit code 0
3,CGLIB动态代理
JDK提供的动态代理存在一个缺陷,就是你必须提供接口才可以使用,为了克服这个缺陷,我们可以使用开源框架CGLIB,它是一种流行的动态代理。
下面我们看看如何使用CGLIB动态代理。HelloService和HelloServiceImpl类都不需要改变,但是我们要实现CGLIB的代理类。要实现CGLIB实现代理,首先需要引入cglib包,引入代码如下:
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.5</version>
</dependency>
它的实现MethodInterceptor的代理方法如下:
package com.jack.impl;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* create by jack 2017/12/18
*/
public class HelloServiceCgLib implements MethodInterceptor {
private Object target;
/**
* 创建代理对象
* @param target
* @return
*/
public Object getInstance(Object target){
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
//回调方法
enhancer.setCallback(this);
//创建代理对象
return enhancer.create();
}
/**
* 回调方法
* @param object
* @param method
* @param objects
* @param methodProxy
* @return
* @throws Throwable
*/
public Object intercept(Object object, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("------------我是CGLIB的动态代理--------------");
//反射方法前调用
System.out.println("我准备说hello");
Object returnObj = methodProxy.invokeSuper(object, objects);
//反射方法后调用
System.out.println("我说过hello了");
return returnObj;
}
}
测试cglib动态代理的代码如下:
package com.jack.test;
import com.jack.impl.HelloServiceCgLib;
import com.jack.impl.HelloServiceImpl;
/**
* create by jack 2017/12/18
*/
public class CgLibMain {
public static void main(String[] args) {
HelloServiceCgLib cgLib = new HelloServiceCgLib();
HelloServiceImpl proxyImpl = (HelloServiceImpl) cgLib.getInstance(new HelloServiceImpl());
proxyImpl.sayHello("jack");
}
}
这样便实现了CGLIB的动态代理。在mybatis中通常在延迟加载的时候才会用到CGLIB的动态代理。有了这些基础,我们就可以更好的了解mybatis的解析和运行过程了。
源码地址:https://github.com/wj903829182/mybatis_study1
二,构建SqlSessionFactory过程
SqlSessionFactory是Mybatis的核心类之一,其最重要的功能就是提供创建Mybatis的核心接口SqlSession,所以我们需要先创建SqlSessionFactory,为此我们需要提供配置文件和相关的参数。而mybatis是一个复杂的系统,采用构造模式去创建SqlSessionFactory,我们可以通过SqlSessionFactoryBuilder去构建。构建分为两步。
第一步:通过org.apache.ibatis.builder.xml.XMLConfigBuilder解析配置的XML文件,读取配置参数,并将读取的数据存入这个org.apache.ibatis.session.Configuration类中。注意,mybatis几乎所有的配置都是存在这里的。
第二步:使用Configuration对象去创建SqlSessionFactory。mybatis中的SqlSessionFactory是一个接口,而不是实现类,为此mybatis提供了一个默认的SqlSessionFactory实现类,我们一般都会使用org.apache.ibatis.session.defaults.DefaultSqlSessionFactory。注意,在大部分情况下我们都没有必要自己去创建新的SqlSessionFactory的实现类。
这种创建的方式就是一种Builder模式。对于复杂的对象而言,直接使用构造方法构建是由困难的,这会导致大量的逻辑放在构造方法中,由于对象的复杂性,在构建的时候,我们更希望一步步的来构建它,从而降低其复杂性。这个时候使用一个参数类总领全局,例如,Configuration类,然后分步构建,例如,DefaultSqlSessionFactory类,就可以构建一个复杂的对象,例如,SqlSessionFactory,这种方式值得学习。
1,构建Configuration
在SqlSessionFactory构建中,Configuration是最重要的,它的作用如下:
1)读入配置文件,包括基础配置的xml文件和映射器的xml文件。
2)初始化基础配置,比如mybatis的别名等,一些重要的类对象,例如,插件,映射器,ObjectFactory和typeHandler对象。
3)提供单例,为后续创建SessionFactory服务并提供配置的参数。
4)执行一些重要的对象方法,初始化配置信息。
Confinguration不会是一个很简单的类,mybatis的配置信息都会来自于此。Configuration是通过XMLConfigBuilder去构建的。首先mybatis会读出所有xml配置信息。然后,将这些信息保存到Configuration类的单例中。它会做如下初始化:
1)properties全局参数
2)settings设置
3)typeAliases别名
4)typeHandler类型处理器
5)ObjectFactory对象
6)plugin插件
7)environment环境
8)DatabaseIdProvider数据库标识
9)Mapper映射器
2,映射器的内部组成
一般而言,一个映射器是由三个部分组成:
1)MappedStatement,它保存映射器的一个结点(select|insert|delete|update)。包括许多我们配置的sql,sql的id,缓存信息,resultMap,parameterType,resultType,languageDriver等重要配置内容。
2)SqlSource,它是提供BoundSql对象的地方,它是MappedStatement的一个属性。
3)BoundSql,它是建立SQL和参数的地方。它有3个常用的属性:SQL,parameterObject,parameterMappings
上面几个都是映射器的重要内容,也是mybatis的核心内容。在插件的应用中常常会用到它们。映射器的解析过程是比较复杂的,但是在大部分的情况下,我们并不需要去理会解析和组装SQL的规则,因为大部分的插件只要做很小的改变即可,无需做很大的改变。大的改变可能导致重写这些内容。所以一般我们主要关注参数和SQL。
下面看看映射器的内部组成,如下:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.apache.ibatis.mapping;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.session.Configuration;
public final class MappedStatement {
private String resource;
private Configuration configuration;
private String id;
private Integer fetchSize;
private Integer timeout;
private StatementType statementType;
private ResultSetType resultSetType;
private SqlSource sqlSource;
private Cache cache;
private ParameterMap parameterMap;
private List<ResultMap> resultMaps;
private boolean flushCacheRequired;
private boolean useCache;
private boolean resultOrdered;
private SqlCommandType sqlCommandType;
private KeyGenerator keyGenerator;
private String[] keyProperties;
private String[] keyColumns;
private boolean hasNestedResultMaps;
private String databaseId;
private Log statementLog;
private LanguageDriver lang;
private String[] resultSets;
MappedStatement() {
}
public KeyGenerator getKeyGenerator() {
return this.keyGenerator;
}
public SqlCommandType getSqlCommandType() {
return this.sqlCommandType;
}
public String getResource() {
return this.resource;
}
public Configuration getConfiguration() {
return this.configuration;
}
public String getId() {
return this.id;
}
public boolean hasNestedResultMaps() {
return this.hasNestedResultMaps;
}
public Integer getFetchSize() {
return this.fetchSize;
}
public Integer getTimeout() {
return this.timeout;
}
public StatementType getStatementType() {
return this.statementType;
}
public ResultSetType getResultSetType() {
return this.resultSetType;
}
public SqlSource getSqlSource() {
return this.sqlSource;
}
public ParameterMap getParameterMap() {
return this.parameterMap;
}
public List<ResultMap> getResultMaps() {
return this.resultMaps;
}
public Cache getCache() {
return this.cache;
}
public boolean isFlushCacheRequired() {
return this.flushCacheRequired;
}
public boolean isUseCache() {
return this.useCache;
}
public boolean isResultOrdered() {
return this.resultOrdered;
}
public String getDatabaseId() {
return this.databaseId;
}
public String[] getKeyProperties() {
return this.keyProperties;
}
public String[] getKeyColumns() {
return this.keyColumns;
}
public Log getStatementLog() {
return this.statementLog;
}
public LanguageDriver getLang() {
return this.lang;
}
public String[] getResultSets() {
return this.resultSets;
}
/** @deprecated */
@Deprecated
public String[] getResulSets() {
return this.resultSets;
}
public BoundSql getBoundSql(Object parameterObject) {
BoundSql boundSql = this.sqlSource.getBoundSql(parameterObject);
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings == null || parameterMappings.isEmpty()) {
boundSql = new BoundSql(this.configuration, boundSql.getSql(), this.parameterMap.getParameterMappings(), parameterObject);
}
Iterator var4 = boundSql.getParameterMappings().iterator();
while(var4.hasNext()) {
ParameterMapping pm = (ParameterMapping)var4.next();
String rmId = pm.getResultMapId();
if (rmId != null) {
ResultMap rm = this.configuration.getResultMap(rmId);
if (rm != null) {
this.hasNestedResultMaps |= rm.hasNestedResultMaps();
}
}
}
return boundSql;
}
private static String[] delimitedStringToArray(String in) {
return in != null && in.trim().length() != 0 ? in.split(",") : null;
}
public static class Builder {
private MappedStatement mappedStatement = new MappedStatement();
public Builder(Configuration configuration, String id, SqlSource sqlSource, SqlCommandType sqlCommandType) {
this.mappedStatement.configuration = configuration;
this.mappedStatement.id = id;
this.mappedStatement.sqlSource = sqlSource;
this.mappedStatement.statementType = StatementType.PREPARED;
this.mappedStatement.parameterMap = (new org.apache.ibatis.mapping.ParameterMap.Builder(configuration, "defaultParameterMap", (Class)null, new ArrayList())).build();
this.mappedStatement.resultMaps = new ArrayList();
this.mappedStatement.sqlCommandType = sqlCommandType;
this.mappedStatement.keyGenerator = (KeyGenerator)(configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType) ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE);
String logId = id;
if (configuration.getLogPrefix() != null) {
logId = configuration.getLogPrefix() + id;
}
this.mappedStatement.statementLog = LogFactory.getLog(logId);
this.mappedStatement.lang = configuration.getDefaultScriptingLanguageInstance();
}
public MappedStatement.Builder resource(String resource) {
this.mappedStatement.resource = resource;
return this;
}
public String id() {
return this.mappedStatement.id;
}
public MappedStatement.Builder parameterMap(ParameterMap parameterMap) {
this.mappedStatement.parameterMap = parameterMap;
return this;
}
public MappedStatement.Builder resultMaps(List<ResultMap> resultMaps) {
this.mappedStatement.resultMaps = resultMaps;
Iterator var2 = resultMaps.iterator();
while(var2.hasNext()) {
ResultMap resultMap = (ResultMap)var2.next();
this.mappedStatement.hasNestedResultMaps = this.mappedStatement.hasNestedResultMaps || resultMap.hasNestedResultMaps();
}
return this;
}
public MappedStatement.Builder fetchSize(Integer fetchSize) {
this.mappedStatement.fetchSize = fetchSize;
return this;
}
public MappedStatement.Builder timeout(Integer timeout) {
this.mappedStatement.timeout = timeout;
return this;
}
public MappedStatement.Builder statementType(StatementType statementType) {
this.mappedStatement.statementType = statementType;
return this;
}
public MappedStatement.Builder resultSetType(ResultSetType resultSetType) {
this.mappedStatement.resultSetType = resultSetType;
return this;
}
public MappedStatement.Builder cache(Cache cache) {
this.mappedStatement.cache = cache;
return this;
}
public MappedStatement.Builder flushCacheRequired(boolean flushCacheRequired) {
this.mappedStatement.flushCacheRequired = flushCacheRequired;
return this;
}
public MappedStatement.Builder useCache(boolean useCache) {
this.mappedStatement.useCache = useCache;
return this;
}
public MappedStatement.Builder resultOrdered(boolean resultOrdered) {
this.mappedStatement.resultOrdered = resultOrdered;
return this;
}
public MappedStatement.Builder keyGenerator(KeyGenerator keyGenerator) {
this.mappedStatement.keyGenerator = keyGenerator;
return this;
}
public MappedStatement.Builder keyProperty(String keyProperty) {
this.mappedStatement.keyProperties = MappedStatement.delimitedStringToArray(keyProperty);
return this;
}
public MappedStatement.Builder keyColumn(String keyColumn) {
this.mappedStatement.keyColumns = MappedStatement.delimitedStringToArray(keyColumn);
return this;
}
public MappedStatement.Builder databaseId(String databaseId) {
this.mappedStatement.databaseId = databaseId;
return this;
}
public MappedStatement.Builder lang(LanguageDriver driver) {
this.mappedStatement.lang = driver;
return this;
}
public MappedStatement.Builder resultSets(String resultSet) {
this.mappedStatement.resultSets = MappedStatement.delimitedStringToArray(resultSet);
return this;
}
/** @deprecated */
@Deprecated
public MappedStatement.Builder resulSets(String resultSet) {
this.mappedStatement.resultSets = MappedStatement.delimitedStringToArray(resultSet);
return this;
}
public MappedStatement build() {
assert this.mappedStatement.configuration != null;
assert this.mappedStatement.id != null;
assert this.mappedStatement.sqlSource != null;
assert this.mappedStatement.lang != null;
this.mappedStatement.resultMaps = Collections.unmodifiableList(this.mappedStatement.resultMaps);
return this.mappedStatement;
}
}
}
SqlSource是MappedStatement的一个属性,
public BoundSql getBoundSql(Object parameterObject) {
BoundSql boundSql = this.sqlSource.getBoundSql(parameterObject);
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings == null || parameterMappings.isEmpty()) {
boundSql = new BoundSql(this.configuration, boundSql.getSql(), this.parameterMap.getParameterMappings(), parameterObject);
}
Iterator var4 = boundSql.getParameterMappings().iterator();
while(var4.hasNext()) {
ParameterMapping pm = (ParameterMapping)var4.next();
String rmId = pm.getResultMapId();
if (rmId != null) {
ResultMap rm = this.configuration.getResultMap(rmId);
if (rm != null) {
this.hasNestedResultMaps |= rm.hasNestedResultMaps();
}
}
}
return boundSql;
}
上面的方法是MappedStatement的一个方法,SqlSource是一个接口,它的作用是根据参数和其他的规则组装SQL,包括动态sql。对于参数和sql而言,主要的规则都反映在BoundSql类对象上,在插件中往往需要拿到它进而可以拿到运行的sql和参数规则,做出适当的修改,来满足我们特殊的需求。
BoundSql会提供3个主要的属性:parameterMappings,parameterObject和sql,源码如下:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.apache.ibatis.mapping;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.property.PropertyTokenizer;
import org.apache.ibatis.session.Configuration;
public class BoundSql {
private final String sql;
private final List<ParameterMapping> parameterMappings;
private final Object parameterObject;
private final Map<String, Object> additionalParameters;
private final MetaObject metaParameters;
public BoundSql(Configuration configuration, String sql, List<ParameterMapping> parameterMappings, Object parameterObject) {
this.sql = sql;
this.parameterMappings = parameterMappings;
this.parameterObject = parameterObject;
this.additionalParameters = new HashMap();
this.metaParameters = configuration.newMetaObject(this.additionalParameters);
}
public String getSql() {
return this.sql;
}
public List<ParameterMapping> getParameterMappings() {
return this.parameterMappings;
}
public Object getParameterObject() {
return this.parameterObject;
}
public boolean hasAdditionalParameter(String name) {
String paramName = (new PropertyTokenizer(name)).getName();
return this.additionalParameters.containsKey(paramName);
}
public void setAdditionalParameter(String name, Object value) {
this.metaParameters.setValue(name, value);
}
public Object getAdditionalParameter(String name) {
return this.metaParameters.getValue(name);
}
}
1)其中parameterObject为参数本身。我们可以传递简单对象,POJO,Map或者@Param注解的参数,由于它在插件中相当常用,需要细细研究。
2)传递简单对象(包括int,String,float,double等),比如当我们传递int类型时,Mybatis会把参数变为Integer对象传递,类型的long,String,float,double也是如此。
3)如果传递的是POJO或者Map,那么这个parameterObject就是你传入的POJO或者Map不变。
4)当然我们也可以传递多个参数,如果没有@Param注解,那么mybatis就会把parameterObject变为一个Map<String,Object>对象,其键值的关系是按顺序来规划的类似于这样的形式{"1":p1,"2":p2,"3":p3..........,"param1":p1,"param2":p2......},所以在编写的时候我们都可以使用#{param1}或者#{1}去引用第一个参数。
5)如果我们使用@Param注解,那么mybatis就会把parameterObject也会变成一个Map<String,Object>对象,类似于没有@Param注解,只是把其数字的键值对应置换为了@Param注解的键值。比如我们注解@Param("key1") String p1,@Param("key2") int p2,@Param("key3") Role p3,那么这个parameterObject对象就是一个Map<String,Object>,它的键值包含:{"key1":p1,"key2":p2,"key3":p3,"param1":p1,"param2":p2,"param3":p3}
6)parameterMappings,它是一个List,每一个元素都是ParameterMapping的对象。这个对象会描述我们的参数。参数包括属性,名称,表达式,javaType,jdbcType,typeHandler等重要信息,我们一般不需要去改变它。通过它可以实现参数和SQL的结合,以便PreparedStatement能够通过它找到parameterObject对象的属性并设置参数,使得查询准确运行。
7)sql属性就是我们书写在映射器里面的一条sql,在大多数的时候无需修改它,只有在插件的情况下,我们可以根据需要进行改写。改写sql将是一件危险的事情,务必小心谨慎。
3,构建SqlSessionFactory
有了Configuration对象构建SqlSessionFactory就很简单了,我们只要写很简单的代码就可以了:
sqlSessionFactory = new SqlSessionFactoryBuilder().builder(inputStream);
mybatis会根据Configuration的配置读取所配置的信息,构建SqlSessionFactory对象。