SpringBoot 2.1.2 + MyBatis-Plus 3.1.1 + Druid 1.1.21搭建双数据源

1. 前言

由于项目需要同时连接多个数据源,通过查询网上的各种帖子搭建出来的框架出现了各种各样的问题,最后将这些问题一一解决后终于搭建出了想要的双数据源效果,在这里留作记录。
使用的环境:
数据库:MySql 8.0.16
SpringBoot版本:2.1.2.RELEASE
MyBatis-Plus版本:3.1.1
Druid版本:1.1.21
Maven仓库:https://repo1.maven.org/maven2/
Java版本:1.8

2. Maven依赖

说明:
1、已经剔除多余的依赖配置。
2、由于使用了jdk1.8,时间格式推荐使用LocalDateTime,根据网上的说法Druid需要使用1.1.18以上版本才会支持,但是我使用1.1.18版本的时候同样会报不支持LocalDateTime类型,索性使用了当前最新版本1.1.21就好了。

<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.st.springboot</groupId>
    <artifactId>framework-java</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>

    <name>framework-java</name>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <!-- 继承父包 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.2.RELEASE</version>
        <relativePath></relativePath>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <!-- 如要打成war包部署在容器上,将下面依赖解开 -->
        <!-- <dependency>
               <groupId>org.springframework.boot</groupId>
               <artifactId>spring-boot-starter-tomcat</artifactId>
               <scope>provided</scope>
        </dependency>-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.1.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <!-- druid阿里巴巴数据库连接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.21</version>
        </dependency>
        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!--mybatis plus代码生成器-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.1.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
            <version>2.0</version>
        </dependency>
    </dependencies>

    <!--maven的插件-->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>

        <!-- 配置java版本 不配置的话默认父类配置的是1.6-->
        <pluginManagement>
            <plugins>
                <plugin>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>
</project>

3. application.yml配置

说明:
1、由于Mysql使用的8.0版本,所以驱动使用了com.mysql.cj.jdbc.Driver, 5.X版本驱动请使用com.mysql.jdbc.Driver
2、双数据源需要在SpringBoot个性配置代码中手动配置DataSource,因此这边自定义了配置层次spring.datasource.database1spring.datasource.database2,Druid的相关配置项也都在这一级定义,后面在配置类中使用直接使用DruidDataSourceBuilder就可以自动识别出来(见后面的标题5.6 MybatisPlusConfig.java)。

server:
  port: 8080
  session-timeout: 30
  tomcat:
    uri-encoding: UTF-8
  servlet:
    context-path: /framework-java
#spring
spring:
  http:
    encoding:
      charset: UTF-8
      enabled: true
      force: true
  aop:
    proxy-target-class: true
  #datasource config
  datasource:
    database1:
      url: jdbc:mysql://localhost:3306/sample1?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&autoReconnect=true&failOverReadOnly=false&serverTimezone=GMT%2B8
      username: sample1
      password: sample1
      driver-class-name: com.mysql.cj.jdbc.Driver
      initial-size: 5  # 初始化大小
      min-idle: 5  # 最小
      max-active: 100  # 最大
      max-wait: 60000  # 连接超时时间
      time-between-eviction-runs-millis: 60000  # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
      min-evictable-idle-time-millis: 300000  # 指定一个空闲连接最少空闲多久后可被清除,单位是毫秒
      validationQuery: select 'x'
      test-while-idle: true  # 当连接空闲时,是否执行连接测试
      test-on-borrow: false  # 当从连接池借用连接时,是否测试该连接
      test-on-return: false  # 在连接归还到连接池时是否测试该连接
      filters: config,wall,stat  # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
      poolPreparedStatements: true
      maxPoolPreparedStatementPerConnectionSize: 20
      maxOpenPreparedStatements: 20
      connectionProperties: druid.stat.slowSqlMillis=200;druid.stat.logSlowSql=true;config.decrypt=false
    database2:
      url: jdbc:mysql://localhost:3306/sample2?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&autoReconnect=true&failOverReadOnly=false&serverTimezone=GMT%2B8
      username: sample2
      password: sample2
      driver-class-name: com.mysql.cj.jdbc.Driver
      initial-size: 5  # 初始化大小
      min-idle: 5  # 最小
      max-active: 100  # 最大
      max-wait: 60000  # 连接超时时间
      time-between-eviction-runs-millis: 60000  # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
      min-evictable-idle-time-millis: 300000  # 指定一个空闲连接最少空闲多久后可被清除,单位是毫秒
      validationQuery: select 'x'
      test-while-idle: true  # 当连接空闲时,是否执行连接测试
      test-on-borrow: false  # 当从连接池借用连接时,是否测试该连接
      test-on-return: false  # 在连接归还到连接池时是否测试该连接
      filters: config,wall,stat  # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
      poolPreparedStatements: true
      maxPoolPreparedStatementPerConnectionSize: 20
      maxOpenPreparedStatements: 20
      connectionProperties: druid.stat.slowSqlMillis=200;druid.stat.logSlowSql=true;config.decrypt=false

