Spring Boot如何整合mybatisplus

文章目录

  • 1. 相关配置和代码
  • 2. 整合原理
    • 2.1 spring boot自动配置
    • 2.2 MybatisPlusAutoConfiguration
    • 2.3 debug流程
      • 2.3.1 MapperScannerRegistrar
      • 2.3.2MapperScannerConfigurer
      • 2.3.3 创建MybatisPlusAutoConfiguration
      • 2.3.4 创建sqlSessionFactory
      • 2.3.5 创建SqlSessionTemplate
      • 2.3.6 mapper接口代理对象创建

1. 相关配置和代码

  • maven

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0modelVersion>
    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>2.7.14version>
        <relativePath/> 
    parent>
    <groupId>com.bogegroupId>
    <artifactId>mybatisplus-spring-bootartifactId>
    <version>0.0.1-SNAPSHOTversion>
    <name>mybatis-spring-bootname>
    <description>mybatis-spring-bootdescription>
    <properties>
        <java.version>1.8java.version>
    properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>

        <dependency>
            <groupId>com.baomidougroupId>
            <artifactId>mybatis-plus-boot-starterartifactId>
            <version>3.5.3.1version>
        dependency>

        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>druid-spring-boot-starterartifactId>
            <version>1.2.18version>
        dependency>

        <dependency>
            <groupId>com.mysqlgroupId>
            <artifactId>mysql-connector-jartifactId>
            <scope>runtimescope>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>
        <dependency>
            <groupId>org.mybatis.spring.bootgroupId>
            <artifactId>mybatis-spring-boot-starter-testartifactId>
            <version>2.3.1version>
            <scope>testscope>
        dependency>
        
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <version>1.18.28version>
            <scope>providedscope>
        dependency>

    dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-maven-pluginartifactId>
            plugin>
        plugins>
    build>

project>

  • application.yml
server:
  port: 8000
spring:
  main:
    allow-circular-references: true
    allow-bean-definition-overriding: true
  application:
    name: freedom-code
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    # MYSQL 5 驱动:com.mysql.jdbc.Driver,MYSQL 6+ 驱动:com.mysql.cj.jdbc.Driver
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mybatis-vip?characterEncoding=utf-8&serverTimezone=UTC
    username: root
    password: sdfsaxs!
    druid:
      # 初始化大小,最小,最大
      initial-size: 5
      min-idle: 5
      max-active: 100
      # 配置获取连接等待超时的时间
      max-wait: 60000
      # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位毫秒
      time-between-eviction-runs-millis: 60000
      # 配置一个连接在池中最小生存时间
      min-evictable-idle-time-millis: 300000
      validation-query: select VERSION()
      test-while-idle: true
      test-on-borrow: false
      test-on-return: false

# mybatisplus的配置
mybatis-plus:
  configuration:
    # 是否开启自动驼峰命名规则
    map-underscore-to-camel-case: true
    # 对所有的 resultMap 都进行自动映射
    auto-mapping-behavior: full
    # #控制台打印完整带参数SQL语句
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  mapper-locations: classpath:mapper/**/*.xml
  type-aliases-package: "com.boge.pojo"
@Mapper
public interface UserMapper extends BaseMapper<UserDO> {
    List<User> query();
}

@Data
@TableName("t_user")
public class UserDO implements Serializable {
    @TableId(type = IdType.ASSIGN_ID)
    private Integer id;

    private String userName;

    private String password;

    private String realName;

    private Integer age;

    private Integer dId;

}

@MapperScan("com.boge.mapper")
@SpringBootApplication
public class MybatisSpringBootApplication {

    public static void main(String[] args) {
        SpringApplication.run(MybatisSpringBootApplication.class, args);
    }

}

2. 整合原理

关注三个对象的创建:

  • SqlSessionFactory如何创建
  • SqlSession如何创建
  • Mapper接口的代理类如何创建(比如UserMapper)。

2.1 spring boot自动配置

Spring Boot如何整合mybatisplus_第1张图片

2.2 MybatisPlusAutoConfiguration

源码


