文章目录
0.前言
Springboot 集成 MyBatis,在纯血 Springboot 项目中增加如下依赖配置
${mybatis-starter-version} 是你要用的版本,自行替换
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis-starter-version}</version>
</dependency>
1.单数据源
此处默认采用的数据源连接池为 HikariDataSource(据说性能很好) 。它是springboot 默认的连接池之一(Hikari、tomcat、dbcp2),具体参见
org.springframework.boot.jdbc.DataSourceBuilder
1.1 application.properties 配置参考
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://ip:port/mytest?useUnicode=true&characterEncoding=utf8&autoReconnect=true&failOverReadOnly=false
spring.datasource.username=xxxx
spring.datasource.password=xxx
spring.datasource.initial-size=2
spring.datasource.min-idle=2
spring.datasource.max-active=500
spring.datasource.max-wait=6000
spring.datasource.time-between-eviction-runs-millis=60000
spring.datasource.min-evictable-idle-time-millis=300000
spring.datasource.validation-query=select 1
spring.datasource.test-while-idle=true
spring.datasource.test-on-borrow=true
spring.datasource.test-on-return=true
spring.datasource.pool-prepared-statements=true
spring.datasource.max-open-prepared-statements=20
# mybatis 配置:实体及 mapper.xml 这里的配置类似 spring 与 mybatis 的集成,只是放到这里配置了
mybatis.type-aliases-package=com.test.app.dao.*.entity
mybatis.config-location=classpath:mybatis/mapper.xml
1.2应用类参考配置
- 关键配置 @MapperScan(“com.test.app.dao.**.mapper”)
@Configuration
@SpringBootApplication
@MapperScan("com.test.app.dao.**.mapper")
public class DAOTestApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(DAOTestApplication.class, args);
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
logger.info("DAO test Application is starting");
return builder.sources(DAOTestApplication.class);
}
}
2.多数据源(主从)
2.1 application.properteis 如下配置
# master
spring.datasource.master.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.master.jdbcUrl=jdbc:mysql://192.168.10.1:3306/mytest?useUnicode=true&characterEncoding=utf8&autoReconnect=true&failOverReadOnly=false
spring.datasource.master.username=mysql
spring.datasource.master.password=******
spring.datasource.master.minimum-idle=5
spring.datasource.master.maximum-pool-size=200
spring.datasource.master.idle-timeout=30000
spring.datasource.master.max-lifetime=1800000
spring.datasource.master.connection-timeout=30000
spring.datasource.master.connection-test-query=SELECT 1
# slave
spring.datasource.slave.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.slave.jdbcUrl=jdbc:mysql://192.168.10.2:3306/mytest?useUnicode=true&characterEncoding=utf8&autoReconnect=true&failOverReadOnly=false
spring.datasource.slave.username=mysql
spring.datasource.slave.password=******
spring.datasource.slave.minimum-idle=5
spring.datasource.slave.maximum-pool-size=200
spring.datasource.slave.idle-timeout=30000
spring.datasource.slave.max-lifetime=1800000
spring.datasource.slave.connection-timeout=30000
spring.datasource.slave.connection-test-query=SELECT 1
spring.datasource.slave.read-only=true
2.2 主从数据源的配置
@Configuration
@EnableTransactionManagement
public class MultiDataSourceConfiguration {
@Primary
@Bean(name = "masterDataSource")
@ConfigurationProperties(prefix = "spring.datasource.master")
public DataSource masterDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "slaveDataSource")
@ConfigurationProperties(prefix = "spring.datasource.slave")
public DataSource slaveDataSource() {
return DataSourceBuilder.create().build();
}
}
2.3 Mybatis 配置动态数据源
@Configuration
@AutoConfigureAfter({MultiDataSourceConfiguration.class})
public class MyBatisConfiguration extends MybatisAutoConfiguration {
public MyBatisConfiguration(MybatisProperties properties, ObjectProvider<Interceptor[]> interceptorsProvider,
ResourceLoader resourceLoader, ObjectProvider<DatabaseIdProvider> databaseIdProvider,
ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider) {
super(properties, interceptorsProvider, resourceLoader, databaseIdProvider, configurationCustomizersProvider);
}
@Resource(name = "masterDataSource")
private DataSource masterDataSource;
@Resource(name = "slaveDataSource")
private DataSource slaveDataSource;
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
return super.sqlSessionFactory(routingDataSource());
}
public AbstractRoutingDataSource routingDataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
Map<Object, Object> targetDataSources = new ClassLoaderRepository.SoftHashMap();
targetDataSources.put(DynamicDataSourceHolder.MASTER, masterDataSource);
targetDataSources.put(DynamicDataSourceHolder.SLAVE, slaveDataSource);
dynamicDataSource.setDefaultTargetDataSource(masterDataSource);
dynamicDataSource.setTargetDataSources(targetDataSources);
return dynamicDataSource;
}
}
2.4 动态数据源线程安全处理
public class DynamicDataSourceHolder {
public static final String MASTER = "master";
public static final String SLAVE = "slave";
public static final ThreadLocal<String> holder = new ThreadLocal<String>();
public static void setDataSource(String name) {
holder.set(name);
}
public static String getDataSource() {
return holder.get();
}
}
2.5 获取动态数据源
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceHolder.getDataSource();
}
}
2.6 AOP 配置,在 DAO 层切入,根据方法名进行主从切换
@Component
@Aspect
public class DataSourceMethodInterceptor {
private static final Logger logger = LoggerFactory.getLogger(DataSourceMethodInterceptor.class);
@Before("execution(* com.test.app.dao.*.mapper.*.*(..))")
public void dynamicSetDataSoruce(JoinPoint joinPoint) throws Exception {
String methodName = joinPoint.getSignature().getName();
// 查询使用从库
if (methodName.startsWith("select") || methodName.startsWith("load") || methodName.startsWith("get")
|| methodName.startsWith("count") || methodName.startsWith("is") || methodName.startsWith("query")
|| methodName.startsWith("search")) {
logger.debug("methodName:{}, execute slave", methodName);
DynamicDataSourceHolder.setDataSource("slave");
} else { // 其它使用主库
logger.debug("methodName:{}, execute master", methodName);
DynamicDataSourceHolder.setDataSource("master");
}
}
}