由于项目需要同时连接多个数据源,通过查询网上的各种帖子搭建出来的框架出现了各种各样的问题,最后将这些问题一一解决后终于搭建出了想要的双数据源效果,在这里留作记录。
使用的环境:
数据库: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
说明:
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>
说明:
1、由于Mysql使用的8.0版本,所以驱动使用了com.mysql.cj.jdbc.Driver, 5.X版本驱动请使用com.mysql.jdbc.Driver
2、双数据源需要在SpringBoot个性配置代码中手动配置DataSource,因此这边自定义了配置层次spring.datasource.database1和spring.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
说明:
这里都是和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);
}
}
说明:
通过枚举类定义出项目中使用的所有数据源的名称,枚举值可以自定义,这里起名为DataSourceFirst和DataSourceSecond。
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;
}
}
说明:
在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;
}
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();
}
}
说明:
我后面会通过向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();
}
}
说明:
前面已经定义了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();
}
}
说明:
这里便是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();
// /**/
// //performanceInterceptor.setMaxTime(1000);
// /**/
// 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;
}
}
说明:
由于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;
}
}
这个不多说了,运行少不了的东西。
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);
}
}
代码生成器也是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)document
// .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"));
}
}
这里主要介绍一下实现数据源切换的Service层代码的写法,注意注解的使用即可
说明:
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;
/**
*
* 服务实现类
*
*
* @author ~孑然妒火~
* @since 2018-08-12
*/
@Service
public class SampleServiceImpl extends ServiceImpl<SampleMapper, Sample> implements SampleService {
}
说明:
如果使用第二数据源,只需要在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;
/**
*
* 服务实现类
*
*
* @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));
}
}
以上就是我通过网上搜索并亲身实践搭建出的SpringBoot + MyBatis-Plus双数据源整合,中间也是出现了很多的问题最后也都一一克服掉了。将结果记录在此,给自己留个念想,同时也希望能给其他人在搭建过程中提供一些方便。