4. SpringBoot Web个性化配置

4.1 MyWebMvcConfigurerAdapter.java

说明:
这里都是和SpringBoot相关的配置,与双数据源本身没有关系,为了完整分享代码,就顺便贴出来了。

package com.st.springboot.config;

import com.st.springboot.interceptor.DefaultInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.config.annotation.*;

import java.nio.charset.Charset;
import java.util.List;

/**
 * 关于springboot的一些个性化设置在这里配置
 * @author ~孑然妒火~
 *
 */
@Configuration
public class MyWebMvcConfigurerAdapter extends WebMvcConfigurationSupport {
	
	/**
	 * 设置request UTF-8中文过滤
	 * 方法描述.
	 * @return
	 */
	@Bean
	public CharacterEncodingFilter characterEncodingFilter() {
	    CharacterEncodingFilter filter = new CharacterEncodingFilter();
	    filter.setEncoding("UTF-8");
	    filter.setForceEncoding(true);
	    return filter;
	}
	
	/**
	 * 设置response UTF-8中文过滤
	 * 方法描述.
	 * @return
	 */
	@Bean
    public HttpMessageConverter<String> responseBodyConverter() {
        StringHttpMessageConverter converter = new StringHttpMessageConverter(
                Charset.forName("UTF-8"));
        return converter;
    }

    @Override
    public void configureMessageConverters(
            List<HttpMessageConverter<?>> converters) {
        super.configureMessageConverters(converters);
        converters.add(responseBodyConverter());
    }

    @Override
    public void configureContentNegotiation(
            ContentNegotiationConfigurer configurer) {
        configurer.favorPathExtension(false);
    }
	
	/**
	 * 配置自定义静态访问目录
	 * @param registry
	 */
	@Override
	public void addResourceHandlers(ResourceHandlerRegistry registry){
		registry.addResourceHandler("/view/**").addResourceLocations("classpath:/view/");
		super.addResourceHandlers(registry);
	}
	
	/**
	 * 如果有些controller只需要执行跳转而不需要在后台执行任何业务,那跳转地址可以在这里配置,controller里就可以不用为这个路径跳转业务单独写空白方法了
	 * 例:registry.addViewController("/toLogin").setViewName("login");当请求/toLogin的时候即跳转到login.html
	 */
	@Override
	public void addViewControllers(ViewControllerRegistry registry){
		super.addViewControllers(registry);
	}
	
	/**
	 * 配置spring拦截器
	 */
	@Override
	public void addInterceptors(InterceptorRegistry registry){
		//addPathPatterns增加拦截路径,不限制参数个数,多个路径参数以英文逗号间隔
		//excludePathPatterns 忽略的拦截路径,不限制参数个数,多个路径参数以英文逗号间隔
		registry.addInterceptor(new DefaultInterceptor()).addPathPatterns("/**").excludePathPatterns("/toLogin","/login");
		super.addInterceptors(registry);
	}
}

5. Mybatis-Plus多数据源配置

5.1 定义枚举类 DataSourceEnum.java

说明:
通过枚举类定义出项目中使用的所有数据源的名称,枚举值可以自定义,这里起名为DataSourceFirstDataSourceSecond

package com.st.springboot.config;

public enum DataSourceEnum {

    DataSourceFirst("DataSourceFirst"),DataSourceSecond("DataSourceSecond");

    private String value;

    DataSourceEnum(String value){
        this.value=value;
    }

    public String getValue() {
        return value;
    }
}

5.2 定义注解 DataSource.java

说明:
在Spring中,多数据源往往是通过AOP方式捕获指定的Service方法名来动态切换当前使用的数据源,在这里也是一样的,后面会使用AOP捕获Service的自定义注解来动态切换数据源

package com.st.springboot.config;

import java.lang.annotation.*;

@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {

    DataSourceEnum value() default DataSourceEnum.DataSourceFirst;

}

