MyBatis Plus插件机制与执行流程原理分析详解

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 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列来断定当前是否全表操作。

④ 性能分析插件

性能分析拦截器,全类名是com.baomidou.mybatisplus.plugins.PerformanceInterceptor,用于输出每条 SQL 语句及其执行时间。SQL性能执行分析 ,开发环境使用 超过指定时间,停止运行。

⑤ 乐观锁插件

全类名是com.baomidou.mybatisplus.plugins.OptimisticLockerInterceptor。如果想实现如下需求 : 当要更新一条记录的时候,希望这条记录没有被别人更新,就可以使用该插件进行判断。

乐观锁的实现原理(@Version 用于注解实体字段,必须要有) :

  • 取出记录时,获取当前 version
  • 更新时,带上这个version
  • 执行更新时,set version = yourVersion+1 where version = yourVersion
  • 如果 version不对,就更新失败

【2】获取sqlSessionFactoryBean

如下图所示,在系统启动时会初始化定义的bean。DefaultListableBeanFactory.preInstantiateSingletons方法中会从beanDefinitionNames中获取bean name然后依次创建。

这里可以看到RootBeanDefinition是com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean。

① 获取bean的过程中bean属性

如下所示,在getBean过程中可以看到bean的属性:

② createBean

第一次获取bean的时候会走到AbstractAutowireCapableBeanFactory.createBean进行bean的创建。

 /** 创建一个bean实例,为bean实例设置属性值,调用post-processors-bean后置处理器 */@Overrideprotected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args)
 throws BeanCreationException
 {//...暂时忽略其他代码
 try {// 这里会首先触发BeanPostProcessors ,如果这里能获取到bean则直接返回,不再走doCreateBean
 Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
 if (bean != null)
 {
 return bean;
 } }//...暂时忽略其他代码

在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) {
bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
if (bean != null)
{
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
}
}
mbd.beforeInstantiationResolved = (bean != null);
}
return bean;
}

执行后置处理器的前置方法如下所示:

③ 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.--实例化beanBeanWrapper 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)
{
 exposedObject = initializeBean(beanName, exposedObject, mbd);
}}//...

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

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方法了。

⑥ MybatisSqlSessionFactoryBean.afterPropertiesSet

如下所示,代码很简短只是创建了sqlSessionFactory。

@Overridepublic 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 + "'");
 }
 } }
 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 + "'");
 }  } }
 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
 {
 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 = 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);
  • xmlConfigBuilder.parse()对configuration做进一步处理
  • 获取SpringManagedTransactionFactory用来创建SpringManagedTransaction
  • 获取一个DefaultSqlSessionFactory实例对象
  • globalConfig.setSqlSessionFactory(sqlSessionFactory)中会创建MybatisSqlSessionTemplate
  • 解析mapperLocation对应的一个个mapper配置文件,使用助手builderAssistant的addMappedStatement方法将一个个结点添加配置对象中
  • 其他属性设置等等

也就是说,在MybatisSqlSessionFactoryBean.afterPropertiesSet方法执行结束后,SqlSessionFactory、SqlSessionTemplate、Configuration等都已存在!

【3】查询执行流程分析

示例代码如下:

List<Employee > emps = employeeMapper.selectPage(page, null);

如下图所示,此时我们获取到的employeeMapper其实是个代理对象:

总结

