Bootstrap

最简单可配置的多数据源dataSource

场景

公司需要从数量不定,类型可能不同的第三方数据库读取数据。

需求

数据库信息需要动态配置(如从数据库读取),需要动态设置DataSource,且考虑到线程安全。

所以写死的多数据源满足不了。

下面代码已满足基本要求,便捷性自行扩展。

解释:ComRecord  它是个自定义的map。

数据源代码:

package com.tyl.localhis.dao;

import com.tyl.comm.basic.ComRecord;
import com.tyl.comm.util.StringUtils;
import com.tyl.localhis.dao.dto.ProcedureDto;
import com.tyl.localhis.entity.config.SyncConfigDb;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.simple.SimpleJdbcCall;
import org.springframework.jdbc.support.lob.DefaultLobHandler;
import org.springframework.stereotype.Service;

import javax.sql.DataSource;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* @description 通用访问数据库类,该类可实现动态多数据源,且线程安全
* @author tangyaliang
 * @time 2021/12/11 22:56
*/
@Service
public class ComDao {
    private final ThreadLocal<MyJdbcTemplate> local = new ThreadLocal<>();
    private final Map<String, HikariDataSource> map = new HashMap<>();
    private final MyJdbcTemplate template = new MyJdbcTemplate();
    /**
     * 没有默认数据源
     */
    public ComDao(){}
    /**
     * 支持配置默认数据源,一般用不上
     * @param dataSource 默认数据源
     */
    public ComDao(DataSource dataSource){
        template.setDataSource(dataSource);
        local.set(template);
    }
    /**
     * @param config 数据库链接配置,db_id,db_url,db_name,db_pwd
     * @description 根据不同数据库用户名切换DataSource
     * @author tangyaliang  线程安全
     * @time 2021/12/11 22:56
     */
    public void setDataSource(SyncConfigDb config) {
        HikariDataSource dataSource = map.get(config.getDbId());
        if (dataSource == null) {
            dataSource = new HikariDataSource();
            //数据库url
            dataSource.setJdbcUrl(config.getDbUrl());
            //数据库用户名
            dataSource.setUsername(config.getDbName());
            //数据库密码
            dataSource.setPassword(config.getDbPwd());
            if (!StringUtils.isEmpry(config.getDbDriverName())) {
                dataSource.setDriverClassName(config.getDbDriverName());
            }
            //DataSource放入map中,等待使用
            map.put(config.getDbId(), dataSource);
        }
        template.setDataSource(dataSource);
        local.set(template);
    }

    /**
     * 执行sql语句
     * @param sql sql
     * @return 结果集
     */
    public List<ComRecord> query(String sql) {
        return local.get().query(sql, new ComRowMapper(local.get().getDbEnCoding()));
    }
    /**
     * 调用存储过程返回结果集
     * 目前可以调用oracle游标结果集,mysql结果集,其他微测试
     * @return 结果集
     */
    public List<ComRecord> queryProcedureByRefcursor(ProcedureDto dto) {
        //参数名称不区分大小写
        local.get().setResultsMapCaseInsensitive(true);
        SimpleJdbcCall simpleJdbcCall = new SimpleJdbcCall(local.get());
        //设置储存过程名称
        simpleJdbcCall.withProcedureName(dto.getProcedurName());
        //设置存储过程游标或数据集名称,如果参数名称与存储过程中out名称相对应则返回ComRowMapper类型,名称对应不上返回linkdlist
        simpleJdbcCall.returningResultSet(dto.getRefcursorName(), new ComRowMapper(local.get().getDbEnCoding()));
        //.returningResultSet("data", BeanPropertyRowMapper.newInstance(Clazz.class));Clazz.class可以直接转化为实体类型
        //设置输入参数
        Map<String, Object> map;
        if (dto.getInParames() == null || dto.getInParames().size() == 0) {
            map = simpleJdbcCall.execute(new HashMap<>(0));
        } else {
            map = simpleJdbcCall.execute(dto.getInParames());
        }
        return (List<ComRecord>) map.get(dto.getRefcursorName());
    }


    /**
    * @description 通用RowMapper,其他地方用不到,所以定义为成员内部类
    * @author tangyaliang
    * @time 2021/12/11
    */
    class ComRowMapper implements RowMapper {
        String charSet;
        public ComRowMapper(){
        }
        public ComRowMapper(String charSet){
            this.charSet = charSet;
        }
        @Override
        public ComRecord mapRow(ResultSet rs, int rowNum) throws SQLException {
            ComRecord record = new ComRecord();
            ResultSetMetaData red = rs.getMetaData();
            DefaultLobHandler handler = new DefaultLobHandler();
            String columnName, value;
            try {
                for (int i = 1; i <= red.getColumnCount(); i++) {
                    columnName = red.getColumnName(i);

                    if (red.getColumnTypeName(i).equalsIgnoreCase("CLOB")) {
                        value = handler.getClobAsString(rs, i);
                    } else {
                        value = rs.getString(i);
                    }
                    //如果数据库字符集不是GBK,修改为GBK
                    if (!"GBK".equalsIgnoreCase(charSet) && charSet != null) {
                        if (value == null) continue;
                        value = new String(value.getBytes(charSet), "GBK");
                    }
                    record.put(columnName, value);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return record;
        }
    }
}

测试代码:用的定时任务,一秒钟执行一次

    private final ComDao comDao;

    @Scheduled(cron = "0/1 * * * * ?")
    public void data1() {
        SyncConfigDb db = new SyncConfigDb();
        db.setDbId("COMM");
        db.setDbUrl("jdbc:oracle:thin:@192.168.1.115:1521:orcl");
        db.setDbName("COMM");
        db.setDbPwd("123");
        comDao.setDataSource(db);
        List<ComRecord> list = comDao.query("select * from tablename1");
        System.out.println("data1==" + list.size());
    }

    @Scheduled(cron = "0/1 * * * * ?")
    public void data2() {
        SyncConfigDb db = new SyncConfigDb();
        db.setDbId("TYL");
        db.setDbUrl("jdbc:oracle:thin:@192.168.1.115:1521:orcl");
        db.setDbName("TYL");
        db.setDbPwd("123");
        comDao.setDataSource(db);
        List<ComRecord> list = comDao.query("select * from tablename2");
        System.out.println("data2==" + list.size());
    }

牛逼,真他么想给自己点个赞。

;