1. maven加入 presto-jdbc 依赖
<dependency>
<groupId>com.facebook.presto</groupId>
<artifactId>presto-jdbc</artifactId>
<version>0.203</version>
</dependency>
在application.properties中增加prosto的配置
presto.spring.datasource.marketing.url = jdbc:presto://ip:port/catalog?user=root
presto.spring.datasource.marketing.driver-class-name = com.facebook.presto.jdbc.PrestoDriver
2.创建一个用于数据源切换的注解
import com.runlion.tms.common.constants.DataSourceConstants;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* aop数据源切换
*
* @author caorui
* @version v1.0
* @date 2018年10月20日 11:09:34
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface DataSource {
String value() default DataSourceConstants.DEFAULT_DATA_SOURCE;
}
3.对注解进行拦截
import com.runlion.tms.common.annotation.DataSource;
import org.springframework.aop.Pointcut;
import org.springframework.aop.framework.autoproxy.AbstractBeanFactoryAwareAdvisingPostProcessor;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
@Component
public class DataSourceConfigurer extends AbstractBeanFactoryAwareAdvisingPostProcessor implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
Pointcut pointcut = new AnnotationMatchingPointcut(null, DataSource.class);
this.advisor = new DefaultPointcutAdvisor(pointcut, new DynamicDataSourceInterceptor());
}
}
import com.runlion.tms.common.annotation.DataSource;
import com.runlion.tms.common.config.DataSourceContextHolder;
import com.runlion.tms.common.constants.DataSourceConstants;
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import java.lang.reflect.Method;
@Slf4j
public class DynamicDataSourceInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Method method = invocation.getMethod();
String dataSource = DataSourceConstants.DEFAULT_DATA_SOURCE;
try {
if (method.isAnnotationPresent(DataSource.class)) {
DataSource annotation = method.getAnnotation(DataSource.class);
// 取出注解中的数据源名
dataSource = annotation.value();
}
} catch (Exception e) {
e.printStackTrace();
}
// 切换数据源
DataSourceContextHolder.setDB(dataSource);
Object proceed = invocation.proceed();
DataSourceContextHolder.clearDB();
return proceed;
}
}
创建ThreadLocal根据当前线程的数据源信息进行切换
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.Resource;
/**
* @author caorui
* @version v1.0
* @date 2018年10月20日 11:09:34
*/
public class DataSourceContextHolder {
/**
* 默认数据源
*/
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
// 设置数据源名
public static void setDB(String dbType) {
contextHolder.set(dbType);
}
// 获取数据源名
public static String getDB() {
return (contextHolder.get());
}
// 清除数据源名
public static void clearDB() {
contextHolder.remove();
}
}
数据源常量类
/**
* @author caorui
* @version v1.0
* @date 2018年10月20日 11:09:34
*/
public class DataSourceConstants {
public static final String DEFAULT_DATA_SOURCE = "defaultDataSource";
public static final String PRESTO_DATA_SOURCE = "prestoDataSource";
}
4.创建动态数据源配置
import com.runlion.tms.common.constants.DataSourceConstants;
import com.runlion.tms.common.thirdparty.mybaties.typehandlers.StringCodeEnumTypeHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.ArrayUtils;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.type.TypeHandler;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* 多数据源配置类
*
* @author caorui
* @version v1.0
* @date 2018年10月20日 11:09:34
*/
@Slf4j
@Configuration
public class DataSourceConfig {
//默认的Druid配置,不再粘贴出来
@Autowired
private DruidConfiguration defaultDataSource;
@Autowired(required = false)
private Interceptor[] interceptors;
//presto数据源
@Bean(name = DataSourceConstants.PRESTO_DATA_SOURCE)
@ConfigurationProperties(prefix = "presto.spring.datasource.marketing")
public DataSource prestoDataSource() {
return DataSourceBuilder.create().build();
}
/**
* 动态数据源: 通过AOP在不同数据源之间动态切换
*
* @return
*/
@Bean(name = "dynamicDataSource")
public DataSource dynamicDataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
// 默认数据源
dynamicDataSource.setDefaultTargetDataSource(defaultDataSource.dataSource());
// 配置多数据源
Map<Object, Object> dsMap = new HashMap<>();
dsMap.put(DataSourceConstants.DEFAULT_DATA_SOURCE, defaultDataSource.dataSource());
dsMap.put(DataSourceConstants.PRESTO_DATA_SOURCE, prestoDataSource());
dynamicDataSource.setTargetDataSources(dsMap);
return dynamicDataSource;
}
@Bean
public SqlSessionFactoryBean sessionFactoryBean(
@Value("${mybatis.mapper-locations}") String mapperLocations,
@Value("${mybatis.typeHandlersPackage}") String typeHandlersPackage,
@Value("${mybatis.configuration.map-underscore-to-camel-case}") boolean mapUnderscoreToCamelCase) throws IOException {
log.info("mapperLocations--------->" + mapperLocations);
log.info("typeHandlersPackage--------->" + typeHandlersPackage);
log.info("map-underscore-to-camel-case--------->" + mapUnderscoreToCamelCase);
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
configuration.setMapUnderscoreToCamelCase(mapUnderscoreToCamelCase);
String[] split = mapperLocations.split(",");
PathMatchingResourcePatternResolver pathMatchingResourcePatternResolver = new PathMatchingResourcePatternResolver();
//
Resource[] resources1 = new Resource[0];
for (int i = 0; i < split.length; i++) {
resources1 = (Resource[]) ArrayUtils.addAll(pathMatchingResourcePatternResolver.getResources(split[i]), resources1);
}
TypeHandler<?>[] typeHandlers = new TypeHandler[1];
typeHandlers[0] = new StringCodeEnumTypeHandler();
sqlSessionFactoryBean.setDataSource(dynamicDataSource());
sqlSessionFactoryBean.setMapperLocations(resources1);
sqlSessionFactoryBean.setConfiguration(configuration);
sqlSessionFactoryBean.setTypeHandlers(typeHandlers);
if (interceptors != null) {
sqlSessionFactoryBean.setPlugins(interceptors);
}
return sqlSessionFactoryBean;
}
/**
* 注入 DataSourceTransactionManager 用于事务管理
*/
@Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dynamicDataSource());
}
}
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* @author caorui
* @version v1.0
* @date 2018年10月20日 11:09:34
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDB();
}
}
使用示例:
在mapper接口上加@DateSource
由于presto-jdbc没有实现sql编译接口所以要指定 statementType="STATEMENT",否则会报错 ,presto-jdbc不能用占位符形式带入参数要用${}带入参数
<select id="stationDetailSum" resultType="com.runlion.tms.finance.vo.StationFinancialVerificationDetailVo"
statementType="STATEMENT">
</select>