mapper层-浅谈mybatis-starter

org.mybatis.spring.boot:mybatis-spring-boot-starter
version 2.1.4
主要技术点 1: SpringBoot starter 2: jdbc原生态使用 3: AOP

一. Mybatis自动装配

  1. mybatis的spring.factories
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
  1. MybatisAutoConfiguration
// @Bean效果
@org.springframework.context.annotation.Configuration
// classPath存在 SqlSessionFactory和SqlSessionFactoryBean时加载
@ConditionalOnClass({
    
     SqlSessionFactory.class, SqlSessionFactoryBean.class })
// 存在且唯一候选DataSource时加载
@ConditionalOnSingleCandidate(DataSource.class)
// 加载MybatisPeoperties配置文件收集信息
@EnableConfigurationProperties(MybatisProperties.class)
// DataSourceAutoConfiguration实例化之后加载MybatisLanguageDriverAutoConfiguration类
@AutoConfigureAfter({
    
     DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class })
public class MybatisAutoConfiguration implements InitializingBean {
    
    

  private static final Logger logger = LoggerFactory.getLogger(MybatisAutoConfiguration.class);

  ......
  // 初始化数据
  public MybatisAutoConfiguration(MybatisProperties properties, ObjectProvider<Interceptor[]> interceptorsProvider,
      ObjectProvider<TypeHandler[]> typeHandlersProvider, ObjectProvider<LanguageDriver[]> languageDriversProvider,
      ResourceLoader resourceLoader, ObjectProvider<DatabaseIdProvider> databaseIdProvider,
      ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider) {
    
    
    this.properties = properties;
    this.interceptors = interceptorsProvider.getIfAvailable();
    this.typeHandlers = typeHandlersProvider.getIfAvailable();
    this.languageDrivers = languageDriversProvider.getIfAvailable();
    this.resourceLoader = resourceLoader;
    this.databaseIdProvider = databaseIdProvider.getIfAvailable();
    this.configurationCustomizers = configurationCustomizersProvider.getIfAvailable();
  }

  // 实现InitializingBean 重写afterPropertiesSet
  @Override
  public void afterPropertiesSet() {
    
    
    checkConfigFileExists();
  }

  private void checkConfigFileExists() {
    
    
    if (this.properties.isCheckConfigLocation() && StringUtils.hasText(this.properties.getConfigLocation())) {
    
    
      Resource resource = this.resourceLoader.getResource(this.properties.getConfigLocation());
      Assert.state(resource.exists(),
          "Cannot find config location: " + resource + " (please add config file or check your Mybatis configuration)");
    }
  }

  // 如果不存在SqlSessionFactory时 初始化SqlSessionFactory
  @Bean
  @ConditionalOnMissingBean
  public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
    
    
    SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
    factory.setDataSource(dataSource);
    factory.setVfs(SpringBootVFS.class);
    
    ......

    return factory.getObject();
  }

  private void applyConfiguration(SqlSessionFactoryBean factory) {
    
    
    Configuration configuration = this.properties.getConfiguration();
    if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) {
    
    
      configuration = new Configuration();
    }
    // 扩展-更改configuration属性
    if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) {
    
    
      for (ConfigurationCustomizer customizer : this.configurationCustomizers) {
    
    
        customizer.customize(configuration);
      }
    }
    factory.setConfiguration(configuration);
  }

  // 如果不存在SqlSessionTemplate 初始化SqlSessionTemplate
  @Bean
  @ConditionalOnMissingBean
  public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
    
    
  	// ExecutorType -> SIMPLE, REUSE, BATCH
    ExecutorType executorType = this.properties.getExecutorType();
    if (executorType != null) {
    
    
      return new SqlSessionTemplate(sqlSessionFactory, executorType);
    } else {
    
    
      return new SqlSessionTemplate(sqlSessionFactory);
    }
  }

  // 扫描接口 @Mapper
  public static class AutoConfiguredMapperScannerRegistrar implements BeanFactoryAware, ImportBeanDefinitionRegistrar {
    
    

    private BeanFactory beanFactory;

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    
    

	  // 注册BeanDefinitions
      ......
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) {
    
    
      this.beanFactory = beanFactory;
    }

  }

  // 如果不存在@MapperScan(postProcessBeanDefinitionRegistry) 那么去找@Mapper
  @org.springframework.context.annotation.Configuration
  // 加载AutoConfiguredMapperScannerRegistrar的Bean
  @Import(AutoConfiguredMapperScannerRegistrar.class)
  @ConditionalOnMissingBean({
    
     MapperFactoryBean.class, MapperScannerConfigurer.class })
  public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean {
    
    

    @Override
    public void afterPropertiesSet() {
    
    
      logger.debug(
          "Not found configuration for registering mapper bean using @MapperScan, MapperFactoryBean and MapperScannerConfigurer.");
    }

  }

}

