在使用mybatis的時候咱們通常設置xml,而後利用beanfactory建立bean進行加載,那麼裏面詳細過程如何:mysql
1、初始化配置:spring
在org.apache.ibatis.session這個包下面有一個Configuration類,這個是mybatis的配置類sql
他的配置數據庫
Configuration配置apache
properties:屬性session
settings:設置mybatis
typeAliases:別名app
typeHandlers:類型處理器dom
objectFactory:對象工廠ide
pulgins:插件
environments:環境
environment:環境變量
transactionManager:事務管理器
dataSource:數據源
mybatis能夠依賴於spring生成SqlSessionFactory也能夠依賴mybatis自身的SqlSessionFactoryBuilder()去獲取SqlSessionFactory我項目中採用的是spring集成mybatis,下面就簡單的記錄如下spring如何從加載到進行查詢結束。
這是我spring的配置:
<bean id="parentDataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="maxActive" value="30" />
<property name="initialSize" value="2" />
<property name="maxWait" value="30000" />
<property name="maxIdle" value="30" />
<property name="minIdle" value="1" />
<property name="testOnBorrow" value="false"></property>
<property name="testWhileIdle" value="true"></property>
<property name="validationQuery" value="select 1"></property>
<property name="timeBetweenEvictionRunsMillis"><value>30000</value></property>
<property name="numTestsPerEvictionRun"><value>10</value></property>
<property name="minEvictableIdleTimeMillis" value="30000"></property>
</bean>
<bean id="dataSource" parent="parentDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="${cms.url}" />
<property name="username" value="${cms.username}" />
<property name="password" value="${cms.password}" />
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation" value="classpath:mybatis_configuration.xml" />
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
下面是mybatis的設置:
<configuration>
<!-- 別名 -->
<typeAliases>
<typeAlias type="cn.cover.server.domain.Advert" alias="Advert" />
<typeAlias type="cn.cover.server.domain.AppVersion" alias="AppVersion" />
<typeAlias type="cn.cover.server.domain.Live" alias="Live" />
<typeAlias type="cn.cover.server.domain.DailyDate" alias="DailyDate" />
</typeAliases>
<mappers>
<mapper resource="sqlmap/Advert.xml" />
<mapper resource="sqlmap/AppVersion.xml" />
<mapper resource="sqlmap/Live.xml" />
<mapper resource="sqlmap/Daily.xml" />
</mappers>
</configuration>
配置採用到注入到SqlSessionFactoryBean中,SqlSessionFactoryBean
在看一下SqlSessionFactoryBean裏面的buildSqlSessionFactory()方法,其中...表示當前方法不執行
protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
Configuration configuration; //Configuration對象
//用來解析xml
XMLConfigBuilder xmlConfigBuilder = null;
//我配置的config不爲空,因此用這個
if (this.configLocation != null) {
//經過location獲取input流解析xml文件
xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
//經過xml文件生成對應的configuration對象
configuration = xmlConfigBuilder.getConfiguration();
} else {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Property 'configLocation' not specified, using default MyBatis Configuration");
}
configuration = new Configuration();
configuration.setVariables(this.configurationProperties);
}
.....
if (xmlConfigBuilder != null) {//上面配置xmlConfigBuilder不未空
try {
//針對mybatis配置,獲取/configuration節點下面的數據
xmlConfigBuilder.parse();
if (this.logger.isDebugEnabled()) {
this.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();
}
}
//針對事務的處理,若是當前事務工廠沒有就new SpringManagedTransactionFactory
if (this.transactionFactory == null) {
this.transactionFactory = new SpringManagedTransactionFactory();
}
//而後設置configuration對象中的environment
Environment environment = new Environment(this.environment, this.transactionFactory, this.dataSource);
configuration.setEnvironment(environment);
if (this.databaseIdProvider != null) {
..
}
if (!isEmpty(this.mapperLocations)) {//針對mapper文件進行解析xml文件
...
}
return this.sqlSessionFactoryBuilder.build(configuration);
}
下面咱們分別看一下每一個方法的執行
xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
會調用org.apache.ibatis.builder.xml包下面的XMLConfigBuilder類下面方法
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}
關於XPathParser方法就不惜看了,看一下this的,this調用下面方法
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
/***
**調用父類的構造方法,傳入參數調用new的Configuration對象
** 在Configuration對象中new的時候會在構造方法中
**typeAliasRegistry.registerAlias("xxxx", xxxx.class);註冊不少默認的別名
**/
super(new Configuration());
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props);
this.parsed = false;
this.environment = environment;
this.parser = parser;
}設置變量
而後是執行configuration = xmlConfigBuilder.getConfiguration();
XMLConfigBuilder類沒有實現這個方法,會調用父類的getConfiguration()方法,獲取剛纔設置的configuration對象
而後在去執行
xmlConfigBuilder.parse();
能夠看一下里面的具體實現
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
//獲取/configuration節點下面的數據
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
下面看一下 parseConfiguration(parser.evalNode("/configuration"));
他的傳入參數是一個XNode對,XNode封裝了節點的一些列屬性
private void parseConfiguration(XNode root) {
try {
//獲取properties文件進行設置數據庫的配置,該處沒有用,由於採用了spring配置
propertiesElement(root.evalNode("properties")); //issue #117 read properties first
//讀取mybatis_config.xml裏面的<typeAliases>標籤下面的數據
typeAliasesElement(root.evalNode("typeAliases"));
//讀取mybatis_config.xml裏面的<plugins>標籤下面的數據
pluginElement(root.evalNode("plugins"));
//讀取mybatis_config.xml裏面的<objectFactory>標籤下面的數據
objectFactoryElement(root.evalNode("objectFactory"));
//讀取mybatis_config.xml裏面的<objectWrapperFactory>標籤下面的數據
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
//讀取mybatis_config.xml裏面的<settings>標籤下面的數據
settingsElement(root.evalNode("settings"));
//讀取mybatis_config.xml裏面的<environments>標籤下面的數據
environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
//讀取mybatis_config.xml裏面的<mappers>標籤下面的數據
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
由於mybatis把sql寫在了xml文件裏面,主要看一下 mapperElement(root.evalNode("mappers"));
下面是這個方法的實現,經過獲取xml裏面的resource標籤或者url,class標籤進行設置
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {//若是mappers下面的字標籤名字爲package採用這種,每用過
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
} else {//我採用的是resource配置,能夠看出來能夠獲取resource這個屬性的值
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
if (resource != null && url == null && mapperClass == null) {//符合這個條件
ErrorContext.instance().resource(resource);
InputStream inputStream = Resources.getResourceAsStream(resource);//讀取配置
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
}}...
}
}
看一下XMLMapperBuilder
public XMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
this(new XPathParser(inputStream, true, configuration.getVariables(), new XMLMapperEntityResolver()),
configuration, resource, sqlFragments);
}
改方法會調用這個方法
private XMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
super(configuration);
this.builderAssistant = new MapperBuilderAssistant(configuration, resource);
this.parser = parser;
this.sqlFragments = sqlFragments;
this.resource = resource;
}相似於XMLBuilder裏面的配置
在看一下 mapperParser.parse();這個方法
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
bindMapperForNamespace();
}
parsePendingResultMaps();
parsePendingChacheRefs();
parsePendingStatements();
}
能夠看出來就是去獲取mapper節點數據下面的數據,看一下
private void configurationElement(XNode context) {
try {
String namespace = context.getStringAttribute("namespace");
if (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. Cause: " + e, e);
}
}
更直觀,分別獲取mapper節點下面的parameterMap,resultMap,sql還有select等其餘操做
mybatis在初始化的時候在解析sql的時候會把全部sql解析成MappedStatement對象,存儲在內存中,具體存在Configuration 對象的mappedStatements中,key是在mybatis_config.xml裏面設置的別名+id
總之mybatis在初始化的時候不管採用mybatis的配置仍是使用spring計策或嗯mybatis都是把全部配置信息初始化到內存中,包括事務配置,sql配置,存儲在configuration對象中