/**
 * {@link EnableAutoConfiguration Auto-Configuration} for Mybatis. Contributes a
 * {@link SqlSessionFactory} and a {@link SqlSessionTemplate}.
 * 

* If {@link org.mybatis.spring.annotation.MapperScan} is used, or a * configuration file is specified as a property, those will be considered, * otherwise this auto-configuration will attempt to register mappers based on * the interface definitions in or under the root auto-configuration package. *

*

copy from {@link org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration}

* * @author Eddú Meléndez * @author Josh Long * @author Kazuki Shimizu * @author Eduardo Macarrón */
@Configuration(proxyBeanMethods = false) @ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class}) @ConditionalOnSingleCandidate(DataSource.class) @EnableConfigurationProperties(MybatisPlusProperties.class) @AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisPlusLanguageDriverAutoConfiguration.class}) public class MybatisPlusAutoConfiguration implements InitializingBean { private static final Logger logger = LoggerFactory.getLogger(MybatisPlusAutoConfiguration.class); private final MybatisPlusProperties properties; private final Interceptor[] interceptors; private final TypeHandler[] typeHandlers; private final LanguageDriver[] languageDrivers; private final ResourceLoader resourceLoader; private final DatabaseIdProvider databaseIdProvider; private final List<ConfigurationCustomizer> configurationCustomizers; private final List<SqlSessionFactoryBeanCustomizer> sqlSessionFactoryBeanCustomizers; private final List<MybatisPlusPropertiesCustomizer> mybatisPlusPropertiesCustomizers; private final ApplicationContext applicationContext; public MybatisPlusAutoConfiguration(MybatisPlusProperties properties, ObjectProvider<Interceptor[]> interceptorsProvider, ObjectProvider<TypeHandler[]> typeHandlersProvider, ObjectProvider<LanguageDriver[]> languageDriversProvider, ResourceLoader resourceLoader, ObjectProvider<DatabaseIdProvider> databaseIdProvider, ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider, ObjectProvider<List<SqlSessionFactoryBeanCustomizer>> sqlSessionFactoryBeanCustomizers, ObjectProvider<List<MybatisPlusPropertiesCustomizer>> mybatisPlusPropertiesCustomizerProvider, ApplicationContext applicationContext) { 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(); this.sqlSessionFactoryBeanCustomizers = sqlSessionFactoryBeanCustomizers.getIfAvailable(); this.mybatisPlusPropertiesCustomizers = mybatisPlusPropertiesCustomizerProvider.getIfAvailable(); this.applicationContext = applicationContext; } @Override public void afterPropertiesSet() { if (!CollectionUtils.isEmpty(mybatisPlusPropertiesCustomizers)) { mybatisPlusPropertiesCustomizers.forEach(i -> i.customize(properties)); } 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)"); } } @Bean @ConditionalOnMissingBean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { // TODO 使用 MybatisSqlSessionFactoryBean 而不是 SqlSessionFactoryBean MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean(); factory.setDataSource(dataSource); factory.setVfs(SpringBootVFS.class); if (StringUtils.hasText(this.properties.getConfigLocation())) { factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation())); } applyConfiguration(factory); if (this.properties.getConfigurationProperties() != null) { factory.setConfigurationProperties(this.properties.getConfigurationProperties()); } if (!ObjectUtils.isEmpty(this.interceptors)) { factory.setPlugins(this.interceptors); } if (this.databaseIdProvider != null) { factory.setDatabaseIdProvider(this.databaseIdProvider); } if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) { factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage()); } if (this.properties.getTypeAliasesSuperType() != null) { factory.setTypeAliasesSuperType(this.properties.getTypeAliasesSuperType()); } if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) { factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage()); } if (!ObjectUtils.isEmpty(this.typeHandlers)) { factory.setTypeHandlers(this.typeHandlers); } if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) { factory.setMapperLocations(this.properties.resolveMapperLocations()); } // TODO 修改源码支持定义 TransactionFactory this.getBeanThen(TransactionFactory.class, factory::setTransactionFactory); // TODO 对源码做了一定的修改(因为源码适配了老旧的mybatis版本,但我们不需要适配) Class<? extends LanguageDriver> defaultLanguageDriver = this.properties.getDefaultScriptingLanguageDriver(); if (!ObjectUtils.isEmpty(this.languageDrivers)) { factory.setScriptingLanguageDrivers(this.languageDrivers); } Optional.ofNullable(defaultLanguageDriver).ifPresent(factory::setDefaultScriptingLanguageDriver); applySqlSessionFactoryBeanCustomizers(factory); // TODO 此处必为非 NULL GlobalConfig globalConfig = this.properties.getGlobalConfig(); // TODO 注入填充器 this.getBeanThen(MetaObjectHandler.class, globalConfig::setMetaObjectHandler); // TODO 注入参与器 this.getBeanThen(PostInitTableInfoHandler.class, globalConfig::setPostInitTableInfoHandler); // TODO 注入主键生成器 this.getBeansThen(IKeyGenerator.class, i -> globalConfig.getDbConfig().setKeyGenerators(i)); // TODO 注入sql注入器 this.getBeanThen(ISqlInjector.class, globalConfig::setSqlInjector); // TODO 注入ID生成器 this.getBeanThen(IdentifierGenerator.class, globalConfig::setIdentifierGenerator); // TODO 设置 GlobalConfig 到 MybatisSqlSessionFactoryBean factory.setGlobalConfig(globalConfig); return factory.getObject(); } /** * 检查spring容器里是否有对应的bean,有则进行消费 * * @param clazz class * @param consumer 消费 * @param 泛型 */ private <T> void getBeanThen(Class<T> clazz, Consumer<T> consumer) { if (this.applicationContext.getBeanNamesForType(clazz, false, false).length > 0) { consumer.accept(this.applicationContext.getBean(clazz)); } } /** * 检查spring容器里是否有对应的bean,有则进行消费 * * @param clazz class * @param consumer 消费 * @param 泛型 */ private <T> void getBeansThen(Class<T> clazz, Consumer<List<T>> consumer) { if (this.applicationContext.getBeanNamesForType(clazz, false, false).length > 0) { final Map<String, T> beansOfType = this.applicationContext.getBeansOfType(clazz); List<T> clazzList = new ArrayList<>(); beansOfType.forEach((k, v) -> clazzList.add(v)); consumer.accept(clazzList); } } // TODO 入参使用 MybatisSqlSessionFactoryBean private void applyConfiguration(MybatisSqlSessionFactoryBean factory) { // TODO 使用 MybatisConfiguration MybatisConfiguration configuration = this.properties.getConfiguration(); if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) { configuration = new MybatisConfiguration(); } if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) { for (ConfigurationCustomizer customizer : this.configurationCustomizers) { customizer.customize(configuration); } } factory.setConfiguration(configuration); } private void applySqlSessionFactoryBeanCustomizers(MybatisSqlSessionFactoryBean factory) { if (!CollectionUtils.isEmpty(this.sqlSessionFactoryBeanCustomizers)) { for (SqlSessionFactoryBeanCustomizer customizer : this.sqlSessionFactoryBeanCustomizers) { customizer.customize(factory); } } } @Bean @ConditionalOnMissingBean public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { ExecutorType executorType = this.properties.getExecutorType(); if (executorType != null) { return new SqlSessionTemplate(sqlSessionFactory, executorType); } else { return new SqlSessionTemplate(sqlSessionFactory); } } /** * This will just scan the same base package as Spring Boot does. If you want more power, you can explicitly use * {@link org.mybatis.spring.annotation.MapperScan} but this will get typed mappers working correctly, out-of-the-box, * similar to using Spring Data JPA repositories. */ public static class AutoConfiguredMapperScannerRegistrar implements BeanFactoryAware, EnvironmentAware, ImportBeanDefinitionRegistrar { private BeanFactory beanFactory; private Environment environment; @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { if (!AutoConfigurationPackages.has(this.beanFactory)) { logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled."); return; } logger.debug("Searching for mappers annotated with @Mapper"); List<String> packages = AutoConfigurationPackages.get(this.beanFactory); if (logger.isDebugEnabled()) { packages.forEach(pkg -> logger.debug("Using auto-configuration base package '{}'", pkg)); } BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class); builder.addPropertyValue("processPropertyPlaceHolders", true); builder.addPropertyValue("annotationClass", Mapper.class); builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(packages)); BeanWrapper beanWrapper = new BeanWrapperImpl(MapperScannerConfigurer.class); Set<String> propertyNames = Stream.of(beanWrapper.getPropertyDescriptors()).map(PropertyDescriptor::getName) .collect(Collectors.toSet()); if (propertyNames.contains("lazyInitialization")) { // Need to mybatis-spring 2.0.2+ // TODO 兼容了mybatis.lazy-initialization配置 builder.addPropertyValue("lazyInitialization", "${mybatis-plus.lazy-initialization:${mybatis.lazy-initialization:false}}"); } if (propertyNames.contains("defaultScope")) { // Need to mybatis-spring 2.0.6+ builder.addPropertyValue("defaultScope", "${mybatis-plus.mapper-default-scope:}"); } // for spring-native boolean injectSqlSession = environment.getProperty("mybatis.inject-sql-session-on-mapper-scan", Boolean.class, Boolean.TRUE); if (injectSqlSession && this.beanFactory instanceof ListableBeanFactory) { ListableBeanFactory listableBeanFactory = (ListableBeanFactory) this.beanFactory; Optional<String> sqlSessionTemplateBeanName = Optional .ofNullable(getBeanNameForType(SqlSessionTemplate.class, listableBeanFactory)); Optional<String> sqlSessionFactoryBeanName = Optional .ofNullable(getBeanNameForType(SqlSessionFactory.class, listableBeanFactory)); if (sqlSessionTemplateBeanName.isPresent() || !sqlSessionFactoryBeanName.isPresent()) { builder.addPropertyValue("sqlSessionTemplateBeanName", sqlSessionTemplateBeanName.orElse("sqlSessionTemplate")); } else { builder.addPropertyValue("sqlSessionFactoryBeanName", sqlSessionFactoryBeanName.get()); } } builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition()); } @Override public void setBeanFactory(BeanFactory beanFactory) { this.beanFactory = beanFactory; } @Override public void setEnvironment(Environment environment) { this.environment = environment; } private String getBeanNameForType(Class<?> type, ListableBeanFactory factory) { String[] beanNames = factory.getBeanNamesForType(type); return beanNames.length > 0 ? beanNames[0] : null; } } /** * If mapper registering configuration or mapper scanning configuration not present, this configuration allow to scan * mappers based on the same component-scanning path as Spring Boot itself. */ @org.springframework.context.annotation.Configuration @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."); } } @Order @Bean @ConditionalOnMissingBean public DdlApplicationRunner ddlApplicationRunner(@Autowired(required = false) List<IDdl> ddlList) { if (ObjectUtils.isEmpty(ddlList)) { return null; } return new DdlApplicationRunner(ddlList); } }

2.3 debug流程

2.3.1 MapperScannerRegistrar

主要为MapperScannerConfigurer定制beanDefinition
Spring Boot如何整合mybatisplus_第2张图片

Spring Boot如何整合mybatisplus_第3张图片

2.3.2MapperScannerConfigurer

MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor。

方法postProcessBeanDefinitionRegistry可以修改beanDefinition信息。为mapper接口定制beanDefinition。

Spring Boot如何整合mybatisplus_第4张图片
Spring Boot如何整合mybatisplus_第5张图片
Spring Boot如何整合mybatisplus_第6张图片
Spring Boot如何整合mybatisplus_第7张图片

2.3.3 创建MybatisPlusAutoConfiguration

Spring Boot如何整合mybatisplus_第8张图片

2.3.4 创建sqlSessionFactory

这里用MybatisSqlSessionFactoryBean来创建sqlSessionFactory。
Spring Boot如何整合mybatisplus_第9张图片
Spring Boot如何整合mybatisplus_第10张图片
Spring Boot如何整合mybatisplus_第11张图片
重点看buildSqlSessionFactory方法
Spring Boot如何整合mybatisplus_第12张图片
Spring Boot如何整合mybatisplus_第13张图片
Spring Boot如何整合mybatisplus_第14张图片

2.3.5 创建SqlSessionTemplate

Spring Boot如何整合mybatisplus_第15张图片

2.3.6 mapper接口代理对象创建

Spring Boot如何整合mybatisplus_第16张图片
打断点到MybatisMapperRegistry
Spring Boot如何整合mybatisplus_第17张图片
Spring Boot如何整合mybatisplus_第18张图片

你可能感兴趣的:(#,MyBatis,spring,boot,后端,java)