5.3 定义当前数据源容器 DataSourceContextHolder.java

package com.st.springboot.config;

public class DataSourceContextHolder {

    private static final ThreadLocal<String> contextHolder = new InheritableThreadLocal<>();

    /**
     *  设置数据源
     * @param db
     */
    public static void setDataSource(String db){
        contextHolder.set(db);
    }

    /**
     * 取得当前数据源
     * @return
     */
    public static String getDataSource(){
        return contextHolder.get();
    }

    /**
     * 清除上下文数据
     */
    public static void clear(){
        contextHolder.remove();
    }
}

5.4 定义动态数据源 MultipleDataSource.java

说明:
我后面会通过向SqlSessionFactory中放入AbstractRoutingDataSource类型的数据源来实现将多个数据源放入数据源会话工厂,这里对AbstractRoutingDataSource做一个实现,意思是将动态切换后的数据源标识设置为当前使用的数据源,具体的原理可以通过搜索AbstractRoutingDataSource相关资料了解。

package com.st.springboot.config;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class MultipleDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDataSource();
    }

}

5.5 定义数据源切换AOP配置类 DataSourceAspect.java

说明:
前面已经定义了DataSource注解,这里定义AOP配置,根据方法上的注解内容动态切换DataSourceContextHolder中的数据源标识。

package com.st.springboot.config;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * 数据源动态切换AOP
 */
@Component
@Aspect
@Order(-1)
public class DataSourceAspect {

    @Pointcut("@within(com.st.springboot.config.DataSource) || @annotation(com.st.springboot.config.DataSource)")
    public void pointCut(){

    }

    @Before("pointCut() && @annotation(dataSource)")
    public void doBefore(DataSource dataSource){
        //LoggerUtil.debug("选择数据源---{}", dataSource.value().getValue());
        DataSourceContextHolder.setDataSource(dataSource.value().getValue());
    }

    @After("pointCut()")
    public void doAfter(){
        DataSourceContextHolder.clear();
    }
}

5.6 定义MyBatis-Plus 配置类 MybatisPlusConfig.java

说明:
这里便是MyBatis-Plus的核心配置了
1、数据源通过DruidDataSourceBuilder读取application.yml的指定配置项直接构建。
2、使用上面定义的MultipleDataSource作为放入数据源工厂中的数据源类型。
3、使用DataSourceEnum枚举类中的枚举值作为MultipleDataSource的数据源标识。
4、其他的MyBatis-Plus配置可在这里自行添加。
5、不要忘了在类名上添加@Configuration注解,否则SpringBoot不会识别。

package com.st.springboot.config;