二. 如何加载mapper

Class MapperScannerConfigurer

@Override
 public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    
    
   if (this.processPropertyPlaceHolders) {
    
    
     processPropertyPlaceHolders();
   }

   ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
   scanner.setAddToConfig(this.addToConfig);
   scanner.setAnnotationClass(this.annotationClass);
   scanner.setMarkerInterface(this.markerInterface);
   scanner.setSqlSessionFactory(this.sqlSessionFactory);
   scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
   scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
   scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
   scanner.setResourceLoader(this.applicationContext);
   scanner.setBeanNameGenerator(this.nameGenerator);
   scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
   if (StringUtils.hasText(lazyInitialization)) {
    
    
     scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));
   }
   if (StringUtils.hasText(defaultScope)) {
    
    
     scanner.setDefaultScope(defaultScope);
   }
   scanner.registerFilters();
   // 实现如下
   scanner.scan(
       StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
 }

scanner.scan -> doScan -> registerAnnotationConfigProcessors

private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
    
    
    AbstractBeanDefinition definition;
   BeanDefinitionRegistry registry = getRegistry();
   for (BeanDefinitionHolder holder : beanDefinitions) {
    
    
     definition = (AbstractBeanDefinition) holder.getBeanDefinition();
     boolean scopedProxy = false;
     if (ScopedProxyFactoryBean.class.getName().equals(definition.getBeanClassName())) {
    
    
       definition = (AbstractBeanDefinition) Optional
           .ofNullable(((RootBeanDefinition) definition).getDecoratedDefinition())
           .map(BeanDefinitionHolder::getBeanDefinition).orElseThrow(() -> new IllegalStateException(
               "The target bean definition of scoped proxy bean not found. Root bean definition[" + holder + "]"));
       scopedProxy = true;
     }
     String beanClassName = definition.getBeanClassName();
     LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName
         + "' mapperInterface");

     definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
     // 核心方法 set了mapper工厂
     definition.setBeanClass(this.mapperFactoryBeanClass);

     definition.getPropertyValues().add("addToConfig", this.addToConfig);

     ..........
 }

MapperFactoryBean -> getObject -> getMapper
DefaultSqlSession

@Override
public <T> T getMapper(Class<T> type) {
    
    
   return configuration.getMapper(type, this);
 }

// 核心 MapperProxyFactory代理类 构建代理对象返回

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);
	}
}

三. 如何执行mapper

MapperProxy重写invoke

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
  try {
    
    
    if (Object.class.equals(method.getDeclaringClass())) {
    
    
      return method.invoke(this, args);
    } else {
    
    
      return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
    }
  } catch (Throwable t) {
    
    
    throw ExceptionUtil.unwrapThrowable(t);
  }
}

// invoke
@Override
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
    
    
	return mapperMethod.execute(sqlSession, args);
}

封装的五种操作类型 UNKNOWN, INSERT, UPDATE, DELETE, SELECT, FLUSH

public Object execute(SqlSession sqlSession, Object[] args) {
    
    
	Object result;
	switch (command.getType()) {
    
    
	  case INSERT: {
    
    
	    Object param = method.convertArgsToSqlCommandParam(args);
	    result = rowCountResult(sqlSession.insert(command.getName(), param));
	    break;
	  }
	  case UPDATE: {
    
    
	    Object param = method.convertArgsToSqlCommandParam(args);
	    result = rowCountResult(sqlSession.update(command.getName(), param));
	    break;
	  }
	  case DELETE: {
    
    
	    Object param = method.convertArgsToSqlCommandParam(args);
	    result = rowCountResult(sqlSession.delete(command.getName(), param));
	    break;
	  }
	  case SELECT:
	    if (method.returnsVoid() && method.hasResultHandler()) {
    
    
	      executeWithResultHandler(sqlSession, args);
	      result = null;
	    } else if (method.returnsMany()) {
    
    
	      result = executeForMany(sqlSession, args);
	    } else if (method.returnsMap()) {
    
    
	      result = executeForMap(sqlSession, args);
	    } else if (method.returnsCursor()) {
    
    
	      result = executeForCursor(sqlSession, args);
	    } else {
    
    
	      Object param = method.convertArgsToSqlCommandParam(args);
	      result = sqlSession.selectOne(command.getName(), param);
	      if (method.returnsOptional()
	          && (result == null || !method.getReturnType().equals(result.getClass()))) {
    
    
	        result = Optional.ofNullable(result);
	      }
	    }
	    break;
	  case FLUSH:
	    result = sqlSession.flushStatements();
	    break;
	  default:
	    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;
}

四. 一级 二级缓存

4.1 一级缓存

核心底层执行jdbc操作类 -> Executor实现类 -> BaseExecutor
query

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.");
	}
	// 默认isFlushCacheRequired=false 也就是说缓存不做特殊设置 这个if判断始终是false
	if (queryStack == 0 && ms.isFlushCacheRequired()) {
    
    
	  clearLocalCache();
	}
	List<E> list;
	try {
    
    
	  queryStack++;
	  // 一级缓存localCache
	  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--;
	}
	if (queryStack == 0) {
    
    
	  for (DeferredLoad deferredLoad : deferredLoads) {
    
    
	    deferredLoad.load();
	  }
	  // issue #601
	  deferredLoads.clear();
	  if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
    
    
	    // issue #482
	    clearLocalCache();
	  }
	}
	return list;
}

