多数据源实现:(数据进入系统,数据会进入那个数据源哪个数据库的哪个表中)
代码中四主四从配置
application.properties配置
server.context-path=/
server.port=8001
## Spring配置:
spring.http.encoding.charset=UTF-8
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8
spring.jackson.default-property-inclusion=NON_NULL
druid.type=com.alibaba.druid.pool.DruidDataSource
druid.order1-master.url=jdbc:mysql://192.168.11.31:3306/order1?characterEncoding=UTF-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useUnicode=true
druid.order1-master.driver-class-name=com.mysql.jdbc.Driver
druid.order1-master.username=root
druid.order1-master.password=root
druid.order1-master.initialSize=5
druid.order1-master.minIdle=1
druid.order1-master.maxIdle=10
druid.order1-master.maxActive=100
druid.order1-master.maxWait=60000
druid.order1-master.timeBetweenEvictionRunsMillis=60000
druid.order1-master.minEvictableIdleTimeMillis=300000
druid.order1-master.validationQuery=SELECT 1 FROM DUAL
druid.order1-master.testWhileIdle=true
druid.order1-master.testOnBorrow=false
druid.order1-master.testOnReturn=false
druid.order1-master.poolPreparedStatements=true
druid.order1-master.maxPoolPreparedStatementPerConnectionSize= 20
druid.order1-master.filters=stat,wall,log4j
druid.order1-master.useGlobalDataSourceStat=true
druid.order2-master.url=jdbc:mysql://192.168.11.31:3306/order2?characterEncoding=UTF-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useUnicode=true
druid.order2-master.driver-class-name=com.mysql.jdbc.Driver
druid.order2-master.username=root
druid.order2-master.password=root
druid.order2-master.initialSize=5
druid.order2-master.minIdle=1
druid.order2-master.maxIdle=10
druid.order2-master.maxActive=100
druid.order2-master.maxWait=60000
druid.order2-master.timeBetweenEvictionRunsMillis=60000
druid.order2-master.minEvictableIdleTimeMillis=300000
druid.order2-master.validationQuery=SELECT 1 FROM DUAL
druid.order2-master.testWhileIdle=true
druid.order2-master.testOnBorrow=false
druid.order2-master.testOnReturn=false
druid.order2-master.poolPreparedStatements=true
druid.order2-master.maxPoolPreparedStatementPerConnectionSize= 20
druid.order2-master.filters=stat,wall,log4j
druid.order2-master.useGlobalDataSourceStat=true
druid.order3-master.url=jdbc:mysql://192.168.11.31:3306/order3?characterEncoding=UTF-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useUnicode=true
druid.order3-master.driver-class-name=com.mysql.jdbc.Driver
druid.order3-master.username=root
druid.order3-master.password=root
druid.order3-master.initialSize=5
druid.order3-master.minIdle=1
druid.order3-master.maxIdle=10
druid.order3-master.maxActive=100
druid.order3-master.maxWait=60000
druid.order3-master.timeBetweenEvictionRunsMillis=60000
druid.order3-master.minEvictableIdleTimeMillis=300000
druid.order3-master.validationQuery=SELECT 1 FROM DUAL
druid.order3-master.testWhileIdle=true
druid.order3-master.testOnBorrow=false
druid.order3-master.testOnReturn=false
druid.order3-master.poolPreparedStatements=true
druid.order3-master.maxPoolPreparedStatementPerConnectionSize= 20
druid.order3-master.filters=stat,wall,log4j
druid.order3-master.useGlobalDataSourceStat=true
druid.order4-master.url=jdbc:mysql://192.168.11.31:3306/order4?characterEncoding=UTF-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useUnicode=true
druid.order4-master.driver-class-name=com.mysql.jdbc.Driver
druid.order4-master.username=root
druid.order4-master.password=root
druid.order4-master.initialSize=5
druid.order4-master.minIdle=1
druid.order4-master.maxIdle=10
druid.order4-master.maxActive=100
druid.order4-master.maxWait=60000
druid.order4-master.timeBetweenEvictionRunsMillis=60000
druid.order4-master.minEvictableIdleTimeMillis=300000
druid.order4-master.validationQuery=SELECT 1 FROM DUAL
druid.order4-master.testWhileIdle=true
druid.order4-master.testOnBorrow=false
druid.order4-master.testOnReturn=false
druid.order4-master.poolPreparedStatements=true
druid.order4-master.maxPoolPreparedStatementPerConnectionSize= 20
druid.order4-master.filters=stat,wall,log4j
druid.order4-master.useGlobalDataSourceStat=true
druid.order1-slave.url=jdbc:mysql://192.168.11.32:3306/order1?characterEncoding=UTF-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useUnicode=true
druid.order1-slave.driver-class-name=com.mysql.jdbc.Driver
druid.order1-slave.username=root
druid.order1-slave.password=root
druid.order1-slave.initialSize=5
druid.order1-slave.minIdle=1
druid.order1-slave.maxIdle=10
druid.order1-slave.maxActive=100
druid.order1-slave.maxWait=60000
druid.order1-slave.timeBetweenEvictionRunsMillis=60000
druid.order1-slave.minEvictableIdleTimeMillis=300000
druid.order1-slave.validationQuery=SELECT 1 FROM DUAL
druid.order1-slave.testWhileIdle=true
druid.order1-slave.testOnBorrow=false
druid.order1-slave.testOnReturn=false
druid.order1-slave.poolPreparedStatements=true
druid.order1-slave.maxPoolPreparedStatementPerConnectionSize= 20
druid.order1-slave.filters=stat,wall,log4j
druid.order1-slave.useGlobalDataSourceStat=true
druid.order2-slave.url=jdbc:mysql://192.168.11.32:3306/order2?characterEncoding=UTF-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useUnicode=true
druid.order2-slave.driver-class-name=com.mysql.jdbc.Driver
druid.order2-slave.username=root
druid.order2-slave.password=root
druid.order2-slave.initialSize=5
druid.order2-slave.minIdle=1
druid.order2-slave.maxIdle=10
druid.order2-slave.maxActive=100
druid.order2-slave.maxWait=60000
druid.order2-slave.timeBetweenEvictionRunsMillis=60000
druid.order2-slave.minEvictableIdleTimeMillis=300000
druid.order2-slave.validationQuery=SELECT 1 FROM DUAL
druid.order2-slave.testWhileIdle=true
druid.order2-slave.testOnBorrow=false
druid.order2-slave.testOnReturn=false
druid.order2-slave.poolPreparedStatements=true
druid.order2-slave.maxPoolPreparedStatementPerConnectionSize= 20
druid.order2-slave.filters=stat,wall,log4j
druid.order2-slave.useGlobalDataSourceStat=true
druid.order3-slave.url=jdbc:mysql://192.168.11.32:3306/order3?characterEncoding=UTF-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useUnicode=true
druid.order3-slave.driver-class-name=com.mysql.jdbc.Driver
druid.order3-slave.username=root
druid.order3-slave.password=root
druid.order3-slave.initialSize=5
druid.order3-slave.minIdle=1
druid.order3-slave.maxIdle=10
druid.order3-slave.maxActive=100
druid.order3-slave.maxWait=60000
druid.order3-slave.timeBetweenEvictionRunsMillis=60000
druid.order3-slave.minEvictableIdleTimeMillis=300000
druid.order3-slave.validationQuery=SELECT 1 FROM DUAL
druid.order3-slave.testWhileIdle=true
druid.order3-slave.testOnBorrow=false
druid.order3-slave.testOnReturn=false
druid.order3-slave.poolPreparedStatements=true
druid.order3-slave.maxPoolPreparedStatementPerConnectionSize= 20
druid.order3-slave.filters=stat,wall,log4j
druid.order3-slave.useGlobalDataSourceStat=true
druid.order4-slave.url=jdbc:mysql://192.168.11.32:3306/order4?characterEncoding=UTF-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useUnicode=true
druid.order4-slave.driver-class-name=com.mysql.jdbc.Driver
druid.order4-slave.username=root
druid.order4-slave.password=root
druid.order4-slave.initialSize=5
druid.order4-slave.minIdle=1
druid.order4-slave.maxIdle=10
druid.order4-slave.maxActive=100
druid.order4-slave.maxWait=60000
druid.order4-slave.timeBetweenEvictionRunsMillis=60000
druid.order4-slave.minEvictableIdleTimeMillis=300000
druid.order4-slave.validationQuery=SELECT 1 FROM DUAL
druid.order4-slave.testWhileIdle=true
druid.order4-slave.testOnBorrow=false
druid.order4-slave.testOnReturn=false
druid.order4-slave.poolPreparedStatements=true
druid.order4-slave.maxPoolPreparedStatementPerConnectionSize= 20
druid.order4-slave.filters=stat,wall,log4j
druid.order4-slave.useGlobalDataSourceStat=true
mybatis.type-aliases-package=bhz.order
mybatis.mapper-locations=classpath:bhz/order/mapping/*.xml
logging.level.tk.mybatis=TRACE
pagehelper.dialect=com.github.pagehelper.dialect.helper.MySqlDialect
pagehelper.reasonable=true
pagehelper.supportMethodsArguments=true
pagehelper.params=count=countSql
application:
package bhz.order;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(Application.class);
app.run(args);
}
}
MainConfig :扫描配置
bhz.order.config.*配置数据源
package bhz.order;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@MapperScan(basePackages = "bhz.order.mapper")
@ComponentScan(basePackages = {"bhz.order.*", "bhz.order.config.*"})
public class MainConfig {
}
DataBaseContextHolder:定义多数据源:定义四主四从,将读取的数据员存储在ThreadLocal中。
package bhz.order.config.database;
public class DataBaseContextHolder {
public enum DataBaseType {
ORDER1_MASTER("order1-master"),
ORDER2_MASTER("order2-master"),
ORDER3_MASTER("order3-master"),
ORDER4_MASTER("order4-master"),
ORDER1_SLAVE("order1-slave"),
ORDER2_SLAVE("order2-slave"),
ORDER3_SLAVE("order3-slave"),
ORDER4_SLAVE("order4-slave");
private String code;
private DataBaseType(String code){
this.code = code;
}
public String getCode(){
return code;
}
}
private static final ThreadLocal<DataBaseType> contextHolder = new ThreadLocal<DataBaseType>();
/**
* <B>方法名称:</B>设置数据源类型<BR>
* <B>概要说明:</B><BR>
* @author baihezhuo
* @since 2018年2月3日 上午11:54:42
* @param dataBaseType
*/
public static void setDataBaseType(DataBaseType dataBaseType) {
if(dataBaseType == null) throw new NullPointerException();
contextHolder.set(dataBaseType);
}
public static DataBaseType getDataBaseType(){
return contextHolder.get() == null ? DataBaseType.ORDER1_MASTER : contextHolder.get();
}
public static void clearDataBaseType(){
contextHolder.remove();
}
}
注入定义的数据源
@ConfigurationProperties(prefix = "druid.order1-master")读取以druid.order1-master开头的数据源配置,
package bhz.order.config.database;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
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;
import org.springframework.context.annotation.Primary;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
@Configuration //相当于一个xml配置文件
public class DataSourceConfiguration {
private static Logger LOGGER = org.slf4j.LoggerFactory.getLogger(DataSourceConfiguration.class);
@Value("${druid.type}")
private Class<? extends DataSource> dataSourceType;
@Bean(name = "order1-master")
@Primary
@ConfigurationProperties(prefix = "druid.order1-master")
public DataSource order1MasterDataSource() throws SQLException {
DataSource order1MasterDataSource = DataSourceBuilder.create().type(dataSourceType).build();
LOGGER.info("============= order1-master: {} ================", order1MasterDataSource);
return order1MasterDataSource;
}
@Bean(name = "order2-master")
@ConfigurationProperties(prefix = "druid.order2-master")
public DataSource order2MasterDataSource() throws SQLException {
DataSource order2MasterDataSource = DataSourceBuilder.create().type(dataSourceType).build();
LOGGER.info("============= order2-master: {} ================", order2MasterDataSource);
return order2MasterDataSource;
}
@Bean(name = "order3-master")
@ConfigurationProperties(prefix = "druid.order3-master")
public DataSource order3MasterDataSource() throws SQLException {
DataSource order3MasterDataSource = DataSourceBuilder.create().type(dataSourceType).build();
LOGGER.info("============= order3-master: {} ================", order3MasterDataSource);
return order3MasterDataSource;
}
@Bean(name = "order4-master")
@ConfigurationProperties(prefix = "druid.order4-master")
public DataSource order4MasterDataSource() throws SQLException {
DataSource order4MasterDataSource = DataSourceBuilder.create().type(dataSourceType).build();
LOGGER.info("============= order4-master: {} ================", order4MasterDataSource);
return order4MasterDataSource;
}
@Bean(name = "order1-slave")
@ConfigurationProperties(prefix = "druid.order1-slave")
public DataSource order1SlaveDataSource() throws SQLException {
DataSource order1SlaveDataSource = DataSourceBuilder.create().type(dataSourceType).build();
LOGGER.info("============= order1-slave: {} ================", order1SlaveDataSource);
return order1SlaveDataSource;
}
@Bean(name = "order2-slave")
@ConfigurationProperties(prefix = "druid.order2-slave")
public DataSource order2SlaveDataSource() throws SQLException {
DataSource order2SlaveDataSource = DataSourceBuilder.create().type(dataSourceType).build();
LOGGER.info("============= order2-slave: {} ================", order2SlaveDataSource);
return order2SlaveDataSource;
}
@Bean(name = "order3-slave")
@ConfigurationProperties(prefix = "druid.order3-slave")
public DataSource order3SlaveDataSource() throws SQLException {
DataSource order3SlaveDataSource = DataSourceBuilder.create().type(dataSourceType).build();
LOGGER.info("============= order3-slave: {} ================", order3SlaveDataSource);
return order3SlaveDataSource;
}
@Bean(name = "order4-slave")
@ConfigurationProperties(prefix = "druid.order4-slave")
public DataSource order4SlaveDataSource() throws SQLException {
DataSource order4SlaveDataSource = DataSourceBuilder.create().type(dataSourceType).build();
LOGGER.info("============= order4-slave: {} ================", order4SlaveDataSource);
return order4SlaveDataSource;
}
@Bean
public ServletRegistrationBean druidServlet(){
//spring boot 的方式 自己写一个servlet
ServletRegistrationBean reg = new ServletRegistrationBean();
reg.setServlet(new StatViewServlet());
reg.addUrlMappings("/druid/*");
reg.addInitParameter("allow", "192.168.1.1");
//reg.addInitParameter("deny", "/deny");
LOGGER.info("============= init druid servlet ================");
return reg;
}
@Bean
public FilterRegistrationBean druidFilter(){
FilterRegistrationBean ftr = new FilterRegistrationBean();
ftr.setFilter(new WebStatFilter());
ftr.addUrlPatterns("/*");
ftr.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico, /druid/*");
LOGGER.info("============= init druid filter ================");
return ftr;
}
}
MyBatisConfiguration加载数据源,要在数据源定义装配后
@AutoConfigureAfter(value = {DataSourceConfiguration.class})表示在DataSourceConfiguration加载后执行装配。@Resource(name= "order1-master")引入定义的数据源bean,最终生成DataSource,order1-master指的是DataSourceConfiguration对应的bean的name。
代理使用方法实现动态数据源DynamicDataSource,方法bean名称dynamicDataSource(自定义继承了extends AbstractRoutingDataSource类).DynamicDataSource 负责帮我们动态切换数据源。
将动态数据源绑定在SqlSessionFactory中
package bhz.order.config.database;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Resource;
import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import bhz.order.config.database.DataBaseContextHolder.DataBaseType;
@Configuration
//@EnableTransactionManagement
@AutoConfigureAfter(value = {DataSourceConfiguration.class})
public class MyBatisConfiguration {
@Resource(name= "order1-master")
private DataSource order1MasterDataSource;
@Resource(name= "order2-master")
private DataSource order2MasterDataSource;
@Resource(name= "order3-master")
private DataSource order3MasterDataSource;
@Resource(name= "order4-master")
private DataSource order4MasterDataSource;
@Resource(name= "order1-slave")
private DataSource order1SlaveDataSource;
@Resource(name= "order2-slave")
private DataSource order2SlaveDataSource;
@Resource(name= "order3-slave")
private DataSource order3SlaveDataSource;
@Resource(name= "order4-slave")
private DataSource order4SlaveDataSource;
@Bean("dynamicDataSource")
public DynamicDataSource roundRobinDataSourceProxy(){
Map<Object, Object> targetDataSource = new HashMap<Object, Object>();
targetDataSource.put(DataBaseType.ORDER1_MASTER, order1MasterDataSource);
targetDataSource.put(DataBaseType.ORDER2_MASTER, order2MasterDataSource);
targetDataSource.put(DataBaseType.ORDER3_MASTER, order3MasterDataSource);
targetDataSource.put(DataBaseType.ORDER4_MASTER, order4MasterDataSource);
targetDataSource.put(DataBaseType.ORDER1_SLAVE, order1SlaveDataSource);
targetDataSource.put(DataBaseType.ORDER2_SLAVE, order2SlaveDataSource);
targetDataSource.put(DataBaseType.ORDER3_SLAVE, order3SlaveDataSource);
targetDataSource.put(DataBaseType.ORDER4_SLAVE, order4SlaveDataSource);
// 实例化动态数据源
DynamicDataSource proxy = new DynamicDataSource();
// 盛放所以需要切换的数据源
proxy.setTargetDataSources(targetDataSource);
// 设置默认的数据源
proxy.setDefaultTargetDataSource(order1MasterDataSource);
return proxy;
}
@Bean(name="sqlSessionFactory")
public SqlSessionFactory sqlSessionFactoryBean(DynamicDataSource dynamicDataSource) {
System.err.println("----------------执行--------------");
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dynamicDataSource);
// 添加XML目录
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
try {
bean.setMapperLocations(resolver.getResources("classpath:bhz/order/mapping/*.xml"));
SqlSessionFactory sqlSessionFactory = bean.getObject();
sqlSessionFactory.getConfiguration().setCacheEnabled(Boolean.TRUE);
return sqlSessionFactory;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
// @Bean
// public DataSourceTransactionManager transactionManager(DynamicDataSource dynamicDataSource) throws Exception {
// return new DataSourceTransactionManager(dynamicDataSource);
// }
}
自定义DynamicDataSource extends AbstractRoutingDataSource负责帮助我们切换数据源
package bhz.order.config.database;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
// 动态的去切换数据源的类型
return DataBaseContextHolder.getDataBaseType();
}
}
service多数据源使用
@SelectConnection(readOnly = true)只读,读取数据去从表。
@SelectConnection写操作,去主表
package bhz.order.service;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.alibaba.fastjson.JSONObject;
import bhz.order.config.database.SelectConnection;
import bhz.order.entity.Order;
import bhz.order.mapper.OrderMapper;
import bhz.order.utils.FastJsonConvertUtil;
import bhz.order.utils.Pair;
import bhz.order.utils.SelectorUtil;
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@SelectConnection(readOnly = true)
public Order shardSelectByPrimaryKey(String preFixTableName, String uuid){
Pair<Integer/*DataBaseNumber*/, Integer/*TableNumber*/> pair = SelectorUtil.getDataBaseAndTableNumber(uuid);
Integer tableNumber = pair.getObject2();
String tableName = preFixTableName + "_" + tableNumber;
return orderMapper.shardSelectByPrimaryKey(tableName, uuid);
}
// 注解的作用: 就是对数据源的切换
@SelectConnection
public int shardInsert(String preFixTableName, String uuid, Order order){
Pair<Integer/*DataBaseNumber*/, Integer/*TableNumber*/> pair = SelectorUtil.getDataBaseAndTableNumber(uuid);
Integer tableNumber = pair.getObject2();
String tableName = preFixTableName + "_" + tableNumber; //order_3
JSONObject json = FastJsonConvertUtil.convertObjectToJSONObject(order);
Map<String, Object> params = FastJsonConvertUtil.convertJSONToObject(json, Map.class);
params.put("tableName", tableName);
return orderMapper.shardInsert(params);
}
@SelectConnection
public int shardUpdateByPrimaryKey(String preFixTableName, String uuid, Order tradeDetail){
Pair<Integer/*DataBaseNumber*/, Integer/*TableNumber*/> pair = SelectorUtil.getDataBaseAndTableNumber(uuid);
Integer tableNumber = pair.getObject2();
String tableName = preFixTableName + "_" + tableNumber;
JSONObject json = FastJsonConvertUtil.convertObjectToJSONObject(tradeDetail);
Map<String, Object> params = FastJsonConvertUtil.convertJSONToObject(json, Map.class);
params.put("tableName", tableName);
return orderMapper.shardUpdateByPrimaryKey(params);
}
}
数据源,表的使用规则
writeDataBaseSize=4四个主数据源
writeTableSize = 5每个数据源有五个表
package bhz.order.utils;
public class SelectorUtil {
private static final int writeDataBaseSize = 4;
private static final int writeTableSize = 5;
public static Pair<Integer, Integer> getDataBaseAndTableNumber(String uuid) {
int hashcode = Math.abs(uuid.hashCode());
System.err.println("hashcode: " + hashcode);
int selectDataBaseNumber = (hashcode/writeTableSize)%writeDataBaseSize + 1;
int selectTableNumber = hashcode%writeTableSize;
System.err.println("----------- SelectorUtil: selectDataBaseNumber: " + selectDataBaseNumber + " ----------------");
System.err.println("----------- SelectorUtil: selectTableNumber: " + selectTableNumber + " ----------------");
return new Pair<Integer, Integer>(selectDataBaseNumber, selectTableNumber);
}
public static void main(String[] args) {
String uuid = KeyUtil.generatorUUID();
int code = Math.abs(uuid.hashCode());
System.err.println("code: " + code);
int selectDataBaseNumber = (code/5)%4 + 1;
int selectTableNumber = code%5;
System.err.println("selectDataBaseNumber: " + selectDataBaseNumber);
System.err.println("selectTableNumber: " + selectTableNumber);
}
}
自定义注解@SelectConnection
// 注解的作用: 就是对数据源的切换
@Target 定义的注解可以利用在哪些地方,默认任何地方@Target({ElementType.METHOD, ElementType.TYPE})用在方法中
@Retention(RetentionPolicy.RUNTIME)注解什么时机被执行:源码,类加载,运行时
@Documented抽取文档
@Inheritance子类是否可以继承
package bhz.order.config.database;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface SelectConnection {
String name() default "";
boolean readOnly() default false;
}
自定义注解解析
@Aspect:AOP
@Around("@annotation(selectConnection)")表示注解被使用时,解析selectConnection
在方法调用之前作什么
(String)proceedingJoinPoint.getArgs()[0]表示注解方法的第一个参数
//2 开始执行方法 Object result = proceedingJoinPoint.proceed();
在方法调用之后作什么
package bhz.order.config.database;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import bhz.order.config.database.DataBaseContextHolder.DataBaseType;
import bhz.order.utils.Pair;
import bhz.order.utils.SelectorUtil;
@Aspect
@Component
public class SelectConnectionInterceptor implements Ordered {
private static Logger LOGGER = org.slf4j.LoggerFactory.getLogger(SelectConnectionInterceptor.class);
private static final String SUFFIX_MASTER = "-master";
private static final String SUFFIX_SLAVE = "-slave";
@Around("@annotation(selectConnection)")
public Object proceed(ProceedingJoinPoint proceedingJoinPoint, SelectConnection selectConnection) throws Throwable {
try{
//1 执行方法前...
LOGGER.info("--------------- select database source ---------------");
String currentDataBaseName = "";
// 如果在注解上添加了: name
if(!StringUtils.isBlank(selectConnection.name())){
currentDataBaseName = selectConnection.name();
} else {
String preFixTableName = (String)proceedingJoinPoint.getArgs()[0]; // 表名前缀 order
String uuid = (String) proceedingJoinPoint.getArgs()[1]; // uuid
Pair<Integer/*DataBaseNumber*/, Integer/*TableNumber*/> pair = SelectorUtil.getDataBaseAndTableNumber(uuid);
currentDataBaseName = preFixTableName + pair.getObject1();
}
if(selectConnection.readOnly()){
currentDataBaseName = currentDataBaseName + SUFFIX_SLAVE;
} else {
currentDataBaseName = currentDataBaseName + SUFFIX_MASTER;
}
//order3-master
System.err.println("----Interceptor: currentDataBaseName : " + currentDataBaseName);
//string-> order2-slave
for(DataBaseType type: DataBaseContextHolder.DataBaseType.values()){
if(!StringUtils.isBlank(currentDataBaseName)){
String typeCode = type.getCode();
if(typeCode.equals(currentDataBaseName)){
DataBaseContextHolder.setDataBaseType(type);
System.err.println("----Interceptor: code :" + DataBaseContextHolder.getDataBaseType().getCode());
}
}
}
//2 开始执行方法
Object result = proceedingJoinPoint.proceed();
//3 执行方法后
return result;
} finally {
DataBaseContextHolder.clearDataBaseType();
LOGGER.info("---------------clear database connection---------------");
}
}
@Override
public int getOrder() {
// TODO Auto-generated method stub
return -1;
}
}
mapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="bhz.order.mapper.OrderMapper" >
<resultMap id="BaseResultMap" type="bhz.order.entity.Order" >
<id column="id" property="id" jdbcType="BIGINT" />
<result column="order_id" property="orderId" jdbcType="VARCHAR" />
<result column="order_type" property="orderType" jdbcType="VARCHAR" />
<result column="city_id" property="cityId" jdbcType="VARCHAR" />
<result column="platform_id" property="platformId" jdbcType="VARCHAR" />
<result column="platform_order_id" property="platformOrderId" jdbcType="VARCHAR" />
<result column="poi_id" property="poiId" jdbcType="VARCHAR" />
<result column="sender_address" property="senderAddress" jdbcType="VARCHAR" />
<result column="sender_phone" property="senderPhone" jdbcType="VARCHAR" />
<result column="sender_lng" property="senderLng" jdbcType="INTEGER" />
<result column="sender_lat" property="senderLat" jdbcType="INTEGER" />
<result column="sender_name" property="senderName" jdbcType="VARCHAR" />
<result column="receiver_address" property="receiverAddress" jdbcType="VARCHAR" />
<result column="receiver_phone" property="receiverPhone" jdbcType="VARCHAR" />
<result column="receiver_lng" property="receiverLng" jdbcType="INTEGER" />
<result column="receiver_lat" property="receiverLat" jdbcType="INTEGER" />
<result column="receiver_name" property="receiverName" jdbcType="VARCHAR" />
<result column="remark" property="remark" jdbcType="VARCHAR" />
<result column="pkg_price" property="pkgPrice" jdbcType="DECIMAL" />
<result column="create_by" property="createBy" jdbcType="VARCHAR" />
<result column="create_time" property="createTime" jdbcType="TIMESTAMP" />
<result column="update_by" property="updateBy" jdbcType="VARCHAR" />
<result column="update_time" property="updateTime" jdbcType="TIMESTAMP" />
</resultMap>
<sql id="Base_Column_List" >
id, order_id, order_type, city_id, platform_id, platform_order_id, poi_id, sender_address,
sender_phone, sender_lng, sender_lat, sender_name, receiver_address, receiver_phone,
receiver_lng, receiver_lat, receiver_name, remark, pkg_price, create_by, create_time,
update_by, update_time
</sql>
<select id="shardSelectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.String" >
select
<include refid="Base_Column_List" />
from ${tableName}
where order_id = #{orderId,jdbcType=VARCHAR}
</select>
<delete id="shardDeleteByPrimaryKey" parameterType="java.lang.String" >
delete from ${tableName}
where order_id = #{orderId,jdbcType=VARCHAR}
</delete>
<insert id="shardInsert" parameterType="bhz.order.entity.Order" >
insert into ${tableName} (id, order_id, order_type,
city_id, platform_id, platform_order_id,
poi_id, sender_address, sender_phone,
sender_lng, sender_lat, sender_name,
receiver_address, receiver_phone, receiver_lng,
receiver_lat, receiver_name, remark,
pkg_price, create_by, create_time,
update_by, update_time)
values (#{id,jdbcType=BIGINT}, #{orderId,jdbcType=VARCHAR}, #{orderType,jdbcType=VARCHAR},
#{cityId,jdbcType=VARCHAR}, #{platformId,jdbcType=VARCHAR}, #{platformOrderId,jdbcType=VARCHAR},
#{poiId,jdbcType=VARCHAR}, #{senderAddress,jdbcType=VARCHAR}, #{senderPhone,jdbcType=VARCHAR},
#{senderLng,jdbcType=INTEGER}, #{senderLat,jdbcType=INTEGER}, #{senderName,jdbcType=VARCHAR},
#{receiverAddress,jdbcType=VARCHAR}, #{receiverPhone,jdbcType=VARCHAR}, #{receiverLng,jdbcType=INTEGER},
#{receiverLat,jdbcType=INTEGER}, #{receiverName,jdbcType=VARCHAR}, #{remark,jdbcType=VARCHAR},
#{pkgPrice,jdbcType=DECIMAL}, #{createBy,jdbcType=VARCHAR}, #{createTime,jdbcType=TIMESTAMP},
#{updateBy,jdbcType=VARCHAR}, #{updateTime,jdbcType=TIMESTAMP})
</insert>
<update id="shardUpdateByPrimaryKey" parameterType="bhz.order.entity.Order" >
update ${tableName}
set order_id = #{orderId,jdbcType=VARCHAR},
order_type = #{orderType,jdbcType=VARCHAR},
city_id = #{cityId,jdbcType=VARCHAR},
platform_id = #{platformId,jdbcType=VARCHAR},
platform_order_id = #{platformOrderId,jdbcType=VARCHAR},
poi_id = #{poiId,jdbcType=VARCHAR},
sender_address = #{senderAddress,jdbcType=VARCHAR},
sender_phone = #{senderPhone,jdbcType=VARCHAR},
sender_lng = #{senderLng,jdbcType=INTEGER},
sender_lat = #{senderLat,jdbcType=INTEGER},
sender_name = #{senderName,jdbcType=VARCHAR},
receiver_address = #{receiverAddress,jdbcType=VARCHAR},
receiver_phone = #{receiverPhone,jdbcType=VARCHAR},
receiver_lng = #{receiverLng,jdbcType=INTEGER},
receiver_lat = #{receiverLat,jdbcType=INTEGER},
receiver_name = #{receiverName,jdbcType=VARCHAR},
remark = #{remark,jdbcType=VARCHAR},
pkg_price = #{pkgPrice,jdbcType=DECIMAL},
create_by = #{createBy,jdbcType=VARCHAR},
create_time = #{createTime,jdbcType=TIMESTAMP},
update_by = #{updateBy,jdbcType=VARCHAR},
update_time = #{updateTime,jdbcType=TIMESTAMP}
where id = #{id,jdbcType=BIGINT}
</update>
</mapper>
读写分离