import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.baomidou.mybatisplus.annotation.FieldStrategy;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.type.JdbcType;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class MybatisPlusConfig {
    /***
     * plus 的性能优化
     * @return
     */
//    @Bean
//    public PerformanceInterceptor performanceInterceptor() {
//        PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
//        /*<!-- SQL 执行性能分析,开发环境使用,线上不推荐。 maxTime 指的是 sql 最大执行时长 -->*/
//        //performanceInterceptor.setMaxTime(1000);
//        /*<!--SQL是否格式化 默认false-->*/
//        performanceInterceptor.setWriteInLog(false);
//        performanceInterceptor.setFormat(false);
//        return performanceInterceptor;
//    }

    /**
     * @Description : mybatis-plus分页插件
     * ---------------------------------
     * @Author : Liang.Guangqing
     * @Date : Create in 2017/9/19 13:59
     */
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor();
    }

    /**
     * 多数据源配置
     * 数据源1
     * @return
     */
    @Bean(name = "DataSourceFirst")
    @ConfigurationProperties(prefix = "spring.datasource.database1" )
    public DataSource dataSourceFirst(){
        return DruidDataSourceBuilder.create().build();
    }
    /**
     * 多数据源配置
     * 数据源2
     * @return
     */
    @Bean(name = "DataSourceSecond")
    @ConfigurationProperties(prefix = "spring.datasource.database2" )
    public DataSource dataSourceSecond(){
        return DruidDataSourceBuilder.create().build();
    }

    /**
     * 动态数据源配置
     * @return
     */
    @Bean
    @Primary
    public DataSource multipleDataSource(@Qualifier("DataSourceFirst") DataSource dataSourceFirst, @Qualifier("DataSourceSecond") DataSource dataSourceSecond) {
        MultipleDataSource multipleDataSource = new MultipleDataSource();
        Map< Object, Object > targetDataSources = new HashMap<>();
        targetDataSources.put(DataSourceEnum.DataSourceFirst.getValue(), dataSourceFirst);
        targetDataSources.put(DataSourceEnum.DataSourceSecond.getValue(), dataSourceSecond);
        //添加数据源
        multipleDataSource.setTargetDataSources(targetDataSources);
        //设置默认数据源
        multipleDataSource.setDefaultTargetDataSource(dataSourceFirst);
        return multipleDataSource;
    }

    @Bean("sqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();
        //将AbstractRoutingDataSource的实现作为DataSource放入工厂
        sqlSessionFactory.setDataSource(multipleDataSource(dataSourceFirst(),dataSourceSecond()));
        sqlSessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/**Mapper.xml"));
        sqlSessionFactory.setTypeAliasesPackage("com.st.springboot.**.entity");
        MybatisConfiguration configuration = new MybatisConfiguration();
        //configuration.setDefaultScriptingLanguage(MybatisXMLLanguageDriver.class);
        configuration.setJdbcTypeForNull(JdbcType.NULL);
        configuration.setMapUnderscoreToCamelCase(true);
        configuration.setCacheEnabled(false);
        sqlSessionFactory.setConfiguration(configuration);
        sqlSessionFactory.setPlugins(new Interceptor[]{ //PerformanceInterceptor(),OptimisticLockerInterceptor()
                paginationInterceptor() //添加分页功能
        });
        sqlSessionFactory.setGlobalConfig(mpGlobalConfig());
        return sqlSessionFactory.getObject();
    }

    /**
     * mybatis-plus 全局配置
     * @return
     */
    public GlobalConfig mpGlobalConfig() {
        // 全局配置文件
        GlobalConfig globalConfig = new GlobalConfig();
        GlobalConfig.DbConfig dbConfig = new GlobalConfig.DbConfig();
        // 默认为自增,这里配置为UUID
        dbConfig.setIdType(IdType.UUID);
        dbConfig.setFieldStrategy(FieldStrategy.NOT_NULL);
        dbConfig.setLogicDeleteValue("0");
        dbConfig.setLogicNotDeleteValue("1");
        globalConfig.setDbConfig(dbConfig);
        return globalConfig;
    }
}

6. Druid个性化配置 DruidConfiguration.java

说明:
由于Druid的数据源在前面使用了DruidDataSourceBuilder读取,yml中没有使用SpringBoot的标准格式配置,所以Druid的个性配置就也只能通过Configuration类来实现了,这里配置了Druid系统监控页面的相关内容。
同样不要忘了@Configuration注解。

package com.st.springboot.config;

import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class DruidConfiguration {

    @Bean
    public ServletRegistrationBean startViewServlet(){
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(),"/druid/*");
        // IP白名单
        //servletRegistrationBean.addInitParameter("allow","127.0.0.1");
        // IP黑名单(共同存在时,deny优先于allow)
       // servletRegistrationBean.addInitParameter("deny","127.0.0.1");
        //控制台管理用户
        servletRegistrationBean.addInitParameter("loginUsername","admin");
        servletRegistrationBean.addInitParameter("loginPassword","ILoveHatsuneMiku");
        //是否能够重置数据
        servletRegistrationBean.addInitParameter("resetEnable","false");
        return servletRegistrationBean;
    }

    @Bean
    public FilterRegistrationBean statFilter(){
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter());
        //添加过滤规则
        filterRegistrationBean.addUrlPatterns("/*");
        //忽略过滤的格式
        filterRegistrationBean.addInitParameter("exclusions","*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
        return filterRegistrationBean;
    }
}

7. SpringBoot主类 Application.java

这个不多说了,运行少不了的东西。

package com.st.springboot;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;

@SpringBootApplication
@EnableAsync
@MapperScan("com.st.springboot.**.mapper")
public class Application {

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

	}

}

8. 代码生成器

代码生成器也是MyBatis-Plus的一个重要组成部分,这里使用的是默认的代码生成器,直接贴代码好了。
配置好数据源和表信息,直接运行即可。

package generator;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.converts.MySqlTypeConvert;
import com.baomidou.mybatisplus.generator.config.po.TableFill;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;

import java.io.File;
import java.util.*;

/**
 * MyBatis Plus代码生成器
 */
public class MysqlGenerator {
    private static String packageName="modules.sample";    //文件路径
    private static String authorName="~孑然妒火~";     //作者
    private static String table="t_module_sample";                  //table名字
    private static String prefix="t_module_";                     //table前缀
    private static File file = new File(packageName);
    private static String path = file.getAbsolutePath();