update

@Override
public int update(MappedStatement ms, Object parameter) throws SQLException {
    
    
	ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());
	if (closed) {
    
    
	  throw new ExecutorException("Executor was closed.");
	}
	// 更新操作会进行清缓存操作 同理 delete insert也会进行次操作map.clear()
	clearLocalCache();
	return doUpdate(ms, parameter);
}

分析源码我们会发现一级缓存默认开启

4.2 二级缓存

关于二级缓存 不想赘述了 mapper配置 如下配置 且select配置useCache=true

扫描二维码关注公众号,回复: 12302686 查看本文章
<cache>

在扫描时候 就会根据节点属性配置 解析成不同属性变量 最终执行逻辑如下:
核心底层执行jdbc操作类 -> Executor实现类 -> CachingExecutor

CachingExecutor -> query

public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
      throws SQLException {
    
    
	Cache cache = ms.getCache();
	if (cache != null) {
    
    
	  flushCacheIfRequired(ms);
	  if (ms.isUseCache() && resultHandler == null) {
    
    
	    ensureNoOutParams(ms, boundSql);
	    @SuppressWarnings("unchecked")
	    List<E> list = (List<E>) tcm.getObject(cache, key);
	    if (list == null) {
    
    
	      list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
	      tcm.putObject(cache, key, list); // issue #578 and #116
	    }
	    return list;
	  }
	}
	return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

查询步骤:

  1. 从二级缓存transactionalCaches拿数据
  2. 如果二级缓存没有 从一级拿localCache
  3. 如果一级缓存没有 查数据库
  4. 查询之后 会putObject 也就是put进transactionalCaches

CachingExecutor -> update

@Override
public int update(MappedStatement ms, Object parameterObject) throws SQLException {
    
    
	flushCacheIfRequired(ms);
	return delegate.update(ms, parameterObject);
}

@Override
public void commit(boolean required) throws SQLException {
    
    
	delegate.commit(required);
	tcm.commit();
}

// tcm.commit()
public void commit() {
    
    
	for (TransactionalCache txCache : transactionalCaches.values()) {
    
    
	  txCache.commit();
	}
}
// txCache.commit -> flushPendingEntries
private void flushPendingEntries() {
    
    
	for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {
    
    
	  delegate.putObject(entry.getKey(), entry.getValue());
	}
	for (Object entry : entriesMissedInCache) {
    
    
	  if (!entriesToAddOnCommit.containsKey(entry)) {
    
    
	    delegate.putObject(entry, null);
	  }
	}
}

更新步骤:

  1. 清除缓存
  2. jdbc底层更新操作
  3. tcm.commit commit之后刷新缓存

总结: 数据库连接配置存到 SqlSessionFactory中, mybatis扫描注解进行元数据绑定, getObject时候MapperProxyBean创建一个MapperProxy代理对象返回, 执行sql时, 走到Executor的实现类里, 默认开启一级缓存(BaseExecutor), (可以理解为)以session为单位. 二级缓存是(可以理解为)事务为单位, 走的是CachingExecutor, commit之后会更新二级缓存. 但是在现如今分布式多节点盛行的大环境下, 如果是我, 我一定把一级缓存关掉.
单纯的作为了一个ORM框架就挺好.

猜你喜欢

转载自blog.csdn.net/weixin_45657738/article/details/111174164
今日推荐