Mybatis的基本构成
核心组件
- SqlSessionFactoryBuilder(构造器):它会根据配置信息或者代码来生成SqlSessionFactory(工厂接口);
- SqlSessionFactory:依靠工厂来产生SqlSession会话;
- SqlSession:是一个既可以发送SQL去执行并返回结果,也可以获取Mapper的接口;
- SQL Mapper:是一个由Java和XML文件或注解构成的需要给出对应的SQL和映射规则。
看源码
正确方式:宏观>微观>画图
宏观:了解本质,了解这个东西是用来干嘛的;
微观:
以查询一条记录为线索;
public class SelectMapper {
static SqlSessionFactory factory;
static {
try {
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
factory = new SqlSessionFactoryBuilder().build(in);
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void select(){
SqlSession sqlSession = factory.openSession();
Student stu = sqlSession.selectOne("mapper.StudentMapper.select", 1);
sqlSession.close();
System.out.println(stu);
}
}
mybatis初始化的过程,如何获取SqlSessionFactory对象?
- 调用SqlSessionFactoryBuilder的build方法,会根据xml配置文件的输入流inputStream等信息创建XMLconfigBuilder对象;
- SqlSessionFactoryBuilder调用XMLConfiguration对象的parse方法,这个方法会返回一个Configuration对象;
- SqlSessionFactoryBuilder根据Configuration对象创建一个DefaultSessionFactory对象;
- 将这个对象返回供Client使用。
/* SqlSessionFactoryBuilder*/
public SqlSessionFactory build(InputStream inputStream) {
return this.build((InputStream)inputStream, (String)null, (Properties)null);
}
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
SqlSessionFactory var5;
try {
//1.创建XMLConfigBuilder对象用来解析XML配置文件,生成Configuration对象
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
//2. parser.parse()将XML配置文件信息解析成Java的Configuration对象
//3. 根据Configuration对象创建出SqlSessionFactory对象
var5 = this.build(parser.parse());
} catch (Exception var14) {
throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException var13) {
;
}
}
return var5;
}
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
参考文章:https://blog.youkuaiyun.com/u010349169/article/details/37744073
如何将mybatis-config.xml内容映射为Java中的Configuration对象
当SqlSessionFactoryBuilder执行build方法,调用XMLConfigBuilder的parse方法,然后返回Configuration对象。那么parse方法是如何处理XML文件,生成Configuration对象的?
/*SqlSessionFactoryBuilder*/
public SqlSessionFactory build(InputStream inputStream) {
return this.build((InputStream)inputStream, (String)null, (Properties)null);
}
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
SqlSessionFactory var5;
try {
//建造xmlConfiguration对象(1)
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
//将XML配置文件的信息解析成Java的Configuration对象
//this.build根据Configuration对象创建出DefaultSqlSessionFactory对象
var5 = this.build(parser.parse());
} catch (Exception var14) {
throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException var13) {
;
}
}
return var5;
}
- XMLConfigBuilder会将XML配置文件的信息转换为Document对象,而XML配置定义文件DTD转换成XMLMapperEntityResolver对象,然后将二者封装到XpathParser对象中,XpathParser的作用是提供根据Xpath表达式获取基本DOM节点Node信息的操作。
(1)/*XMLConfigBuilder*/
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}
/*XPathParser*/
public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
this.commonConstructor(validation, variables, entityResolver);
this.document = this.createDocument(new InputSource(inputStream));
}
- 之后XMLConfigBuilder调用parse方法,会从XPathParser中取出
<configuration>
节点对应的Node对象,然后解析Node节点的子节点:包括properties, settings, typeAliases,typeHandlers, objectFactory, objectWrapperFactory, plugins, environments,databaseIdProvider, mappers。
/*XMLConfigBuilder*/
public Configuration parse() {
if (this.parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
} else {
this.parsed = true;
this.parseConfiguration(this.parser.evalNode("/configuration"));
return this.configuration;
}
}
/*
解析"/configuration"节点下的子节点信息,然后将解析的结果设置到Configuration对象中
*/
private void parseConfiguration(XNode root) {
try {
this.propertiesElement(root.evalNode("properties"));
Properties settings = this.settingsAsProperties(root.evalNode("settings"));
this.loadCustomVfs(settings);
this.typeAliasesElement(root.evalNode("typeAliases"));
this.pluginElement(root.evalNode("plugins"));
this.objectFactoryElement(root.evalNode("objectFactory"));
this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
this.reflectorFactoryElement(root.evalNode("reflectorFactory"));
this.settingsElement(settings);
this.environmentsElement(root.evalNode("environments"));
this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
this.typeHandlerElement(root.evalNode("typeHandlers"));
this.mapperElement(root.evalNode("mappers"));
} catch (Exception var3) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
}
}
- 然后将这些值解析出来设置到Configuration对象中,以Environments为例:
Configuration类包含的属性:
public class Configuration {
protected Environment environment;
protected boolean safeRowBoundsEnabled;
protected boolean safeResultHandlerEnabled;
protected boolean mapUnderscoreToCamelCase;
protected boolean aggressiveLazyLoading;
protected boolean multipleResultSetsEnabled;
protected boolean useGeneratedKeys;
protected boolean useColumnLabel;
protected boolean cacheEnabled;
protected boolean callSettersOnNulls;
...
protected final Collection<ResultMapResolver> incompleteResultMaps;
protected final Collection<MethodResolver> incompleteMethods;
protected final Map<String, String> cacheRefMap;
- Environment
其中TransactionFactory由ManagedTransactionFactory实现对应xml标签中的transactionManager;
/*XMLConfigBuilder*/
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
if (this.environment == null) {
this.environment = context.getStringAttribute("default");
}
Iterator var2 = context.getChildren().iterator();
while(var2.hasNext()) {
XNode child = (XNode)var2.next();
String id = child.getStringAttribute("id");
if (this.isSpecifiedEnvironment(id)) {
//1. 创建事务工厂
TransactionFactory txFactory = this.transactionManagerElement(child.evalNode("transactionManager"));
//2. 创建数据源
DataSourceFactory dsFactory = this.dataSourceElement(child.evalNode("dataSource"));
DataSource dataSource = dsFactory.getDataSource();
//3. 构造Environment对象
Builder environmentBuilder = (new Builder(id)).transactionFactory(txFactory).dataSource(dataSource);
//4. 将创建Environment对象设置到Configuration对象中。
this.configuration.setEnvironment(environmentBuilder.build());
}
}
}
}
/*Environment*/
/*
注意:创建environment时,如果SqlSessionFactoryBuilder制定了额特定环境;则返回指定数据眼的Environment对象,否则返回默认的Environment对象;这种方式实现了Mybatis可以连接多数据源。
*/
public Environment build() {
return new Environment(this.id, this.transactionFactory, this.dataSource);
}
- 返回Configuration对象
如何获取sql语句
- 通过调用SqlSessionFactoryBuilder的build方法的,返回DefaultSqlSessionFactory对象;
- 将解析好的configuration对象传入DefaultSqlSessionFactory构造器中,创建一个DefaultSqlSessionFactory对象。
- 再通过调用DefaultSqlSessionFactory的openSession方法返回一个DefaultSqlSession对象。
- SqlSession接口里面包含了jdbc里面的增删改查方法,其实就是底层封装了jdbc,通过jdbc来操作数据库。
/*SqlSessionFactoryBuilder*/
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
SqlSessionFactory var5;
try {
//建造xmlConfiguration对象(1)
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
//将XML配置文件的信息解析成Java的Configuration对象
//this.build根据Configuration对象创建出DefaultSqlSessionFactory对象
var5 = this.build(parser.parse());
} catch (Exception var14) {
throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException var13) {
;
}
}
return var5;
}
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
/*DefaultSqlSessionFactory*/
public SqlSession openSession() {
//第一个参数为配置中的指定的执行器类型包含 SIMPLE,REUSE,BATCH;
//第二个参数为配置中事务的隔离级别:NONE(0),
// READ_COMMITTED(2),
// READ_UNCOMMITTED(1),
// REPEATABLE_READ(4),
// SERIALIZABLE(8);
//第三个参数表示是否自动提交事务
return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, false);
}
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
DefaultSqlSession var8;
try {
Environment environment = this.configuration.getEnvironment();
//从environment对象中获取事务管理对象,如果没有则创建一个(1);
TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
//创建一个事务实例
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//根据执行器类型个事务对象创建一个执行器(2)
Executor executor = this.configuration.newExecutor(tx, execType);
//创建DefaultSqlSession对象,并返回
var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
} catch (Exception var12) {
this.closeTransaction(tx);
throw ExceptionFactory.wrapException("Error opening session. Cause: " + var12, var12);
} finally {
ErrorContext.instance().reset();
}
return var8;
}
(1)获取事务工厂
//如果environment不为空并且environment配置中指定了事务工厂TransactionFactory则返回它,否则创建一个事务工厂管理对象。
private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
return (TransactionFactory)(environment != null && environment.getTransactionFactory() != null ? environment.getTransactionFactory() : new ManagedTransactionFactory());
}
(2)创建执行器的方法
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
//如果没有指定创建器类型,则默认使用SIMPLE类型
executorType = executorType == null ? this.defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Object executor;
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);
}
if (this.cacheEnabled) {
executor = new CachingExecutor((Executor)executor);
}
Executor executor = (Executor)this.interceptorChain.pluginAll(executor);
return executor;
}
拿到sql如何去执行?
@Test
public void select(){
SqlSession sqlSession = factory.openSession();
Student stu = sqlSession.selectOne("mapper.StudentMapper.select", 1);
sqlSession.close();
System.out.println(stu);
}
来看看Student stu = sqlSession.selectOne("mapper.StudentMapper.select", 1);
都做了什么事。
- 将获取到的MappedStatement对象传入query方法中。在BaseExecutor类中:
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
List var5;
try {
MappedStatement ms = this.configuration.getMappedStatement(statement);
(1)
var5 = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception var9) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + var9, var9);
} finally {
ErrorContext.instance().reset();
}
return var5;
}
- select方法调用configuration的getMappedStatement方法传入sql语句statement返回这个语句节点对象;
- MappedStatement中包含BoundSql对象
MappedStatement维护了一条<select|update|delete|insert>节点的封装;获取的方式为命令空间+id;
SqlSource 负责根据用户传递的parameterObject动态的生成SQL语句,将信息封装到BoundSql对象中,并返回;
BoundSql表示动态生成的SQL语句以及相应的参数信息。有了BoundSql就可以执行sql语句了;
(1)
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
//获取BoundSql对象
(2)
BoundSql boundSql = ms.getBoundSql(parameter);
//创建一级缓存,再调用该对象的重载方法query
CacheKey key = this.createCacheKey(ms, parameter, rowBounds, boundSql);
return this.query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
/*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);
}
public class DynamicSqlSource implements SqlSource {
private final Configuration configuration;
private final SqlNode rootSqlNode;
/*DynamicSqlSource*/
public DynamicSqlSource(Configuration configuration, SqlNode rootSqlNode) {
this.configuration = configuration;
this.rootSqlNode = rootSqlNode;
}
//根据配置和参数对象动态sql生成
public BoundSql getBoundSql(Object parameterObject) {
DynamicContext context = new DynamicContext(this.configuration, parameterObject);
//读取sql结点对象(1),动态组建完整的sql语句
this.rootSqlNode.apply(context);
SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(this.configuration);
Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
//context.getSql()获取sql语句是完整的sql语句的原始形式包括#{}等
//将sql语句解析为staticSqlSource(2)
SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
//用staticSqlSource构建boundSql
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
Iterator var7 = context.getBindings().entrySet().iterator();
//添加metaParameter
while(var7.hasNext()) {
Entry<String, Object> entry = (Entry)var7.next();
boundSql.setAdditionalParameter((String)entry.getKey(), entry.getValue());
}
return boundSql;
}
}
/*SqlSourceBuilder*/
public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
SqlSourceBuilder.ParameterMappingTokenHandler handler = new SqlSourceBuilder.ParameterMappingTokenHandler(this.configuration, parameterType, additionalParameters);
//解析#{}形式的参数
GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
//解析后的sql为用"?"替换#{}参数,且提取对应参数的ParameterMapping
String sql = parser.parse(originalSql);
//以StaticSqlSource为解析结果
return new StaticSqlSource(this.configuration, sql, handler.getParameterMappings());
}
动态sql结点对象,可以看到常用的<foreach>、<set>
等标签;
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
//如果连接关闭则抛出异常
if (closed) {
throw new ExecutorException("Executor was closed.");
}
//如果查询栈中记录为0或者刷新了缓存则清空缓存;
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
...
//先从缓存中获取,如果缓存中没有则冲数据库中获取
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
//处理本地缓存的输出参数
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
//从数据库中查询
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
private void handleLocallyCachedOutputParameters(MappedStatement ms, CacheKey key, Object parameter, BoundSql boundSql) {
if (ms.getStatementType() == StatementType.CALLABLE) {
Object cachedParameter = this.localOutputParameterCache.getObject(key);
if (cachedParameter != null && parameter != null) {
MetaObject metaCachedParameter = this.configuration.newMetaObject(cachedParameter);
MetaObject metaParameter = this.configuration.newMetaObject(parameter);
Iterator var8 = boundSql.getParameterMappings().iterator();
while(var8.hasNext()) {
ParameterMapping parameterMapping = (ParameterMapping)var8.next();
if (parameterMapping.getMode() != ParameterMode.IN) {
String parameterName = parameterMapping.getProperty();
Object cachedValue = metaCachedParameter.getValue(parameterName);
metaParameter.setValue(parameterName, cachedValue);
}
}
}
}
}
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
this.localCache.putObject(key, ExecutionPlaceholder.EXECUTION_PLACEHOLDER);
List list;
try {
//执行查询
list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
//清空旧的缓存
this.localCache.removeObject(key);
}
//创建新的缓存
this.localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
this.localOutputParameterCache.putObject(key, parameter);
}
//返回查询结果的List集合
return list;
}