【1】MyBatis Plus插件
MyBatis Plus提供了分页插件PaginationInterceptor、执行分析插件SqlExplainInterceptor、性能分析插件PerformanceInterceptor以及乐观锁插件OptimisticLockerInterceptor。
Mybatis 通过插件 (Interceptor) 可以做到拦截四大对象相关方法
的执行 ,根据需求完成相关数据的动态改变。注意,这句话是核心哦。
四大对象
Executor
StatementHandler
ParameterHandler
ResultSetHandler
四大对象的每个对象在创建时,都会执行interceptorChain.pluginAll(),会经过每个插件对象的 plugin()方法,目的是为当前的四大对象创建代理。代理对象就可以拦截到四大对象相关方法的执行,因为要执行四大对象的方法需要经过代理 。四大插件对各自感兴趣的对象、方法、参数如下
① xml下插件的配置
如下所示:
<bean id="sqlSessionFactoryBean" class="com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean"> <!-- 数据源 --> <property name="dataSource" ref="dataSource"></property> <property name="configLocation" value="classpath:mybatis-config.xml"></property> <!-- 别名处理 --> <property name="typeAliasesPackage" value="com.jane.mp.beans"></property> <!-- 注入全局MP策略配置 --> <property name="globalConfig" ref="globalConfiguration"></property> <!-- 插件注册 --> <property name="plugins"> <list> <!-- 注册分页插件 --> <bean class="com.baomidou.mybatisplus.plugins.PaginationInterceptor"></bean> <!-- 注册执行分析插件 --> <bean class="com.baomidou.mybatisplus.plugins.SqlExplainInterceptor"> <property name="stopProceed" value="true"></property> </bean> <!-- 注册性能分析插件 --> <bean class="com.baomidou.mybatisplus.plugins.PerformanceInterceptor"> <property name="format" value="true"></property> <!-- <property name="maxTime" value="5"></property> --> </bean> <!-- 注册乐观锁插件 --> <bean class="com.baomidou.mybatisplus.plugins.OptimisticLockerInterceptor"> </bean> </list> </property> </bean>
② springboot下注册插件
@Bean
注解向容器中注入bean,这里以分页插件为例:
@Bean public PaginationInterceptor paginationInterceptor() { PaginationInterceptor paginationInterceptor = new PaginationInterceptor(); // 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求 默认false // paginationInterceptor.setOverflow(false); // 设置最大单页限制数量,默认 500 条,-1 不受限制 // paginationInterceptor.setLimit(500); // 开启 count 的 join 优化,只针对部分 left join paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true)); return paginationInterceptor; }
③ SqlExplainInterceptor
SQL执行分析拦截器,全类名是com.baomidou.mybatisplus.plugins.SqlExplainInterceptor,只支持 MySQL5.6.3以上版本。
该插件的作用是分析 DELETE UPDATE语句 ,防止小白或者恶意进行DELETE UPDATE全表操作,不建议在生产环境中使用会造成性能下降,
在插件的底层通过SQL语句分析命令 Explain 分析当前的 SQL语句,根据结果集中的 Extra列来断定当前是否全表操作。
④ PerformanceInterceptor性能分析插件
性能分析拦截器,全类名是com.baomidou.mybatisplus.plugins.PerformanceInterceptor,用于输出每条 SQL 语句及其执行时间。SQL性能执行分析 ,开发环境使用 超过指定时间,停止运行。
⑤ OptimisticLockerInterceptor乐观锁插件
全类名是com.baomidou.mybatisplus.plugins.OptimisticLockerInterceptor。如果想实现如下需求 : 当要更新一条记录的时候,希望这条记录没有被别人更新,就可以使用该插件进行判断。
乐观锁的实现原理(@Version 用于注解实体字段,必须要有) :
- 取出记录时,获取当前 version
- 更新时,带上这个version
- 执行更新时,set version = yourVersion+1 where version = yourVersion
- 如果 version不对,就更新失败
⑥ InterceptorChain
拦截器链,内部维护了一个私有常量List<Interceptor> interceptors
,其pluginAll方法为遍历interceptors并调用每个拦截器的plugin方法。
public class InterceptorChain { private final List<Interceptor> interceptors = new ArrayList<Interceptor>(); public Object pluginAll(Object target) { for (Interceptor interceptor : interceptors) { target = interceptor.plugin(target); } return target; } //添加拦截器到链表中 public void addInterceptor(Interceptor interceptor) { interceptors.add(interceptor); } //获取链表中的拦截器 public List<Interceptor> getInterceptors() { return Collections.unmodifiableList(interceptors); } }
在创建SqlSessionFactory会把MybatisSqlSessionFactoryBean.plugins
放到InterceptorChain.interceptors
中。
【2】获取sqlSessionFactoryBean
如下图所示,在系统启动时会初始化定义的bean。DefaultListableBeanFactory.preInstantiateSingletons
方法中会从beanDefinitionNames
中获取bean name然后依次创建。
这里可以看到RootBeanDefinition
是com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean
。
① 获取bean的过程中bean属性
如下所示,在getBean过程中可以看到bean的属性:
这里属性为plugins,会在bean的实例化时(populateBean)为属性赋值–也就是说会创建四个插件实例。
② createBean
第一次获取bean的时候会走到AbstractAutowireCapableBeanFactory.createBean
进行bean的创建。
/** 创建一个bean实例,为bean实例设置属性值,调用post-processors-bean后置处理器 */ @Override protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException { //...暂时忽略其他代码 //这里会拿到bean的class类型 Class<?> resolvedClass = resolveBeanClass(mbd, beanName); try { // 这里会首先触发BeanPostProcessors ,如果这里能获取到bean则直接返回,不再走doCreateBean Object bean = resolveBeforeInstantiation(beanName, mbdToUse); if (bean != null) { return bean; } } //...暂时忽略其他代码 //如果上面没有获取到bean,则会走doCreateBean--这也是创建bean的核心过程 Object beanInstance = doCreateBean(beanName, mbdToUse, args); //...暂时忽略其他代码 return beanInstance; }
如下图所示此时sqlSessionFactoryBean拿到的class为
com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean,不再是Mybatis自身的哦。
在AbstractAutowireCapableBeanFactory.resolveBeforeInstantiation中就会分别执行bean后置处理器的前置和后置方法。
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) { Object bean = null; if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) { // Make sure bean class is actually resolved at this point. if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { Class<?> targetType = determineTargetType(beanName, mbd); if (targetType != null) { // 后置处理器的before方法 bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName); if (bean != null) { // 后置处理器的after方法 bean = applyBeanPostProcessorsAfterInitialization(bean, beanName); } } } mbd.beforeInstantiationResolved = (bean != null); } return bean; }
执行后置处理器的前置方法如下所示:
拿到bean后置处理器,如果其是InstantiationAwareBeanPostProcessor
则调用其postProcessBeforeInstantiation
方法。
③ doCreateBean
AbstractAutowireCapableBeanFactory.doCreateBean
是创建bean的核心方法,这会为bean属性赋值并会触发bean后置处理器、InitializingBean以及自定init方法
等。
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) throws BeanCreationException { // Instantiate the bean.--实例化bean BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { //获取bean的包装对象-这里很重要 instanceWrapper = createBeanInstance(beanName, mbd, args); } final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null); Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null); mbd.resolvedTargetType = beanType; // Allow post-processors to modify the merged bean definition. synchronized (mbd.postProcessingLock) { if (!mbd.postProcessed) { try { //调用MergedBeanDefinitionPostProcessors的postProcessMergedBeanDefinition方法 applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); } catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Post-processing of merged bean definition failed", ex); } mbd.postProcessed = true; } } //... //这里暂时忽略其他代码 // Initialize the bean instance.--初始化bean实例 Object exposedObject = bean; try { //如下方法很重要 populateBean(beanName, mbd, instanceWrapper); if (exposedObject != null) { //这里会调用initializeBean方法 exposedObject = initializeBean(beanName, exposedObject, mbd); } } //... // Register bean as disposable. try { registerDisposableBeanIfNecessary(beanName, bean, mbd); } //... return exposedObject; }
④ populateBean
顾名思义,为bean实例属性赋值。
AbstractAutowireCapableBeanFactory.populateBean
protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) { //获取属性值列表 PropertyValues pvs = mbd.getPropertyValues(); //...该种符号表示暂时忽略其他代码 //在为bean属性赋值前,给InstantiationAwareBeanPostProcessors 机会修改bean的状态 //应用场景如支持字段注入 boolean continueWithPropertyPopulation = true; //循环调用InstantiationAwareBeanPostProcessors 的postProcessAfterInstantiation方法 if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) { continueWithPropertyPopulation = false; break; } } } } if (!continueWithPropertyPopulation) { return; } //解析autowire注解字段,进行主动注入 if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME || mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) { MutablePropertyValues newPvs = new MutablePropertyValues(pvs); // Add property values based on autowire by name if applicable. if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) { autowireByName(beanName, mbd, bw, newPvs); } // Add property values based on autowire by type if applicable. if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) { autowireByType(beanName, mbd, bw, newPvs); } pvs = newPvs; } boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors(); boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE); //循环调用InstantiationAwareBeanPostProcessors 的postProcessPropertyValues方法 if (hasInstAwareBpps || needsDepCheck) { PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); if (hasInstAwareBpps) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName); if (pvs == null) { return; } } } } if (needsDepCheck) { checkDependencies(beanName, mbd, filteredPds, pvs); } } //在这里为属性赋值,会进行类型转换,这里注意关键词deep copy //如果是引用类型且bean没有存在,则会进行bean的创建过程 applyPropertyValues(beanName, mbd, bw, pvs); }
如下图所示在创建sqlSessionFactoryBean过程中会创建其属性globalConfiguration对象:
如下图所示在创建sqlSessionFactoryBean过程中(从左侧的方法顺序就可以看出来)会创建其属性PaginationInterceptor对象:
如下图所示在创建sqlSessionFactoryBean过程中(从左侧的方法顺序就可以看出来)会创建其属性SqlExplainInterceptor
对象:
如下图所示在创建sqlSessionFactoryBean过程中(从左侧的方法顺序就可以看出来)会创建其属性PerformanceInterceptor
对象:
如下图所示在创建sqlSessionFactoryBean过程中(从左侧的方法顺序就可以看出来)会创建其属性OptimisticLockerInterceptor
对象:
⑤ initializeBean
AbstractAutowireCapableBeanFactory.initializeBean源码如下:
protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) { if (System.getSecurityManager() != null) { AccessController.doPrivileged(new PrivilegedAction<Object>() { @Override public Object run() { invokeAwareMethods(beanName, bean); return null; } }, getAccessControlContext()); } else { //调用意识/通知方法 invokeAwareMethods(beanName, bean); } Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { //调用bean后置处理器的前置方法 wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } //调用初始化方法 try { invokeInitMethods(beanName, wrappedBean, mbd); } catch (Throwable ex) { throw new BeanCreationException( (mbd != null ? mbd.getResourceDescription() : null), beanName, "Invocation of init method failed", ex); } if (mbd == null || !mbd.isSynthetic()) { // //调用bean后置处理器的后置方法 wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } return wrappedBean; }
AbstractAutowireCapableBeanFactory.invokeInitMethods方法源码如下:
protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd) throws Throwable { boolean isInitializingBean = (bean instanceof InitializingBean); if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) { if (logger.isDebugEnabled()) { logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'"); } //调用InitializingBean.afterPropertiesSet if (System.getSecurityManager() != null) { try { AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() { @Override public Object run() throws Exception { ((InitializingBean) bean).afterPropertiesSet(); return null; } }, getAccessControlContext()); } catch (PrivilegedActionException pae) { throw pae.getException(); } } else { ((InitializingBean) bean).afterPropertiesSet(); } } //调用自定义初始化方法 if (mbd != null) { String initMethodName = mbd.getInitMethodName(); if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) && !mbd.isExternallyManagedInitMethod(initMethodName)) { invokeCustomInitMethod(beanName, bean, mbd); } } }
如下图所示,MybatisSqlSessionFactoryBean
同样实现了InitializingBean
接口。那么我们就需要注意其afterPropertiesSet
方法了。
⑥ afterPropertiesSet
如下图所示,如果bean实现了InitializingBean
接口,那么在初始化过程中一定会调用其afterPropertiesSet
方法。
MybatisSqlSessionFactoryBean.afterPropertiesSet
如下所示,代码很简短只是创建了sqlSessionFactory。
@Override public void afterPropertiesSet() throws Exception { notNull(dataSource, "Property 'dataSource' is required"); //sqlSessionFactoryBuilder在populateBean的applyPropertyValues过程中已经存在! notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required"); state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null), "Property 'configuration' and 'configLocation' can not specified with together"); this.sqlSessionFactory = buildSqlSessionFactory(); }
进入afterPropertiesSet()
方法前MybatisSqlSessionFactoryBean
如下所示:
我们看一下sqlSessionFactory,这是一段很长的过程:
protected SqlSessionFactory buildSqlSessionFactory() throws Exception { Configuration configuration; // TODO 加载自定义 MybatisXmlConfigBuilder MybatisXMLConfigBuilder xmlConfigBuilder = null; if (this.configuration != null) { configuration = this.configuration; if (configuration.getVariables() == null) { configuration.setVariables(this.configurationProperties); } else if (this.configurationProperties != null) { configuration.getVariables().putAll(this.configurationProperties); } } else if (this.configLocation != null) { //通常如果配置了configLocation会从这里创建MybatisXMLConfigBuilder, //其构造方法又创建了MybatisConfiguration xmlConfigBuilder = new MybatisXMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties); configuration = xmlConfigBuilder.getConfiguration(); } else { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration"); } // TODO 使用自定义配置 configuration = new MybatisConfiguration(); if (this.configurationProperties != null) { configuration.setVariables(this.configurationProperties); } } if (this.objectFactory != null) { configuration.setObjectFactory(this.objectFactory); } if (this.objectWrapperFactory != null) { configuration.setObjectWrapperFactory(this.objectWrapperFactory); } if (this.vfs != null) { configuration.setVfsImpl(this.vfs); } // 类型别名包配置处理 if (hasLength(this.typeAliasesPackage)) { // TODO 支持自定义通配符 String[] typeAliasPackageArray; if (typeAliasesPackage.contains("*") && !typeAliasesPackage.contains(",") && !typeAliasesPackage.contains(";")) { typeAliasPackageArray = PackageHelper.convertTypeAliasesPackage(typeAliasesPackage); } else { typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); } if (typeAliasPackageArray == null) { throw new MybatisPlusException("not find typeAliasesPackage:" + typeAliasesPackage); } for (String packageToScan : typeAliasPackageArray) { configuration.getTypeAliasRegistry().registerAliases(packageToScan, typeAliasesSuperType == null ? Object.class : typeAliasesSuperType); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Scanned package: '" + packageToScan + "' for aliases"); } } } // TODO 自定义枚举类扫描处理 if (hasLength(this.typeEnumsPackage)) { Set<Class> classes = null; if (typeEnumsPackage.contains("*") && !typeEnumsPackage.contains(",") && !typeEnumsPackage.contains(";")) { classes = PackageHelper.scanTypePackage(typeEnumsPackage); } else { String[] typeEnumsPackageArray = tokenizeToStringArray(this.typeEnumsPackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); if (typeEnumsPackageArray == null) { throw new MybatisPlusException("not find typeEnumsPackage:" + typeEnumsPackage); } classes = new HashSet<Class>(); for (String typePackage : typeEnumsPackageArray) { classes.addAll(PackageHelper.scanTypePackage(typePackage)); } } // 取得类型转换注册器 TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry(); for (Class cls : classes) { if (cls.isEnum()) { if (IEnum.class.isAssignableFrom(cls)) { typeHandlerRegistry.register(cls.getName(), com.baomidou.mybatisplus.handlers.EnumTypeHandler.class.getCanonicalName()); } else { // 使用原生 EnumOrdinalTypeHandler typeHandlerRegistry.register(cls.getName(), org.apache.ibatis.type.EnumOrdinalTypeHandler.class.getCanonicalName()); } } } } // 类型别名注册 if (!isEmpty(this.typeAliases)) { for (Class<?> typeAlias : this.typeAliases) { configuration.getTypeAliasRegistry().registerAlias(typeAlias); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Registered type alias: '" + typeAlias + "'"); } } } //这里会遍历plugins然后调用 configuration.addInterceptor(plugin); //--这会把plugin放到InterceptorChain.interceptors中 if (!isEmpty(this.plugins)) { for (Interceptor plugin : this.plugins) { configuration.addInterceptor(plugin); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Registered plugin: '" + plugin + "'"); } } } if (hasLength(this.typeHandlersPackage)) { 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)) { for (TypeHandler<?> typeHandler : this.typeHandlers) { configuration.getTypeHandlerRegistry().register(typeHandler); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Registered type handler: '" + typeHandler + "'"); } } } // databaseIdProvider -据库厂商标识处理 if (this.databaseIdProvider != null) {//fix #64 set databaseId before parse mapper xmls try { configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource)); } catch (SQLException e) { throw new NestedIOException("Failed getting a databaseId", e); } } // 缓存 if (this.cache != null) { configuration.addCache(this.cache); } if (xmlConfigBuilder != null) { try { // xml解析 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) { this.transactionFactory = new SpringManagedTransactionFactory(); } configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource)); // 设置元数据相关 GlobalConfigUtils.setMetaData(dataSource, globalConfig); // 获取SqlSessionFactory SqlSessionFactory sqlSessionFactory = this.sqlSessionFactoryBuilder.build(configuration); // TODO SqlRunner SqlRunner.FACTORY = sqlSessionFactory; // TODO 缓存 sqlSessionFactory globalConfig.setSqlSessionFactory(sqlSessionFactory); // TODO 设置全局参数属性 globalConfig.signGlobalConfig(sqlSessionFactory); if (!isEmpty(this.mapperLocations)) { if (globalConfig.isRefresh()) { //TODO 设置自动刷新配置 减少配置 new MybatisMapperRefresh(this.mapperLocations, sqlSessionFactory, 2, 2, true); } for (Resource mapperLocation : this.mapperLocations) { if (mapperLocation == null) { continue; } try { // TODO 这里也换了噢噢噢噢 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 sqlSessionFactory; }
上面代码主要做了如下事情:
获取MybatisXMLConfigBuilder对象
获取Configuration对象-MybatisConfiguration
配置对象Configuration添加插件configuration.addInterceptor(plugin);其会添加到InterceptorChain.interceptors中
xmlConfigBuilder.parse()对configuration做进一步处理
获取SpringManagedTransactionFactory用来创建SpringManagedTransaction
获取一个DefaultSqlSessionFactory实例对象
globalConfig.setSqlSessionFactory(sqlSessionFactory)中会创建MybatisSqlSessionTemplate--SqlSession的实现类
解析mapperLocation对应的一个个mapper配置文件,使用助手builderAssistant的addMappedStatement方法将一个个结点添加配置对象中–这里可以理解为解析SQL完成
其他属性设置等等
也就是说,在MybatisSqlSessionFactoryBean.afterPropertiesSet方法执行结束后,SqlSessionFactory、SqlSessionTemplate、Configuration等都已存在!
【3】查询执行流程分析之实例准备
查询流程可以参考如下图示
① employeeMapper
示例代码如下:
List<Employee > emps = employeeMapper.selectPage(page, null);
如下图所示,此时我们获取到的employeeMapper其实是个代理对象:
1.1如下所示,会执行MapperProxy.invoke
方法
1.2 判断SQLtype
MapperMethod.execute
中会根据SqlCommand.type
进行类型判断:
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: //这里根据method的返回类型执行不同方法 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); } 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; }
首先根据INSERT、UPDATE、DELETE、SELECT、FLUSH进行判断,然后根据方法返回结果进行判断(如果是select的话)。
1.3 MapperMethod.executeForMany
这里我们走到了executeForMany
,该方法解析参数然后调用sqlSession获取结果。
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) { List<E> result; //解析参数为map结构 Object param = method.convertArgsToSqlCommandParam(args); //判断是否拥有RowBounds参数---也就是是否需要分页 if (method.hasRowBounds()) { RowBounds rowBounds = method.extractRowBounds(args); result = sqlSession.<E>selectList(command.getName(), param, rowBounds); } else { //如果不需要分页,则会查询所有符合条件的结果 result = sqlSession.<E>selectList(command.getName(), param); } // issue #510 Collections & arrays support //判断返回数组还是集合 if (!method.getReturnType().isAssignableFrom(result.getClass())) { if (method.getReturnType().isArray()) { return convertToArray(result); } else { return convertToDeclaredCollection(sqlSession.getConfiguration(), result); } } return result; }
② sqlSession.<E>selectList(command.getName(), param, rowBounds)
2.1 SqlSessionTemplate.selectList
注意哦,这里是sqlsessionTemplate。
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { return this.sqlSessionProxy.<E> selectList(statement, parameter, rowBounds); }
这里调用其代理对象来执行方法,sqlSessionProxy如下所示:
如下图所示,其来到了SqlSessionTemplate$SqlSessionInterceptor.invoke方法中(SqlSessionInterceptor是SqlSessionTemplate的内部类):
2.2 SqlSessionInterceptor
这里会获取SqlSession 实例对象,根据SqlSession 和参数反射调用Method 获取结果。
private class SqlSessionInterceptor implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //这里获取SqlSession 实例对象 SqlSession sqlSession = getSqlSession( SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator); try { //反射调用方法获取结果 交给获取的SqlSession 去处理 Object result = method.invoke(sqlSession, args); if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) { // force commit even on non-dirty sessions because some databases require // a commit/rollback before calling close() sqlSession.commit(true); } return result; } catch (Throwable t) { Throwable unwrapped = unwrapThrowable(t); if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) { // release the connection to avoid a deadlock if the translator is no loaded. See issue #22 closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory); sqlSession = null; Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped); if (translated != null) { unwrapped = translated; } } throw unwrapped; } finally { if (sqlSession != null) { closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory); } } } }
这里invoke方法会先获取一个sqlsession,然后反射调用目标方法,最后关闭sqlsession后将result返回。我们接下来看一下其获取sqlsession的过程。
③ 获取sqlSession
这里是真实获取一个sqlsession实例对象,SqlSessionUtils.getSqlSession如下所示:
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) { //断言sessionFactory不为null notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED); //断言executorType不为null notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED); //获取SqlSessionHolder 先尝试从holder中获取SqlSession SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory); //如果获取的session不为null,则直接返回 SqlSession session = sessionHolder(executorType, holder); if (session != null) { return session; } if (LOGGER.isDebugEnabled()) { LOGGER.debug("Creating a new SqlSession"); } //如果上面没有获取到sqlsession,则使用sessionFactory创建一个session session = sessionFactory.openSession(executorType); //获取一个SessionHolder,并把括号内参数赋予给SessionHolder registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session); //返回获取的sqlsession return session; }
3.1 sessionFactory.openSession
DefaultSqlSessionFactory.openSession
如下所示:
public SqlSession openSession(ExecutorType execType) { return openSessionFromDataSource(execType, null, false); }
继续往下跟踪openSessionFromDataSource:
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; try { //根据MybatisConfiguration获取environment :其有数据源、transactionFactory等 final Environment environment = configuration.getEnvironment(); //获取事务工厂 final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); //获取事务 tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); //获取执行器,这里很重要哦会调用interceptorChain.pluginAll final Executor executor = configuration.newExecutor(tx, execType); //返回一个DefaultSqlSession实例 return new DefaultSqlSession(configuration, executor, autoCommit); } catch (Exception e) { closeTransaction(tx); // may have fetched a connection so lets call close() throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
④ configuration.newExecutor
获取执行器对象并第一次调用interceptorChain.pluginAll。这里来到了Configuration.newExecutor方法处:
public Executor newExecutor(Transaction transaction, ExecutorType executorType) { executorType = executorType == null ? defaultExecutorType : executorType; executorType = executorType == null ? ExecutorType.SIMPLE : executorType; Executor executor; //根据不同ExecutorType获取不同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 (cacheEnabled) { executor = new CachingExecutor(executor); } //会调用插件 executor = (Executor) interceptorChain.pluginAll(executor); return executor; }
首先会根据ExecutorType创建不同的Executor,如批处理BatchExecutor、ReuseExecutor、SimpleExecutor以及缓存处理CachingExecutor。Executor是Mybatis的核心,负责SQL动态语句的生成以及查询缓存的维护。
而BatchExecutor、ReuseExecutor、SimpleExecutor实例化的构造方法都会走到BaseExecutor构造方法,可以看到执行器Executor持有了事务对象transaction 、缓存对象localCache 、出参缓存对象localOutputParameterCache 、配置对象configuration 以及包装执行器对象wrapper -其也是Executor类型。
protected BaseExecutor(Configuration configuration, Transaction transaction) { this.transaction = transaction; this.deferredLoads = new ConcurrentLinkedQueue<DeferredLoad>(); this.localCache = new PerpetualCache("LocalCache"); this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache"); this.closed = false; this.configuration = configuration; this.wrapper = this; }
获取sqlsession时序图如下
⑤ InterceptorChain.pluginAll
会挨个调用插件,如果该插件对当前对象感兴趣就会对其包装生成代理对象。
此时包括的插件如下图所示:
如下代码所示,会遍历拦截器然后调用每个拦截器的plugin方法,本文这里target为CachingExecutor
。
public Object pluginAll(Object target) { for (Interceptor interceptor : interceptors) { target = interceptor.plugin(target); } return target; }
5.1PaginationInterceptor.plugin
如下,其只会对StatementHandler起作用。
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})}) public class PaginationInterceptor extends SqlParserHandler implements Interceptor { //... }
plugin方法如下:
public Object plugin(Object target) { if (target instanceof StatementHandler) { return Plugin.wrap(target, this); } return target; }
5.2SqlExplainInterceptor.plugin
(目前只支持 MYSQL-5.6.3 以上版本)如下所示,其对Executor起作用。
@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})}) public class SqlExplainInterceptor implements Interceptor { //... }
plugin方法如下:
public Object plugin(Object target) { if (target instanceof Executor) { return Plugin.wrap(target, this); } return target; }
Plugin.wrap方法,会生成一个target代理对象
public static Object wrap(Object target, Interceptor interceptor) { //获取感兴趣的类与类的方法 Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor); //获取目标类型,这里为class org.apache.ibatis.executor.CachingExecutor Class<?> type = target.getClass(); //获取所有type.getInterfaces()且在signatureMap 中的有 Class<?>[] interfaces = getAllInterfaces(type, signatureMap); if (interfaces.length > 0) { return Proxy.newProxyInstance( type.getClassLoader(), interfaces, new Plugin(target, interceptor, signatureMap)); } return target; }
5.3PerformanceInterceptor.plugin
其对StatementHandler起作用
@Intercepts({@Signature(type = StatementHandler.class, method = "query", args = {Statement.class, ResultHandler.class}), @Signature(type = StatementHandler.class, method = "update", args = {Statement.class}), @Signature(type = StatementHandler.class, method = "batch", args = {Statement.class})}) public class PerformanceInterceptor implements Interceptor { //... }
plugin方法如下所示:
public Object plugin(Object target) { if (target instanceof StatementHandler) { return Plugin.wrap(target, this); } return target; }
5.4 OptimisticLockerInterceptor.plugin
其对Executor起作用
@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})}) public class OptimisticLockerInterceptor implements Interceptor { //... } 同样调用Plugin.wrap返回一个动态代理对象。 publ
同样调用Plugin.wrap返回一个动态代理对象。
public Object plugin(Object target) { if (target instanceof Executor) { return Plugin.wrap(target, this); } return target; }
这时target如下所示:
插件会产生目标对象的代理对象,多个插件就会产生多层代理。创建动态代理的时候,是按照插件配置顺序创建层层代理对象。执行目标方法的之后,按照逆向顺序执行。
⑥ 依次返回到SqlSessionUtils.getSqlSession处
这里会根据session
, executorType
, exceptionTranslator
实例化一个SqlSessionHolder
。
然后会依次返回,返回到③获取sqlsession处:
那么接下来就该Object result = method.invoke(sqlSession, args)使用刚获取到的sqlsession以及参数反射调用具体方法了。这里method为public abstract java.util.List org.apache.ibatis.session.SqlSession.selectList(java.lang.String,java.lang.Object,org.apache.ibatis.session.RowBounds)
参数如下图所示:
【4】查询执行流程分析之查询过程
① DefaultSqlSession.selectList
这里的statement其实就是方法的完整描述,如com.jane.mp.mapper.EmployeeMapper.selectPage,RowBounds 是用来进行分页的。这里的executor是org.apache.ibatis.executor.CachingExecutor@433ffad1
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { try { MappedStatement ms = configuration.getMappedStatement(statement); return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); } catch (Exception e) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
需要注意的是,这里的executor为代理对象:
② Plugin.invoke
代码如下所示:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { //获取感兴趣的方法 Set<Method> methods = signatureMap.get(method.getDeclaringClass()); if (methods != null && methods.contains(method)) { return interceptor.intercept(new Invocation(target, method, args)); } return method.invoke(target, args); } catch (Exception e) { throw ExceptionUtil.unwrapThrowable(e); } }
如下图所示,这里target为代理对象,interceptor为OptimisticLockerInterceptor。
Executor会被OptimisticLockerInterceptor、SqlExplainInterceptor处理(二者都处理update方法),所以会进行两次代理。那么在这里也同样会进行两次解析。第二次来到invoke方法如下图所示:
如上图所示,这里我们的目标Executor是CachingExecutor。
③ CachingExecutor.query
代码如下所示:
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { BoundSql boundSql = ms.getBoundSql(parameterObject); CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql); return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); }
首先使用ms.getBoundSql获取了boundSql对象如下:
createCacheKey(ms, parameterObject, rowBounds, boundSql)创建缓存key。这个方法很有意思,它自己并不处理而是使用了其委派对象delegate去处理(这里可以联想一下设计模式的委派模式,也可以理解为这里运用了装饰器,在delegate基础功能上增加了额外功能),如下所示:
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) { return delegate.createCacheKey(ms, parameterObject, rowBounds, boundSql); }
我们再回顾下CachingExecutor的构造方法:
public class CachingExecutor implements Executor { //委派实例对象 private final Executor delegate; private final TransactionalCacheManager tcm = new TransactionalCacheManager(); public CachingExecutor(Executor delegate) { this.delegate = delegate; delegate.setExecutorWrapper(this); } //... }
然后我们依次返回查看获取到的CacheKey-1113517040:-1225312459:com.jane.mp.mapper.EmployeeMapper.selectPage:0:1:SELECT id AS id,last_name AS lastName,email,gender,age,version FROM tbl_employee:MybatisSqlSessionFactoryBean
④ CachingExecutor.query
这里会首先判断缓存是否存在,如果缓存存在则尝试从缓存里面根据CacheKey 获取结果,如果结果获取不到则执行查询并再次放到缓存里面;如果缓存不存在则直接直接查询。
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.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); tcm.putObject(cache, key, list); // issue #578 and #116 } return list; } } return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); }
这里delegate为SimpleExecutor实例对象,这个方法是典型的装饰器运用。
⑤ BaseExecutor.query
SimpleExecutor会调用父类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."); } if (queryStack == 0 && ms.isFlushCacheRequired()) { clearLocalCache(); } List<E> list; try { queryStack++; 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; }
⑥ BaseExecutor.queryFromDatabase
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { List<E> list; localCache.putObject(key, EXECUTION_PLACEHOLDER); try { //执行查询 list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql); } finally { localCache.removeObject(key); } localCache.putObject(key, list); if (ms.getStatementType() == StatementType.CALLABLE) { localOutputParameterCache.putObject(key, parameter); } return list; }
方法解释如下:
① 往localCache中放入(key, EXECUTION_PLACEHOLDER);
② doQuery执行查询获取list结果;
③ localCache.removeObject(key);从localCache中移除key
④ localCache.putObject(key, list);放入结果
⑤ 如果是存储过程则处理out类型参数localOutputParameterCache.putObject(key, parameter);
⑥ 返回结果list
⑦ SimpleExecutor.doQuery
继续看一下doQuery方法,其实这里就到了JDBC的范畴。
@Override public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { Statement stmt = null; try { //获取环境配置信息 Configuration configuration = ms.getConfiguration(); //获取语句处理器 StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); //准备statement stmt = prepareStatement(handler, ms.getStatementLog()); // 使用StatementHandler 进行查询 return handler.<E>query(stmt, resultHandler); } finally { // 关闭Statement closeStatement(stmt); } }
下面我们看一下其获取StatementHandler
并使用StatementHandler
进行查询的过程。
⑧ 获取StatementHandler
需要注意的是newStatementHandler
中会调用interceptorChain.pluginAll
方法。那么此时如果项目配置了分页插件和性能分析插件,就会生效。
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; }
StatementHandler:处理sql语句预编译,设置参数等相关工作;
ParameterHandler:设置预编译参数用的
ResultHandler:处理结果集
TypeHandler:在整个过程中,进行数据库类型和javaBean类型的映射。
这里interceptorChain.pluginAll我们就不再看了,着重分析RoutingStatementHandler的创建。
如下所示其根据statementType(STATEMENT/PREPARED/CALLABLE)分别实例化SimpleStatementHandler、PreparedStatementHandler以及CallableStatementHandler。通常我们默认的是PreparedStatementHandler。
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { switch (ms.getStatementType()) { case STATEMENT: delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); break; case PREPARED: delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); break; case CALLABLE: delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); break; default: throw new ExecutorException("Unknown statement type: " + ms.getStatementType()); } }
通常我们默认的是PreparedStatementHandler
,,而PreparedStatementHandler又调用了父类BaseStatementHandler构造函数进行初始化。
// PreparedStatementHandler super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql); // BaseStatementHandler protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { this.configuration = mappedStatement.getConfiguration(); this.executor = executor; this.mappedStatement = mappedStatement; this.rowBounds = rowBounds; this.typeHandlerRegistry = configuration.getTypeHandlerRegistry(); this.objectFactory = configuration.getObjectFactory(); if (boundSql == null) { // issue #435, get the key before calculating the statement generateKeys(parameterObject); boundSql = mappedStatement.getBoundSql(parameterObject); } this.boundSql = boundSql; // 哈哈,没想到吧,在获取StatementHandler中实例化parameterHandler 、resultSetHandler this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql); this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql); }
如上所示在父类BaseStatementHandler的构造函数中为configuration 、executor、mappedStatement、rowBounds、typeHandlerRegistry、objectFactory、boundSql、parameterHandler以及resultSetHandler进行了赋值。这里需要注意的是configuration.newParameterHandler和configuration.newResultSetHandler在实例化过程中同样会走一遍interceptorChain.pluginAll哦。
⑨ prepareStatement
什么意思呢?预先准备Statement。方法如下所示:获取Connection ,调用handler的prepare方法,然后进行参数化。
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Statement stmt; Connection connection = getConnection(statementLog); stmt = handler.prepare(connection, transaction.getTimeout()); handler.parameterize(stmt); return stmt; }
① handler.prepare
其最终会走到抽象父类BaseStatementHandler
的prepare
方法,本文这里会获取一个PreparedStatement
实例。
@Override public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException { ErrorContext.instance().sql(boundSql.getSql()); Statement statement = null; try { // 实例化Statement statement = instantiateStatement(connection); // 设置Timeout setStatementTimeout(statement, transactionTimeout); // 设置FetchSize setFetchSize(statement); return statement; } catch (SQLException e) { closeStatement(statement); throw e; } catch (Exception e) { closeStatement(statement); throw new ExecutorException("Error preparing statement. Cause: " + e, e); } } ② handler.parameterize PreparedStatementHandler.parameterize如下所示: parameterHandler.setParameters((PreparedStatement) statement);
② handler.parameterize
PreparedStatementHandler.parameterize
如下所示:
parameterHandler.setParameters((PreparedStatement) statement);
简单来说,就是为PreparedStatement设置参数。
@Override public void setParameters(PreparedStatement ps) { ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId()); List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); if (parameterMappings != null) { for (int i = 0; i < parameterMappings.size(); i++) { ParameterMapping parameterMapping = parameterMappings.get(i); if (parameterMapping.getMode() != ParameterMode.OUT) { Object value; String propertyName = parameterMapping.getProperty(); if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params value = boundSql.getAdditionalParameter(propertyName); } else if (parameterObject == null) { value = null; } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) { value = parameterObject; } else { MetaObject metaObject = configuration.newMetaObject(parameterObject); value = metaObject.getValue(propertyName); } TypeHandler typeHandler = parameterMapping.getTypeHandler(); JdbcType jdbcType = parameterMapping.getJdbcType(); if (value == null && jdbcType == null) { jdbcType = configuration.getJdbcTypeForNull(); } try { typeHandler.setParameter(ps, i + 1, value, jdbcType); } catch (TypeException | SQLException e) { throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e); } } } } }
⑩ handler.query(stmt, resultHandler)
本文这里会走到PreparedStatementHandler.query
方法,简单来说就是调用PreparedStatement
执行查询,然后使用resultSetHandler
对返回结果进行处理。
@Override public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; // 这里Java只是提供了接口,具体实现由各数据库厂商来定 ps.execute(); return resultSetHandler.handleResultSets(ps); }
这里我们可以放一段查询流程如下(从里到外的查询流程):
at com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal(ClientPreparedStatement.java:953) at com.mysql.cj.jdbc.ClientPreparedStatement.execute(ClientPreparedStatement.java:370) at com.zaxxer.hikari.pool.ProxyPreparedStatement.execute(ProxyPreparedStatement.java:44) at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.execute(HikariProxyPreparedStatement.java) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.apache.ibatis.logging.jdbc.PreparedStatementLogger.invoke(PreparedStatementLogger.java:59) at com.sun.proxy.$Proxy141.execute(Unknown Source) at org.apache.ibatis.executor.statement.PreparedStatementHandler.query(PreparedStatementHandler.java:64) at org.apache.ibatis.executor.statement.RoutingStatementHandler.query(RoutingStatementHandler.java:79) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.apache.ibatis.plugin.Plugin.invoke(Plugin.java:63) at com.sun.proxy.$Proxy140.query(Unknown Source) at com.baomidou.mybatisplus.core.executor.MybatisSimpleExecutor.doQuery(MybatisSimpleExecutor.java:67) at org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(BaseExecutor.java:324) at org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:156) at org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:136) at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:147) at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:140) at org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(DefaultSqlSession.java:76) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:426)
(11)resultSetHandler.handleResultSets
也就是处理返回结果集获取list。看到resultSet是不是联想到了最初写原生JDBC时的场景?原始jdbc使用resultSet解析返回结果如下所示:
ResultSetMetaData rsmd = resultSet.getMetaData(); Map<String, Object> valueMap = new HashMap<String, Object>(); /* 得到列的别名及值 */ while (resultSet.next()) { /* 打印每一列的列名---getcolumncount 得到結果集中多少列 */ for (int i = 0; i < rsmd.getColumnCount(); i++) { /* 索引默认从 1 开始,获取列名 */ String columnLabel = rsmd.getColumnLabel(i + 1); /* 获取对应的列值 */ Object columnValue = resultSet.getObject(columnLabel); // Object columnValue = resultSet.getObject(i+1); /* 放入map里面 */ valueMap.put(columnLabel, columnValue); } }
ResultSet 对象以逻辑表格的形式封装了执行数据库操作的结果集,ResultSet 接口由数据库厂商实现。ResultSet 对象维护了一个指向当前数据行的游标,初始的时候,游标在第一行之前,可以通过 ResultSet 对象的 next() 方法移动到下一行。
这里我们看下mybatis处理返回结果集。
@Override public List<Object> handleResultSets(Statement stmt) throws SQLException { ErrorContext.instance().activity("handling results").object(mappedStatement.getId()); final List<Object> multipleResults = new ArrayList<>(); int resultSetCount = 0; ResultSetWrapper rsw = getFirstResultSet(stmt); List<ResultMap> resultMaps = mappedStatement.getResultMaps(); int resultMapCount = resultMaps.size(); validateResultMapsCount(rsw, resultMapCount); while (rsw != null && resultMapCount > resultSetCount) { ResultMap resultMap = resultMaps.get(resultSetCount); handleResultSet(rsw, resultMap, multipleResults, null); rsw = getNextResultSet(stmt); cleanUpAfterHandlingResultSet(); resultSetCount++; } String[] resultSets = mappedStatement.getResultSets(); if (resultSets != null) { while (rsw != null && resultSetCount < resultSets.length) { ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]); if (parentMapping != null) { String nestedResultMapId = parentMapping.getNestedResultMapId(); ResultMap resultMap = configuration.getResultMap(nestedResultMapId); handleResultSet(rsw, resultMap, null, parentMapping); } rsw = getNextResultSet(stmt); cleanUpAfterHandlingResultSet(); resultSetCount++; } } return collapseSingleResultList(multipleResults); }
SpringBoot+MybatisPlus环境下查询流程痕迹(假设查询是从UserController.checkLogin(UserController.java:306)开始
)
UserController.checkLogin(UserController.java:306) JdkDynamicAopProxy.invoke AopUtils.invokeJoinpointUsingReflection com.baomidou.mybatisplus.extension.service.impl.ServiceImpl.list com.baomidou.mybatisplus.core.override.MybatisMapperProxy.invoke com.baomidou.mybatisplus.core.override.MybatisMapperMethod.execute com.baomidou.mybatisplus.core.override.MybatisMapperMethod.executeForMany org.mybatis.spring.SqlSessionTemplate.selectList org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke org.apache.ibatis.session.defaults.DefaultSqlSession.selectList org.apache.ibatis.session.defaults.DefaultSqlSession.selectList org.apache.ibatis.executor.BaseExecutor.query org.apache.ibatis.executor.BaseExecutor.queryFromDatabase com.baomidou.mybatisplus.core.executor.MybatisSimpleExecutor.doQuery org.apache.ibatis.plugin.Plugin.invoke org.apache.ibatis.executor.statement.RoutingStatementHandler.query org.apache.ibatis.executor.statement.PreparedStatementHandler.query org.apache.ibatis.logging.jdbc.PreparedStatementLogger.invoke com.zaxxer.hikari.pool.HikariProxyPreparedStatement.execute com.zaxxer.hikari.pool.ProxyPreparedStatement.execute com.mysql.cj.jdbc.ClientPreparedStatement.execute com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal ...
这里第一步就是AopProxy,说明这里List<SysUser> list = userService.list(queryWrapper);中userService是个代理类。
com.baomidou.mybatisplus.extension.service.impl.ServiceImpl.list时这里的baseMapper如下也是一个代理
MybatisMapperMethod.executeForMany时,这里的sqlSession其实是SqlSessionTemplate。将会调用内部的this.sqlSessionProxy.selectList(statement, parameter);方法,委派给sqlSessionProxy处理。sqlSessionProxy中的目标处理器是SqlSessionTemplate$SqlSessionInterceptor。
在SqlSessionInterceptor的invoke方法中获取真正的sqlsession处理查询。