    public static void main(String[] args) {
        // 自定义需要填充的字段
        List<TableFill> tableFillList = new ArrayList<>();
        //tableFillList.add(new TableFill("ASDD_SS", FieldFill.INSERT_UPDATE));
        // 代码生成器
        AutoGenerator mpg = new AutoGenerator().setGlobalConfig(
                // 全局配置
                new GlobalConfig()
                        .setOutputDir(path+"/src/main/java")//输出目录
                        .setFileOverride(true)// 是否覆盖文件
                        .setActiveRecord(true)// 开启 activeRecord 模式
                        .setEnableCache(false)// XML 二级缓存
                        .setBaseResultMap(true)// XML ResultMap
                        .setBaseColumnList(true)// XML columList
                        .setOpen(false)//生成后打开文件夹
                        .setAuthor(authorName)
                        // 自定义文件命名,注意 %s 会自动填充表实体属性!
                        .setMapperName("%sMapper")
                        .setXmlName("%sMapper")
                        .setServiceName("%sService")
                        .setServiceImplName("%sServiceImpl")
                        .setControllerName("%sController")
        ).setDataSource(
                // 数据源配置
                new DataSourceConfig()
                        .setDbType(DbType.MYSQL)// 数据库类型
                        .setTypeConvert(new MySqlTypeConvert() {
                            // 自定义数据库表字段类型转换【可选】
                        })
                        .setDriverName("com.mysql.cj.jdbc.Driver")
                        .setUsername("sample1")
                        .setPassword("sample1")
                        .setUrl("jdbc:mysql://localhost:3306/sample1?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&autoReconnect=true&failOverReadOnly=false&serverTimezone=GMT%2B8")
        ).setStrategy(
                // 策略配置
                new StrategyConfig()
                        // .setCapitalMode(true)// 全局大写命名
                        //.setDbColumnUnderline(true)//全局下划线命名
                        .setTablePrefix(new String[]{prefix})// 此处可以修改为您的表前缀
                        .setNaming(NamingStrategy.underline_to_camel)// 表名生成策略
                        .setInclude(new String[] { table }) // 需要生成的表
                        .setRestControllerStyle(true)
                        //.setExclude(new String[]{"test"}) // 排除生成的表
                        // 自定义实体父类
                        // .setSuperEntityClass("com.baomidou.demo.TestEntity")
                        // 自定义实体,公共字段
                        //.setSuperEntityColumns(new String[]{"test_id"})
                        .setTableFillList(tableFillList)
                        // 自定义 mapper 父类
                        //.setSuperMapperClass("com.st.springboot.base.BaseDao")
                        // 自定义 service 父类
                         //.setSuperServiceClass("com.st.springboot.base.BaseService")
                        // 自定义 service 实现类父类
                        //.setSuperServiceImplClass("com.st.springboot.base.BaseServiceImpl")
                        // 自定义 controller 父类
                        //.setSuperControllerClass("com.st.springboot.base.BaseController")
                // 【实体】是否生成字段常量(默认 false)
                // public static final String ID = "test_id";
                // .setEntityColumnConstant(true)
                // 【实体】是否为构建者模型(默认 false)
                // public User setName(String name) {this.name = name; return this;}
                // .setEntityBuilderModel(true)
                // 【实体】是否为lombok模型(默认 false)<a href="https://projectlombok.org/">document</a>
                // .setEntityLombokModel(true)
                // Boolean类型字段是否移除is前缀处理
                // .setEntityBooleanColumnRemoveIsPrefix(true)
                // .setRestControllerStyle(true)
                // .setControllerMappingHyphenStyle(true)
        ).setPackageInfo(
                // 包配置
                new PackageConfig()
                        //.setModuleName("User")
                        .setParent("com.st.springboot."+packageName)// 自定义包路径
                        .setController("controller")// 这里是控制器包名,默认 web
                        .setEntity("entity")
                        .setMapper("mapper")
                        .setService("service")
                        .setServiceImpl("service.impl")
                //.setXml("mapper")
        ).setCfg(
                // 注入自定义配置,可以在 VM 中使用 cfg.abc 设置的值
                new InjectionConfig() {
                    @Override
                    public void initMap() {
                        Map<String, Object> map = new HashMap<>();
                        map.put("abc", this.getConfig().getGlobalConfig().getAuthor() + "-mp");
                        this.setMap(map);
                    }
                }.setFileOutConfigList(Collections.<FileOutConfig>singletonList(new FileOutConfig("/templates/mapper.xml.vm") {
                    // 自定义输出文件目录
                    @Override
                    public String outputFile(TableInfo tableInfo) {
                        return path+"/src/main/resources/mapper/" + tableInfo.getEntityName() + "Mapper.xml";
                    }
                }))
        ).setTemplate(
                // 关闭默认 xml 生成,调整生成 至 根目录
                new TemplateConfig().setXml(null)
                // 自定义模板配置,模板可以参考源码 /mybatis-plus/src/main/resources/template 使用 copy
                // 至您项目 src/main/resources/template 目录下,模板名称也可自定义如下配置:
                //.setController("/template/controller.java.vm")
                //.setEntity("/template/entity.java.vm")
                //.setMapper("/template/mapper.java.vm")
                //.setXml("/template/mapper.xml.vm")
                //.setService("/template/service.java.vm")
                //.setServiceImpl("/template/serviceImpl.java.vm")
        );

        // 执行生成
        mpg.execute();

        // 打印注入设置,这里演示模板里面怎么获取注入内容【可无】
        System.err.println(mpg.getCfg().getMap().get("abc"));
    }


}

