最近有个需求是需要通过数据源类型来动态切换数据源的操作,所有配置通过读取配置文件获取。与一般的选择实现类最大的区别是本文是动态选择,不需要通过@Qualifier注释写死实现类。看了下网上相关资料较少,且存在部分不一定正确的情况,故写此篇博客以作记录,也希望碰到此问题的小伙伴能够少走弯路。
注:本文以postgresql和maxcompute官方SDK两种查询方式查询maxcompute为例
package com.XXXX.tools.email.common.model.BO;
import com.aliyun.odps.Odps;
import com.aliyun.odps.account.Account;
import com.aliyun.odps.account.AliyunAccount;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
/**
* Description:创建maxcompute中的odps
* 作者:gu.weidong(Marco)
*/
@Component
public class MaxComputeOdps {
//读取配置文件中的配置,由于实际项目中还用到了mysql,故此处是secondary
@Value("${spring.datasource.secondary.username}")
private String accessId;
@Value("${spring.datasource.secondary.password}")
private String accessKey;
private final String endPoint = "http://service.odps.aliyun.com/api";
private final String project = "XXXX";//对应库名
@Bean(name="odps")
public Odps odps(){
Account account = new AliyunAccount(accessId, accessKey);
Odps odps = new Odps(account);
odps.setEndpoint(endPoint);
odps.setDefaultProject(project);
return odps;
}
}
package com.XXXX.tools.email.service.query;
import java.util.LinkedHashMap;
import java.util.List;
/**
* Description:查询数据接口
* 作者:gu.weidong(Marco)
*/
public interface QueryService {
//验证sql,避免DROP等操作
default boolean validSql(String orgSql){
String sql = orgSql.toUpperCase().trim();
if(sql.startsWith("DROP")||sql.startsWith("DELETE")){
return false;
}
return true;
}
//查询数据
List> queryData(String sql);
}
package com.XXX.tools.email.service.query;
import com.XXX.tools.email.common.code.ErrorCode;
import com.XXX.tools.email.common.exception.RequestException;
import com.aliyun.odps.Column;
import com.aliyun.odps.Instance;
import com.aliyun.odps.Odps;
import com.aliyun.odps.OdpsException;
import com.aliyun.odps.XXXdata.RecordXXX;
import com.aliyun.odps.task.SQLTask;
import lombok.extern.slf4j.Slf4j;XXX
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
/**
* Description:maxcompute官方SDK的查询方式
* 作者:gu.weidong(Marco)
*/
@Service
@Slf4j
public class MaxComputeQueryService implements QueryService {
@Autowired
private Odps odps;
@Override
public List> queryData(String sql) {
boolean b = validSql(sql);
if (!b) {
log.error("maxcompute的sql异常,sql语句为{}", sql);
throw new RequestException(ErrorCode.SQL_WRONG);
}
Instance i;
//将所有查询结果放置到该list中
LinkedList> resultLinkedHashMapList = new LinkedList<>();
try {
i = SQLTask.run(odps, sql);
i.waitForSuccess();
List records = SQLTask.getResult(i);
for (Record record : records) {
Column[] columns = record.getColumns();
LinkedHashMap resultLinkedHashMap = new LinkedHashMap<>();
for (int j = 0; j < columns.length; j++) {
resultLinkedHashMap.put(columns[j].getName(), record.get(j).toString());
}
resultLinkedHashMapList.add(resultLinkedHashMap);
}
} catch (OdpsException e) {
log.error("MaxCompute中sql错误,错误语句为:{}", sql, e);
throw new RequestException(ErrorCode.SQL_WRONG);
}
return resultLinkedHashMapList;
}
}
package com.XXX.tools.email.service.query;
import com.XXX.tools.email.common.code.ErrorCode;
import com.XXX.tools.email.common.exception.RequestException;
import com.XXX.tools.email.dao.mapper.secondary.QueryInfoMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
/**
* Description:postgresql方式查询maxcompute
* 作者:gu.weidong(Marco)
*/
@Service
@Slf4j
public class PostgreQueryService implements QueryService {
@Autowired
private QueryInfoMapper queryInfoMapper;
@Override
public List> queryData(String sql) {
boolean b = validSql(sql);
if (!b) {
log.error("postgre的sql异常,sql语句为{}", sql);
throw new RequestException(ErrorCode.SQL_WRONG);
}
List> mapList = new LinkedList<>();
try {
mapList = queryInfoMapper.queryInfos(sql);
} catch (Exception e) {
log.error("Postgre中sql错误,错误语句为:{}", sql, e);
throw new RequestException(ErrorCode.SQL_WRONG);
}
return mapList;
}
}
package com.XXX.tools.email.service.query;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
/**
* Description:动态选择查询接口的实现类
* 作者:gu.weidong(Marco)
*/
@Service
public class GetQueryService implements InitializingBean, ApplicationContextAware {
private Map queryServiceImplMap = new HashMap<>();
private ApplicationContext applicationContext;
public QueryService createQueryService(String type){
switch (type){
case "postgresql":return queryServiceImplMap.get("PostgreQueryService");
case "maxcompute":return queryServiceImplMap.get("MaxComputeQueryService");
default:return queryServiceImplMap.get("MaxComputeQueryService");
}
}
@Override
public void afterPropertiesSet() throws Exception {
Map beanMap = applicationContext.getBeansOfType(QueryService.class);
//遍历该接口的所有实现,将其放入map中
for (QueryService serviceImpl : beanMap.values()) {
queryServiceImplMap.put(serviceImpl.getClass().getSimpleName(), serviceImpl);
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext=applicationContext;
}
}
package com.XXX.tools.email.facade;
import com.XXX.tools.email.service.query.QueryService;
import com.XXX.common.Result;
import com.alibaba.fastjson.JSONObject;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* Description:
* 作者:gu.weidong(Marco)
*/
@RestController
@Slf4j
public class JobController implements JobFacade {
@Autowired
private GetQueryService queryService;
@Override
@PostMapping("/jobs/excution")
@ApiOperation(value = "试跑Job")
public Result excuteJob(@RequestHeader("X-Username") String username, @RequestBody JobConfigRequestDTO jobConfigRequestDTO) {
log.info("试跑接口,请求参数 username:" + username + ",jobConfigRequestDTO:" + JSONObject.toJSONString(jobConfigRequestDTO));
String sql = jobConfigRequestDTO.getSql();
//重点,获取动态的查询实现
QueryService queryService = this.queryService.createQueryService("");
List> mapList = queryService.queryData(sql);
return Result.success();
}
}
完毕!!!
有问题可以通过左侧公众号联系后台管理员