SpringBoot2 + Druid + Mybatis multi data source configuration

In large high concurrent application scenario data, faster response to user requests, read and write separation is more common response options. It uses separate read and write data using multiple sources. Here's how to build a record SpringBoot2 + Druid + Mybatis multi data source configuration as well as problems encountered during use.

First, start with pom.xml start (version springboot 2 in)

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.17</version>
</dependency>
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
</dependencies>

java inject dependency injection is standard. spring tacit support recognition. @Autowired default spring comes JSR-330 is equivalent to the annotation @Inject; @Qualifier according Bean default name is equivalent to the case of injection @Named the JSR-330 annotations.

Second, add the Mapper reading DB

@Mapper
public interface AssetMapper {

@Select("select * from Asset where account = #{account}")
Asset queryName(String account);
}

Use mybatis notes on here, so you can omit less * .xml and other configuration files.

Third, add configuration parameters multiple data sources

spring.datasource.druid.write.url=jdbc:mysql://192.168.0.110:3306/master?characterEncoding=utf-8&serverTimezone=Asia/Shanghai
spring.datasource.druid.write.username=root
spring.datasource.druid.write.password=123456
spring.datasource.druid.write.driver-class-name=com.mysql.cj.jdbc.Driver
#
spring.datasource.druid.read.url=jdbc:mysql://192.168.0.110:3306/slave1?characterEncoding=utf-8&serverTimezone=Asia/Shanghai
spring.datasource.druid.read.username=root
spring.datasource.druid.read.password=123456
spring.datasource.druid.read.driver-class-name=com.mysql.cj.jdbc.Driver

新版本mysql的url后面必需要添加serverTimezone=。 不然会报以下异常:
2019-06-05 18:47:24.058 ERROR 17804 --- [-Create-6910184] com.alibaba.druid.pool.DruidDataSource   : create connection SQLException, url: jdbc:mysql://localhost:3306/master, errorCode 0, state 01S00

java.sql.SQLException: The server time zone value '�й���׼ʱ��' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value if you want to utilize time zone support.
   

Fourth, the configuration data source

@Configuration
public class DataSourceConfig {

@Bean(name = "masterDataSource")
@ConfigurationProperties(prefix = "spring.datasource.druid.write")
@Primary
public DataSource masterDataSource() {
return DruidDataSourceBuilder.create().build();
}

@Bean(name = "slaveDataSource")
@ConfigurationProperties(prefix = "spring.datasource.druid.read")
public DataSource slaveDataSource() {
return DruidDataSourceBuilder.create().build();
}

@Inject
@Named("masterDataSource")
private DataSource masterDataSource;

@Inject
@Named("slaveDataSource")
private DataSource slaveDataSource;

/**
* 根据数据源创建SqlSessionFactory
*/
@Bean
public SqlSessionFactory sqlSessionFactory(DynamicDataSource dataSource) throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
return sessionFactory.getObject();
}
}

SqlSessionFactory will need to re-create, if not create abnormal cycle call will be reported

Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'masterDataSource': Requested bean is currently in creation: Is there an unresolvable circular reference?

Because SqlSessionFactory or go way created by default.

How to use the context data source that can be used to process ThreadLocal.

Fifth, the data source routing


public class DataSourceContextRouting implements AutoCloseable {

static final ThreadLocal<String> dataSourceKeyThreadLocal = new ThreadLocal<>();

public String getDataSourceName(){
String key = dataSourceKeyThreadLocal.get();
return StringUtils.isBlank(key) ?"masterDataSource":key;
}

public DataSourceContextRouting(String key){
dataSourceKeyThreadLocal.set(key);
}

@Override
public void close() throws Exception {
dataSourceKeyThreadLocal.remove();
}
}

It provides a dynamic source of spring realize the function. Only need to inherit AbstractRoutingDataSource, and rewrites protected Object determineCurrentLookupKey ()

public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextRouting.getDataSourceName();
}
}

 // This is the core code

@Bean

DynamicDataSource public the dataSource () { 
the Map <Object , Object> = targetDataSources new new the HashMap <> () ;
targetDataSources.put ( "masterDataSource" , masterDataSource) ;
targetDataSources.put ( "slaveDataSource" , slaveDataSource) ;

DynamicDataSource the dataSource = new new DynamicDataSource () ; // set the data source mapping dataSource.setTargetDataSources (targetDataSources) ; // set the default data source when the data source may not be mapped to the default data source dataSource.setDefaultTargetDataSource ( slaveDataSource) ; dataSource.afterPropertiesSet () ; return the dataSource





;
}

 Six, controller route switching

 @RequestMapping("master")
public String master(String account){
String key = "masterDataSource";
new DataSourceContextRouting(key);
//TODO .....
}
@RequestMapping("slave")
public String slave(String account){
String key = "slaveDataSource";
new DataSourceContextRouting(key);
      //TODO......
}

 So far, the entire configuration of the plurality of data sources.

However, this code more invasive, can be handled as annotations. Notes define identity

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource {
String value();
}

 Use annotations that need to be resolved cut this, so you need to spend spring AOP functionality.

 First add maven dependent

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

 Then add them to resolve Aspect

@Aspect
@Named
public class DataSourceRoutingAspect {
@Around("@annotation(targetDataSource)")
public Object routingWithDataSource(ProceedingJoinPoint joinPoint, TargetDataSource targetDataSource) throws Throwable {
String key = targetDataSource.value();
try (DataSourceContextRouting ctx = new DataSourceContextRouting(key)) {
return joinPoint.proceed();
}
}
}

 @RequestMapping("master")
@TargetDataSource("masterDataSource")
public String master(String account){
TODO:.....
}
@RequestMapping("slave")
@TargetDataSource("slaveDataSource")
public String slave(String account){
TODO:.....
}

 

Guess you like

Origin www.cnblogs.com/song27/p/10977241.html