到此这篇关于MyBatis Plus插件机制与执行流程原理分析的文章就介绍到这了,更多相关MyBatis Plus插件机制内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Mybatis Plus 字段为空值时执行更新方法未更新解决方案

    问题描述 系统测试过程中,同事测试出使用Mybatis Plus提供的封装方法UpdateById()时,字段从前台传入的数据为空值,但是执行方法后该字段未得到更新. 问题重现 因涉及到公司层面的模型.数据,本文都为测试模型和例子. 实体类: @Data @TableName("shop_item") public class ShopItem implements Serializable { private static final long serialVersionUID =

  • MyBatis-Plus通过插件将数据库表生成Entiry,Mapper.xml,Mapper.class的方式

    创建maven项目,修改pom.xml文件,如下: <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://mave

  • 完美解决MybatisPlus插件分页查询不起作用总是查询全部数据问题

    问题描述: 在使用mybatisplus插件进行分页查询时分页参数不起作用,总是查出来全部数据. 原因分析: 查看打印的sql日志发现sql后面并没有limit条件,怀疑是缺少配置. 解决方案: 查阅资料通过添加配置类MybatisPlusConfig解决问题: @Configuration public class MybatisPlusConfig { @Bean public PaginationInterceptor paginationInterceptor(){ return new

  • MyBatis Plus插件机制与执行流程原理分析详解

    MyBatis Plus插件 MyBatis Plus提供了分页插件PaginationInterceptor.执行分析插件SqlExplainInterceptor.性能分析插件PerformanceInterceptor以及乐观锁插件OptimisticLockerInterceptor. Mybatis 通过插件 (Interceptor) 可以做到拦截四大对象相关方法的执行 ,根据需求完成相关数据的动态改变. 四大对象是: Executor StatementHandler Parame

  • SpringMVC的执行流程及组件详解

    这篇文章主要介绍了SpringMVC的执行流程及组件详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 一.核心模块 数据库访问技术与集成:JDBC.XML等 Web与远程调用技术:SpringMVC.WebServlet.WebSocket等 面向切面编程:AOP 基础设施:Tomcat Spring核心容器:Beans.Core.Context.Expression.ContestSupport 测试:Test 二.执行流程 1.用户通过页

  • Java中注解与原理分析详解

    目录 一.注解基础 二.注解原理 三.常用注解 1.JDK注解 2.Lombok注解 四.自定义注解 1.同步控制 2.类型引擎 一.注解基础 注解即标注与解析,在Java的代码工程中,注解的使用几乎是无处不在,甚至多到被忽视: 无论是在JDK源码或者框架组件,都在使用注解能力完成各种识别和解析动作:在对系统功能封装时,也会依赖注解能力简化各种逻辑的重复实现: 基础接口 在Annotation的源码注释中有说明:所有的注解类型都需要继承该公共接口,本质上看注解是接口,但是代码并没有显式声明继承关

  • java方法及this关键字原理分析详解

    目录 步骤1 .给顾客增加一个吃饭的方法 步骤 2 . 没有加static的属性和方法,一定需要先new对象 步骤 3 . 用new出来的对象去执行eat方法 步骤 4 . 怎么理解c.eat() 步骤 5 . 消息接受器 步骤 6 . 如果有两个顾客? 步骤 7 . 答案 步骤 8 .其实有个this 步骤 9 . 在eat方法里面直接使用this 步骤 10 . 构造方法 步骤 11 . 总结:this的意义是什么? 步骤 12 . 道理我都懂,那static又是什么? 步骤 13 . 本节

  • Java CompletableFuture实现原理分析详解

    目录 简介 CompletableFuture类结构 CompletableFuture回调原理 CompletableFuture异步原理 总结 简介 前面的一篇文章你知道Java8并发新特性CompletableFuture吗?介绍了CompletableFuture的特性以及一些使用方法,今天我们主要来聊一聊CompletableFuture的回调功能以及异步工作原理是如何实现的. CompletableFuture类结构 1.CompletableFuture类结构主要有两个属性 pub

  • Spring Aop基本流程原理示例详解

    一.代理对象的创建过程: AbstractAutowireCapableBeanFactory#initializeBean protectedObjectinitializeBean(StringbeanName,Objectbean,@NullableRootBeanDefinitionmbd){ if(System.getSecurityManager()!=null){ AccessController.doPrivileged((PrivilegedAction<Object>)()

  • MySQL主从延迟现象及原理分析详解

    一.现象 凌晨对线上一张表添加索引,表数据量太大(1亿+数据,数据量50G以上),造成主从延迟几个小时,各个依赖从库的系统无法查询数据,最终影响业务. 现在就梳理下主从延迟的原理. 二.原理 根据 MySQL 官方文档 MySQL Replication Implementation Details 中的描述,MySQL 主从复制依赖于三个线程:master一个线程(Binlog dump thread),slave两个线程(I/O thread和SQL thread).主从复制流程如下图: m

  • Security 登录认证流程详细分析详解

    目录 一.前言:流程图: 二.前台发送请求 三.请求到达UsernamePasswordAuthenticationFilter过滤器 四.制作UsernamePasswordAuthenticationToken 五.父类中的 AuthenticationManager 对Token 进行认证 六.我们找到了AuthenticationManager 实现类ProviderManager 七.AuthenticationProvider接口 八.DaoAuthenticationProvide

  • MyBatis 执行动态 SQL语句详解

    大家基本上都知道如何使用 MyBatis 执行任意 SQL,使用方法很简单,例如在一个 XXMapper.xml 中: <select id="executeSql" resultType="map"> ${_parameter} </select> 你可以如下调用: sqlSession.selectList("executeSql", "select * from sysuser where enabled

  • Mybatis的一级缓存和二级缓存原理分析与使用

    目录 Mybatis的一级缓存和二级缓存 1 Mybatis如何判断两次查询是完全相同的查询 2 二级缓存 2.1 二级缓存配置 2.2 二级缓存特点 2.3 配置二级缓存 2.4 测试 Mybatis的一级缓存和二级缓存 Mybatis会将相同查询条件的SQL语句的查询结果存储在内存或者某种缓存介质中,当下次遇到相同的SQL时不执行该SQL,而是直接从缓存中获取结果,减少服务器的压力,尤其是在查询越多.缓存命中率越高的情况下,使用缓存对性能的提高更明显. Mybatis缓存分为一级缓存和二级缓

随机推荐