Mybatis xml解析流程
Xml解析的常见方式:DOM SAX Xpath ,Mybatis使用的时Xpath
,因其足够简单。
对应代码:
String resource="mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
执行流程:
1、 执行SqlSessionFactoryBuilder 中的build
//以下3个方法都是调用下面第8种方法
public SqlSessionFactory build(InputStream inputStream) {
return build(inputStream, null, null);
}
public SqlSessionFactory build(InputStream inputStream, String environment) {
return build(inputStream, environment, null);
}
public SqlSessionFactory build(InputStream inputStream, Properties properties) {
return build(inputStream, null, properties);
}
//第8种方法和第4种方法差不多,Reader换成了InputStream
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
//创建一个XMLConfigBuilder对象
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
//解析xml文件各个结点返回Configuration对象,传入build对象返回DefaultSqlSessionFactory
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
//最后一个build方法使用了一个Configuration作为参数,并返回DefaultSqlSessionFactory
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
}
2、执行XMLConfigBuilder的构造方法
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
//构建XPathParser对象 进行xml解析
this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}
//上面6个构造函数最后都合流到这个函数,传入XPathParser
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
//首先调用父类初始化Configuration
super(new Configuration());
//错误上下文设置成SQL Mapper Configuration(XML文件配置),以便后面出错了报错用吧
ErrorContext.instance().resource("SQL Mapper Configuration");
//将Properties全部设置到Configuration里面去
this.configuration.setVariables(props);
this.parsed = false;
this.environment = environment;
this.parser = parser;
}
3、new Configuration()
(属性都为默认值)
设置configuration
的属性参数
设置XMLConfigBuilder
的其他参数(document对象也在其中创建 即将xml 转化为Document对象)
此时XMLConfigBuilder
构建完成
public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
commonConstructor(validation, variables, entityResolver);
//调用createDocument() 构建document ,底层使用SAX解析 将进来的xml文件 转换为document
this.document = createDocument(new InputSource(inputStream));
}
4、调用parser.parse()
获得Configuration
对象
//解析配置
public Configuration parse() { //只解析一次
//如果已经解析过了,报错
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true; //未解析过 将标志位置为true
// <?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>
// <environments default="development">
// <environment id="development">
// <transactionManager type="JDBC"/>
// <dataSource type="POOLED">
// <property name="driver" value="${driver}"/>
// <property name="url" value="${url}"/>
// <property name="username" value="${username}"/>
// <property name="password" value="${password}"/>
// </dataSource>
// </environment>
// </environments>
// <mappers>
// <mapper resource="org/mybatis/example/BlogMapper.xml"/>
// </mappers>
// </configuration>
//根节点是configuration parser Xpath解析器
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
//解析配置
private void parseConfiguration(XNode root) {
try {
//分步骤解析
//issue #117 read properties first
//1.properties
propertiesElement(root.evalNode("properties"));
//2.类型别名
typeAliasesElement(root.evalNode("typeAliases"));
//3.插件
pluginElement(root.evalNode("plugins"));
//4.对象工厂
objectFactoryElement(root.evalNode("objectFactory"));
//5.对象包装工厂
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
//6.设置
settingsElement(root.evalNode("settings"));
// read it after objectFactory and objectWrapperFactory issue #631
//7.环境
environmentsElement(root.evalNode("environments"));
//8.databaseIdProvider
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
//9.类型处理器
typeHandlerElement(root.evalNode("typeHandlers"));
//10.映射器
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
5、解析对应结点下的各个结点内容,放进Configuration
中
//1.properties
//<properties resource="org/mybatis/example/config.properties">
// <property name="username" value="dev_user"/>
// <property name="password" value="F2Fa3!33TYyg"/>
//</properties>
private void propertiesElement(XNode context) throws Exception {
if (context != null) {
//如果在这些地方,属性多于一个的话,MyBatis 按照如下的顺序加载它们:
//1.在 properties 元素体内指定的属性首先被读取。
//2.从类路径下资源或 properties 元素的 url 属性中加载的属性第二被读取,它会覆盖已经存在的完全一样的属性。
//3.作为方法参数传递的属性最后被读取, 它也会覆盖任一已经存在的完全一样的属性,这些属性可能是从 properties 元素体内和资源/url 属性中加载的。
//传入方式是调用构造函数时传入,public XMLConfigBuilder(Reader reader, String environment, Properties props)
//1.XNode.getChildrenAsProperties函数方便得到孩子所有Properties
Properties defaults = context.getChildrenAsProperties();
//2.然后查找resource或者url,加入前面的Properties
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));
}
//3.Variables也全部加入Properties
Properties vars = configuration.getVariables();
if (vars != null) {
defaults.putAll(vars);
}
parser.setVariables(defaults);
configuration.setVariables(defaults);
}
}
mapperElement节点解析
//10.映射器
// 10.1使用类路径
// <mappers>
// <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
// <mapper resource="org/mybatis/builder/BlogMapper.xml"/>
// <mapper resource="org/mybatis/builder/PostMapper.xml"/>
// </mappers>
//
// 10.2使用绝对url路径
// <mappers>
// <mapper url="file:///var/mappers/AuthorMapper.xml"/>
// <mapper url="file:///var/mappers/BlogMapper.xml"/>
// <mapper url="file:///var/mappers/PostMapper.xml"/>
// </mappers>
//
// 10.3使用java类名
// <mappers>
// <mapper class="org.mybatis.builder.AuthorMapper"/>
// <mapper class="org.mybatis.builder.BlogMapper"/>
// <mapper class="org.mybatis.builder.PostMapper"/>
// </mappers>
//
// 10.4自动扫描包下所有映射器
// <mappers>
// <package name="org.mybatis.builder"/>
// </mappers>
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
//10.4自动扫描包下所有映射器
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) {
//10.1使用类路径
ErrorContext.instance().resource(resource);
InputStream inputStream = Resources.getResourceAsStream(resource);
//映射器比较复杂,调用XMLMapperBuilder
//注意在for循环里每个mapper都重新new一个XMLMapperBuilder,来解析
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
//10.2使用绝对url路径
ErrorContext.instance().resource(url);
InputStream inputStream = Resources.getUrlAsStream(url);
//映射器比较复杂,调用XMLMapperBuilder
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url == null && mapperClass != null) {
//10.3使用java类名
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.");
}
}
}
}
}
//Configuration类下
public void addMappers(String packageName) {
mapperRegistry.addMappers(packageName);
}
//查找包下所有类 MapperRegistry类下
public void addMappers(String packageName) {
addMappers(packageName, Object.class);
}
//MapperRegistry类下
public void addMappers(String packageName, Class<?> superType) {
//查找包下所有是superType的类
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
for (Class<?> mapperClass : mapperSet) {
addMapper(mapperClass); //将set里面的xxxMapper使用动态代理创建出来
}
}
//看一下如何添加一个映射
public <T> void addMapper(Class<T> type) {
//mapper必须是接口!才会添加
if (type.isInterface()) {
if (hasMapper(type)) {
//如果重复添加了,报错
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
//创建mapper的动态代理,使用JDK的动态代理实现
knownMappers.put(type, new MapperProxyFactory<T>(type));
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse(); //解析成对应的mapper.xml 文件
loadCompleted = true;
} finally {
//如果加载过程中出现异常需要再将这个mapper从mybatis中删除
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
6、调用build得到会话工厂DefaultSqlSessionFactory
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
//创建一个XMLConfigBuilder对象
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
//解析xml文件各个结点返回Configuration对象,传入build对象返回DefaultSqlSessionFactory
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
//最后一个build方法使用了一个Configuration作为参数,并返回DefaultSqlSessionFactory
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
流程图:
SqlSession创建流程
SqlSessionFactoryBuilder
使用**建造者模式**
SqlSessionFactory
使用工厂模式,单例模式
SqlSession
Mybatis核心对象
Mybatis的核心对象及其作用
存储类对象
概念:在java中(jvm)对Mybatis相关的配置信息进行封装
Configrution
mybatis-config.xml ----------------->Configuration
public class Configuration {
//环境
protected Environment environment;
//---------以下都是<settings>节点-------
protected boolean safeRowBoundsEnabled = false;
protected boolean safeResultHandlerEnabled = true;
protected boolean mapUnderscoreToCamelCase = false; //数据库表字段驼峰
protected boolean aggressiveLazyLoading = true;
protected boolean multipleResultSetsEnabled = true;
protected boolean useGeneratedKeys = false; //自增主键
protected boolean useColumnLabel = true;
//默认启用缓存
protected boolean cacheEnabled = true;
protected boolean callSettersOnNulls = false;
protected String logPrefix;
protected Class <? extends Log> logImpl;
//缓存作用域 本地缓存使用范围为SESSION 对相同SqlSession的不同调用将不会共享数据
protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
protected JdbcType jdbcTypeForNull = JdbcType.OTHER;
protected Set<String> lazyLoadTriggerMethods = new HashSet<String>(Arrays.asList(new String[] { "equals", "clone", "hashCode", "toString" }));
protected Integer defaultStatementTimeout;
//默认为简单执行器 实际上因为cacheEnabled = true 缓存开启而使用了CachingExecutor
protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;
//---------以上都是<settings>节点-------
protected Properties variables = new Properties();
//对象工厂和对象包装器工厂
protected ObjectFactory objectFactory = new DefaultObjectFactory();
protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
//映射注册机
protected MapperRegistry mapperRegistry = new MapperRegistry(this);
//默认禁用延迟加载
protected boolean lazyLoadingEnabled = false;
protected ProxyFactory proxyFactory = new JavassistProxyFactory(); // #224 Using internal Javassist instead of OGNL
protected String databaseId;
/**
* Configuration factory class.
* Used to create Configuration for loading deserialized unread properties.
*
* @see <a href='https://code.google.com/p/mybatis/issues/detail?id=300'>Issue 300</a> (google code)
*/
protected Class<?> configurationFactory;
//拦截器链 在Executor创建的时候赋值在
protected final InterceptorChain interceptorChain = new InterceptorChain();
//类型处理器注册机
protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
//类型别名注册机
protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();
//映射的语句,存在Map里 对应这各个xxxDAOMapper.xml文件,将各个Mapper信息进行了汇总
protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");
//缓存,存在Map里
protected final Map<String, Cache> caches = new StrictMap<Cache>("Caches collection");
//结果映射,存在Map里 所有的ResultMap 全都放在resultMaps中
protected final Map<String, ResultMap> resultMaps = new StrictMap<ResultMap>("Result Maps collection");
protected final Map<String, ParameterMap> parameterMaps = new StrictMap<ParameterMap>("Parameter Maps collection");
protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<KeyGenerator>("Key Generators collection");
//路径 对应xml中的mapper
protected final Set<String> loadedResources = new HashSet<String>();
protected final Map<String, XNode> sqlFragments = new StrictMap<XNode>("XML fragments parsed from previous mappers");
//不完整的SQL语句
protected final Collection<XMLStatementBuilder> incompleteStatements = new LinkedList<XMLStatementBuilder>();
protected final Collection<CacheRefResolver> incompleteCacheRefs = new LinkedList<CacheRefResolver>();
protected final Collection<ResultMapResolver> incompleteResultMaps = new LinkedList<ResultMapResolver>();
protected final Collection<MethodResolver> incompleteMethods = new LinkedList<MethodResolver>();
/*
* A map holds cache-ref relationship. The key is the namespace that
* references a cache bound to another namespace and the value is the
* namespace which the actual cache is bound to.
*/
protected final Map<String, String> cacheRefMap = new HashMap<String, String>();
public Configuration(Environment environment) {
this();
this.environment = environment;
}
public Configuration() {
//注册更多的类型别名,至于为何不直接在TypeAliasRegistry里注册,还需进一步研究
typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class); //事务处理器
typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class); //这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的数据源引用
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); //raw 图像格式
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); //默认使用xml
languageRegistry.register(RawLanguageDriver.class);
}
//创建元对象
public MetaObject newMetaObject(Object object) {
return MetaObject.forObject(object, objectFactory, objectWrapperFactory);
}
//创建参数处理器
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
//创建ParameterHandler
ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
//插件在这里插入
parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
return parameterHandler;
}
//创建结果集处理器
public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
ResultHandler resultHandler, BoundSql boundSql) {
//创建DefaultResultSetHandler(稍老一点的版本3.1是创建NestedResultSetHandler或者FastResultSetHandler)
ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
//插件在这里插入
resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
return resultSetHandler;
}
//创建语句处理器
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
//创建路由选择语句处理器
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
//插件在这里插入
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
public Executor newExecutor(Transaction transaction) {
return newExecutor(transaction, defaultExecutorType);
}
//产生执行器
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
//这句再做一下保护,囧,防止粗心大意的人将defaultExecutorType设成null?
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
//然后就是简单的3个分支,产生3种执行器BatchExecutor/ReuseExecutor/SimpleExecutor
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
//如果要求缓存,生成另一种CachingExecutor(默认就是有缓存),装饰者模式,所以默认都是返回CachingExecutor
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
//此处调用插件,通过插件可以改变Executor行为
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
************************** get()和set()方法省略**************
}
Configrution小结:
设置配置文件中的对应属性
-
设置默认执行器 (简单执行器)
protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
创建拦截器链
InterceptorChain
创建类型处理器注册机
TypeHandlerRegistry
创建类型别名注册机
TypeAliasRegistry
-
将各个mapper文件中的信息进行汇总存放
protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");
为系统内部使用的类定义别名注册(在构造函数中设置)
创建元对象
创建参数处理器
parameterHandler
创建结果集处理器
resultSetHandler
创建语句处理器
statementHandler
创建执行器
executor
MappedStatement
xxxDaoMapper.xml -----------------> MappedStatement
MappedStatement对象
对应的就是Mapper文件中的一个一个的配置
//映射的语句,存在Map里 对应这各个xxxDAOMapper.xml文件,将各个Mapper信息进行了汇总
protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");
<select id="getRole" parameterType="long" resultMap="roleMap" >
select
id,role_name as roleName,note from role where id=#{id}
</select>
mapper中的一条标签语句对应着一个MappedStatement
MappedStatment 中使用BoundSql
来封装sql语句
public final class MappedStatement {
//对应的属性
private String resource;
private Configuration configuration;
private String id; //sql的id namespace.id
private Integer fetchSize;
private Integer timeout;
private StatementType statementType;
private ResultSetType resultSetType;
//SQL源码
private SqlSource sqlSource; //解析出来的sql 对应一条sql语句
private Cache cache;
private ParameterMap parameterMap; //以废弃
private List<ResultMap> resultMaps; //对应的ResultMap
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; //数据库ID,用来区分不同环境
private Log statementLog;
private LanguageDriver lang;
private String[] resultSets;
MappedStatement() {
// constructor disabled
}
//静态内部类,建造者模式
public static class Builder {
private MappedStatement mappedStatement = new MappedStatement();
public Builder(Configuration configuration, String id, SqlSource sqlSource, SqlCommandType sqlCommandType) {
mappedStatement.configuration = configuration;
mappedStatement.id = id;
mappedStatement.sqlSource = sqlSource;
mappedStatement.statementType = StatementType.PREPARED; //默认使用PREPARED
mappedStatement.parameterMap = new ParameterMap.Builder(configuration, "defaultParameterMap", null, new ArrayList<ParameterMapping>()).build();
mappedStatement.resultMaps = new ArrayList<ResultMap>();
mappedStatement.timeout = configuration.getDefaultStatementTimeout();
mappedStatement.sqlCommandType = sqlCommandType; //select ||delete|| update ||insert || UNKNOWN
mappedStatement.keyGenerator = configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType) ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
String logId = id;
if (configuration.getLogPrefix() != null) {
logId = configuration.getLogPrefix() + id;
}
mappedStatement.statementLog = LogFactory.getLog(logId);
mappedStatement.lang = configuration.getDefaultScriptingLanuageInstance();
}
public Builder resource(String resource) {
mappedStatement.resource = resource;
return this;
}
public String id() {
return mappedStatement.id;
}
public Builder parameterMap(ParameterMap parameterMap) {
mappedStatement.parameterMap = parameterMap;
return this;
}
public Builder resultMaps(List<ResultMap> resultMaps) {
mappedStatement.resultMaps = resultMaps;
for (ResultMap resultMap : resultMaps) {
mappedStatement.hasNestedResultMaps = mappedStatement.hasNestedResultMaps || resultMap.hasNestedResultMaps();
}
return this;
}
public Builder fetchSize(Integer fetchSize) {
mappedStatement.fetchSize = fetchSize;
return this;
}
public Builder timeout(Integer timeout) {
mappedStatement.timeout = timeout;
return this;
}
public Builder statementType(StatementType statementType) {
mappedStatement.statementType = statementType;
return this;
}
public Builder resultSetType(ResultSetType resultSetType) {
mappedStatement.resultSetType = resultSetType;
return this;
}
public Builder cache(Cache cache) {
mappedStatement.cache = cache;
return this;
}
public Builder flushCacheRequired(boolean flushCacheRequired) {
mappedStatement.flushCacheRequired = flushCacheRequired;
return this;
}
public Builder useCache(boolean useCache) {
mappedStatement.useCache = useCache;
return this;
}
public Builder resultOrdered(boolean resultOrdered) {
mappedStatement.resultOrdered = resultOrdered;
return this;
}
public Builder keyGenerator(KeyGenerator keyGenerator) {
mappedStatement.keyGenerator = keyGenerator;
return this;
}
public Builder keyProperty(String keyProperty) {
mappedStatement.keyProperties = delimitedStringtoArray(keyProperty);
return this;
}
public Builder keyColumn(String keyColumn) {
mappedStatement.keyColumns = delimitedStringtoArray(keyColumn);
return this;
}
public Builder databaseId(String databaseId) {
mappedStatement.databaseId = databaseId;
return this;
}
public Builder lang(LanguageDriver driver) {
mappedStatement.lang = driver;
return this;
}
public Builder resulSets(String resultSet) {
mappedStatement.resultSets = delimitedStringtoArray(resultSet);
return this;
}
public MappedStatement build() {
assert mappedStatement.configuration != null;
assert mappedStatement.id != null;
assert mappedStatement.sqlSource != null;
assert mappedStatement.lang != null;
mappedStatement.resultMaps = Collections.unmodifiableList(mappedStatement.resultMaps);
return mappedStatement;
}
}
***************************get()和set()方法省略********************************************
}
MappedStatement 小结:
- 使用建造者模式构建
- 内部有
configuration
便于操作和使用 - 含有xml某个结点中的完整信息
操作类对象
Executor:Mybatis的执行器
主要功能:
- 增删改 update
- 查 query
- 事务 提交 回滚
-
缓存
Executor小结 :
- 调用StatementHandler来进行数据库相关的改、查操作
- 维护缓存
StatementHandler
ParameterHandler
ResultSetHandler
TypeHandler
Mybatis工作流程源码分析
将核心对象与SqlSession建立联系
对应代码:
RoleMapper roleMapper=sqlSession.getMapper(RoleMapper.class);
//sqlSession.selectList("com.wxx.mapper.RoleMapper.getRole"); 上面语句是下面语句的封装
执行流程:
1、DefaultSqlSession(sqlSession默认实现方式)调用getMapper()方法 将xxxMapper.class 和 当前进行会话的sqlsession传入进去
//DefaultSqlSession类
public <T> T getMapper(Class<T> type) {
//最后会去调用MapperRegistry.getMapper
return configuration.<T>getMapper(type, this);
}
2、调用mapper注册机的getMapper()方法
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
- 程序中的mapper都放在一个hashmap中,先查找这个hashmap中有没有key为当前类的代理工厂类。如果有则为当前mapper创建一个代理对象,并返回;如果没有则进行异常处理。
@SuppressWarnings("unchecked")
//返回代理类
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
代理实现原理分析:
对应代码:
mapperProxyFactory.newInstance(sqlSession)
- 动态代理创建具体实现类
- 具体实现类内部调用对应的sqlsession的方法
mybatis源码中的这些核心对象,在sqlsession调用对应功能时候建立联系
例如:
SqlSession.insert()
DefaultSqlSession
Exctutor
StatementHandler
MyBatis 完成代理创建核心类型 ---> DAO接口的实现类
执行顺序如下:
MapperProxy implements InvocationHandler
invoke(类加载器,方法名,参数)
sqlSession.insert
update
delete
selectOne
selectList
MapperProxyFactory
Proxy.newProxyInstrance()
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class<T> getMapperInterface() {
return mapperInterface;
}
public Map<Method, MapperMethod> getMethodCache() {
return methodCache;
}
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) { //生成xxxDAO的实现类
//用JDK自带的动态代理生成映射器
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) { //传进sqlSession和对应mapperInterface 生成代理对象 methodCache是方法缓存
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
public class MapperProxy<T> implements InvocationHandler, Serializable {
private static final long serialVersionUID = -6424540398559729838L;
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache;
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//代理以后,所有Mapper的方法调用时,都会调用这个invoke方法
//并不是任何一个方法都需要执行调用代理对象进行执行,如果这个方法是Object中通用的方法(toString、hashCode等)无需执行
if (Object.class.equals(method.getDeclaringClass())) {
try {
return method.invoke(this, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
//这里优化了,去缓存中找MapperMethod
final MapperMethod mapperMethod = cachedMapperMethod(method);
//执行
return mapperMethod.execute(sqlSession, args);
}
//去缓存中找MapperMethod
private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
//找不到才去new
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
}
invoke()
中的method.invoke(this, args);
底层对应的相应的SqlSession.insert
、update
、delete
、selectOne
、selectList
方法。
public class MapperMethod {
private final SqlCommand command; //对应的标签 是什么类型 才能具体的知道调用对应crud所对应的方法
private final MethodSignature method; //返回值 和 参数
//需求Configuration对象
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
this.command = new SqlCommand(config, mapperInterface, method);
this.method = new MethodSignature(config, method);
}
//执行
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
//可以看到执行时就是4种情况,insert|update|delete|select,分别调用SqlSession的4大类方法
if (SqlCommandType.INSERT == command.getType()) { //insert
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
} else if (SqlCommandType.UPDATE == command.getType()) { //update
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
} else if (SqlCommandType.DELETE == command.getType()) { //delete
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
} else if (SqlCommandType.SELECT == command.getType()) { //select
if (method.returnsVoid() && method.hasResultHandler()) {
//如果有结果处理器
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
//如果结果有多条记录
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
//如果结果是map
result = executeForMap(sqlSession, args);
} else {
//否则就是一条记录 selectOne
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
}
} else {
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
其他部分省略
}
//SQL命令,静态内部类
public static class SqlCommand {
private final String name; //name.id
private final SqlCommandType type; //select || update || insert || delete
public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
String statementName = mapperInterface.getName() + "." + method.getName();
MappedStatement ms = null;
if (configuration.hasStatement(statementName)) {
ms = configuration.getMappedStatement(statementName);
} else if (!mapperInterface.equals(method.getDeclaringClass().getName())) { // issue #35
//如果不是这个mapper接口的方法,再去查父类
String parentStatementName = method.getDeclaringClass().getName() + "." + method.getName();
if (configuration.hasStatement(parentStatementName)) {
ms = configuration.getMappedStatement(parentStatementName);
}
}
if (ms == null) {
throw new BindingException("Invalid bound statement (not found): " + statementName);
}
name = ms.getId();
type = ms.getSqlCommandType(); //select delete update insert
if (type == SqlCommandType.UNKNOWN) {
throw new BindingException("Unknown execution method for: " + name);
}
}
public String getName() {
return name;
}
public SqlCommandType getType() {
return type;
}
}
//方法签名,静态内部类
public static class MethodSignature { //对应sql语句中的参数
private final boolean returnsMany;
private final boolean returnsMap;
private final boolean returnsVoid;
private final Class<?> returnType;
private final String mapKey;
private final Integer resultHandlerIndex;
private final Integer rowBoundsIndex;
private final SortedMap<Integer, String> params;
private final boolean hasNamedParameters;
public MethodSignature(Configuration configuration, Method method) {
this.returnType = method.getReturnType();
this.returnsVoid = void.class.equals(this.returnType);
this.returnsMany = (configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray());
this.mapKey = getMapKey(method);
this.returnsMap = (this.mapKey != null);
this.hasNamedParameters = hasNamedParams(method);
//以下重复循环2遍调用getUniqueParamIndex,是不是降低效率了
//记下RowBounds是第几个参数
this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
//记下ResultHandler是第几个参数
this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
this.params = Collections.unmodifiableSortedMap(getParams(method, this.hasNamedParameters));
}
StatementHandler执行流程:
时序图(图片来自于http://www.coderead.cn/)
核心对象总结
configuration
默认初始化时,默认构造函数为各种类型起别名:
1. 事务处理器
2. 数据源类型
3. 缓存策略
4. 图像格式
5. 日志类型
6. 动态代理类型
7. 默认驱动类(xml方式)
其他配置:
- 默认使用简单执行器
ExecutorType.SIMPLE
- 缓存作用域在session范围内
LocalCacheScope.SESSION
- 创建拦截器链
- 创建类型处理器注册机
- 创建类型别名注册机
mappedStatement
用来表示xml中的一个sql,例如:
<select id="findRole" parameterType="long" resultMap="roleMap">
select
id,role_name,note from role where role_name like CONCAT('%',#{roleName
javaType=string,
jdbcType=VARCHAR,typeHandler=com.wxx.handler.MyStringHandler},'%')
</select>
用来描述<select|update|insert|delete>或者@Select、@Update等注解配置的SQL信息。
sqlsession
使用门面模式,只设定api,具体参数让executor来执行
executor
简单执行器(SimpleExecutor),可实现sqlsession中的全部功能 一次一次的执行
重用执行器(ReuseExecutor),先查缓存中时候有相应的sql语句,若有,则不再进行sql的预编译
批处理执行器(BatchExecutor),一次插入一批数据,只对改操作有效,对查询操作和简单执行器一样
四大对象
StatementHandler
ParameterHandler
用来处理StatementHandler
中的参数
ResultSetHandler
用来处理StatementHandler
中的结果集
TypeHandler
用来帮助ParameterHandler
和ResultSetHandler
处理javaType和数据库类型的相互转换
缓存
缓存逻辑图
一级缓存
命中场景(会话级别缓存)
运行时参数相关:
- sql语句 和参数相同
-
statement
的id
需要相同 -
sqlsession
也必须相同 -
RowBounds
返回行范围必须相同
操作与配置相关:
①②③ 都会调用clearLocalCache()来对缓存进行清空
- 未手动清空 (提交 回滚)
- 未调用
flushCache == true
的查询 - 未执行update操作
update
操作会清空全部缓存 - 缓存作用域 不是
STATEMENT
spring整合mybatis 以及缓存失效原因
使用拦截器(动态代理)来控制方法的调用,最终达到实现事务的效果
二级缓存
作用范围是整个应用,而且可以跨线程调用
使用流程:
使用场景
运行时参数相关:
- 会话提交之后
- sql语句 和参数相同
- statement的id需要相同
- RowBounds 返回行范围必须相同
为何提交之后才能命中缓存
解决方案:
为每个会话创建一个事务缓存管理器(transactionCaches),每次访问一个新的xxxMapper都会创建一个暂存区,对应着相应的缓存区
Mybatis中使用的设计模式
单例模式
configuration 使用单例 保证整个程序运行期间都可使用,且不重复创建,放置浪费资源
构造器模式
在Mybatis的初始化的主要工作是加载并解析mybatis-config.xml的配置文件、映射配置文件以及相关的注解信息。因为使用了建造者模式,BashBuilder抽象类即为建造者接口的角色
public abstract class BaseBuilder {
//需要配置,类型别名注册,类型处理器注册3个东西
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();
}
………………
}
适配器模式
(Excutor 等核心对象,使用缺省适配器模式) 使用extends
关键字来继承,只用实现想实现的功能
interface Test{
void eat();
void drink();
void walk();
}
abstract class abstractClass implements Test{
public void eat() {
// TODO Auto-generated method stub
}
@Override
public void drink() {
// TODO Auto-generated method stub
}
@Override
public void walk() {
// TODO Auto-generated method stub
}
}
class eatClass extends abstractClass{
@Override
public void eat() {
// TODO Auto-generated method stub
super.eat();
}
}
class drinkClass extends abstractClass{
@Override
public void eat() {
// TODO Auto-generated method stub
super.eat();
}
}
代理模式(动态代理)
RoleMapper roleMapper=sqlSession.getMapper(RoleMapper.class); 实现类使用动态字节码技术来创建,在jvm中运行时创建
等价于
Proxy.newProxyInstance(Test.class.getClassLoader(),new Class[]{RoleMapper.class},
new MyMapperProxy(sqlSession,RoleMapper.class));
工厂模式
使用mapper工厂来创建mapper
MapperProxyFactory 和 MapperProxy
protected T newInstance(MapperProxy<T> mapperProxy) {
//用JDK自带的动态代理生成映射器
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
门面模式
sqlsession使用门面模式,只定义api 具体的使用让Executor来进行实现
装饰器模式
在不改变原有类结构和继承的情况下,通过包装原对象去扩展一个新功能
Caching Executor 实现二级缓存,而其他的操作,如同获取连接等操作BaseExecutor已经做了,直接使用BaseExecutor
责任链模式
二级缓存中的责任链模式