基本思路: 從SqlSessionFactory的初始化出發,觀察資源的準備和環境的準備,以及實現持久層的一些過程;spring
進入SqlSessionFactoryBean類,發現先執行的是sql
而後是:session
在初始化類以後,作的準備工做以下:mybatis
public void afterPropertiesSet() throws Exception {
notNull(dataSource, "Property 'dataSource' is required");//1,檢查spring準備的datasource是否ok
notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");//2,檢查空構造方法的sqlSessionFactoryBuilder是否準備好appthis.sqlSessionFactory = buildSqlSessionFactory();//3,利用配置的屬性,構造sqlSessionFactory
}ide構造細節以下:方法有點長,注意註釋這個是流程,以後我畫一個圖來加深理解;ui
protected SqlSessionFactory buildSqlSessionFactory() throws IOException {this
Configuration configuration;spa
XMLConfigBuilder xmlConfigBuilder = null;
if (this.configLocation != null) {//1,檢查是否有配置configLocation,即mybatis的總體配置文件,非mapper文件,若是有加載進去,沒有,構造一個空的
xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
configuration = xmlConfigBuilder.getConfiguration();
} else {
if (logger.isDebugEnabled()) {
logger.debug("Property 'configLocation' not specified, using default MyBatis Configuration");
}
configuration = new Configuration();
configuration.setVariables(this.configurationProperties);
}插件if (this.objectFactory != null) {//2,檢查對象工廠,若是有設置進去,沒有留空
configuration.setObjectFactory(this.objectFactory);
}if (this.objectWrapperFactory != null) {//3,檢查對象裝飾工廠,若是有設置進去,沒有留空
configuration.setObjectWrapperFactory(this.objectWrapperFactory);
}if (hasLength(this.typeAliasesPackage)) {//4,檢查包的簡稱,若是有註冊進去
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");
}
}
}if (!isEmpty(this.typeAliases)) {//5,檢查類的簡稱,若是有註冊進去
for (Class<?> typeAlias : this.typeAliases) {
configuration.getTypeAliasRegistry().registerAlias(typeAlias);
if (logger.isDebugEnabled()) {
logger.debug("Registered type alias: '" + typeAlias + "'");
}
}
}if (!isEmpty(this.plugins)) {//6,檢查插件,若是有註冊進去
for (Interceptor plugin : this.plugins) {
configuration.addInterceptor(plugin);
if (logger.isDebugEnabled()) {
logger.debug("Registered plugin: '" + plugin + "'");
}
}
}if (hasLength(this.typeHandlersPackage)) {//7,檢查類型轉換類包,若是有註冊進去
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");
}
}
}if (!isEmpty(this.typeHandlers)) {//8,檢查類型轉換類,若是有註冊進去
for (TypeHandler<?> typeHandler : this.typeHandlers) {
configuration.getTypeHandlerRegistry().register(typeHandler);
if (logger.isDebugEnabled()) {
logger.debug("Registered type handler: '" + typeHandler + "'");
}
}
}if (xmlConfigBuilder != null) {//9,檢查是否有xml的構造器,若是有,直接使用構造器構造
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();
}
}if (this.transactionFactory == null) {//10,檢查事物工廠,若是沒有,構造一個spring管理的事物工廠
this.transactionFactory = new SpringManagedTransactionFactory();
}//11,構造環境變量
Environment environment = new Environment(this.environment, this.transactionFactory, this.dataSource);
configuration.setEnvironment(environment);if (this.databaseIdProvider != null) {
try {//12,檢查是否配置了db id,若是有,設置到配置的類中
configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
} catch (SQLException e) {
throw new NestedIOException("Failed getting a databaseId", e);
}
}if (!isEmpty(this.mapperLocations)) {//13,把配置的mapper弄進來總配置文件裏
for (Resource mapperLocation : this.mapperLocations) {
if (mapperLocation == null) {
continue;
}try {
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
configuration, mapperLocation.toString(), configuration.getSqlFragments());
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);//14,最後經過獲得的配置類來構造一個sqlsessionFactory工廠
}
設置好屬性以後,執行
獲得factory構造的bean,即sqlSessionFactory,
最後容器啓動成功的事件監控
public void onApplicationEvent(ApplicationEvent event) {
if (failFast && event instanceof ContextRefreshedEvent) {
// fail-fast -> check all statements are completed
this.sqlSessionFactory.getConfiguration().getMappedStatementNames();
}
}以上是資源的準備,下面來一次增刪改查的跟蹤,觀察內部的工做原理;
查詢單條記錄的過程:
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);//1,經過轉換,獲得存儲的mapperedStatement
List<E> result = executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);//2,轉換成jdbc代碼執行
return result;
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}內部就像是一臺精密的儀器,去除了大量的模版jdbc代碼;