9. 业务代码编写

这里主要介绍一下实现数据源切换的Service层代码的写法,注意注解的使用即可

9.1 使用主数据源

说明:
MyBatis-Plus内部已经实现了针对单表操作的常用服务,如果使用主数据源的话,就按照正常的方式编写代码即可,无需关注数据源切换问题。

package com.st.springboot.modules.sample.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.st.springboot.modules.sample.entity.Sample;
import com.st.springboot.modules.sample.mapper.SampleMapper;
import com.st.springboot.modules.sample.service.SampleService;
import org.springframework.stereotype.Service;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author ~孑然妒火~
 * @since 2018-08-12
 */
@Service
public class SampleServiceImpl extends ServiceImpl<SampleMapper, Sample> implements SampleService {

}

9.2 使用第二数据源

说明:
如果使用第二数据源,只需要在service方法上增加@DataSource(DataSourceEnum.DataSourceSecond)注解即可,如果使用了MyBatis-Plus内部实现的方法,像我这样将父类的方法重写然后加上注解就可以了。

package com.st.springboot.modules.sampleSecond.service.impl;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.st.springboot.config.DataSource;
import com.st.springboot.config.DataSourceEnum;
import com.st.springboot.modules.sampleSecond.entity.SampleSecond;
import com.st.springboot.modules.sampleSecond.mapper.SampleSecondMapper;
import com.st.springboot.modules.sampleSecond.service.SampleSecondService;
import org.springframework.stereotype.Service;

import java.io.Serializable;
import java.util.Collection;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author Sun Tong123
 * @since 2018-08-12
 */
@Service
public class SampleSecondServiceImpl extends ServiceImpl<SampleSecondMapper, SampleSecond> implements SampleSecondService {

    @Override
    @DataSource(DataSourceEnum.DataSourceSecond)
    public IPage<SampleSecond> page(IPage<SampleSecond> page, Wrapper<SampleSecond> queryWrapper) {
        return this.baseMapper.selectPage(page, queryWrapper);
    }

    @Override
    @DataSource(DataSourceEnum.DataSourceSecond)
    public boolean updateById(SampleSecond entity) {
        return this.retBool(this.baseMapper.updateById(entity));
    }

    @Override
    @DataSource(DataSourceEnum.DataSourceSecond)
    public boolean save(SampleSecond entity) {
        return this.retBool(this.baseMapper.insert(entity));
    }

    @Override
    @DataSource(DataSourceEnum.DataSourceSecond)
    public SampleSecond getById(Serializable id) {
        return this.baseMapper.selectById(id);
    }

    @Override
    @DataSource(DataSourceEnum.DataSourceSecond)
    public boolean removeByIds(Collection<? extends Serializable> idList) {
        return SqlHelper.retBool(this.baseMapper.deleteBatchIds(idList));
    }
}

10. 结语

以上就是我通过网上搜索并亲身实践搭建出的SpringBoot + MyBatis-Plus双数据源整合,中间也是出现了很多的问题最后也都一一克服掉了。将结果记录在此,给自己留个念想,同时也希望能给其他人在搭建过程中提供一些方便。

发布了1 篇原创文章 · 获赞 0 · 访问量 14
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 数字20 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览