2020.06.02 springboot动态配置对个数据源,数据源配置在表中

1.DBIdentifier

public class DBIdentifier {

    /**
     * 用不同的projectCode 来区分数据库
     */
    private static ThreadLocal<String> projectCode = new ThreadLocal<String>();

    public static String getProjectCode() {
        return projectCode.get();
    }

    public static void setProjectCode(String code) {
        projectCode.set(code);
    }
}

2.DynamicDataSource

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.SQLException;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.apache.tomcat.jdbc.pool.PoolProperties;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * 定义动态数据源派生类。从基础的DataSource派生,动态性自己实现。
 *
 */
public class DynamicDataSource extends DataSource {
    private static Logger log = LogManager.getLogger(DynamicDataSource.class);
    @Autowired
    private UserService userService;

    /**
     * 改写本方法是为了在请求不同工程的数据时去连接不同的数据库。
     */
    @Override
    public Connection getConnection(){
        String projectCode = DBIdentifier.getProjectCode();
        //1、获取数据源
        DataSource dds = DDSHolder.instance().getDDS(projectCode);
        //2、如果数据源不存在则创建
        if (dds == null) {
            try {
                DataSource newDDS = initDDS(projectCode);
                DDSHolder.instance().addDDS(projectCode, newDDS);
            } catch (IllegalArgumentException | IllegalAccessException e) {
                log.error("Init data source fail. projectCode:" + projectCode);
                return null;
            }
        }
        dds = DDSHolder.instance().getDDS(projectCode);
        try {
            return dds.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 以当前数据对象作为模板复制一份。
     *
     * @return dds
     * @throws IllegalAccessException
     * @throws IllegalArgumentException
     */
    private DataSource initDDS(String projectCode) throws IllegalArgumentException, IllegalAccessException {
        DataSource dds = new DataSource();
        // 2、复制PoolConfiguration的属性
        PoolProperties property = new PoolProperties();
        Field[] pfields = PoolProperties.class.getDeclaredFields();
        for (Field f : pfields) {
            f.setAccessible(true);
            Object value = f.get(this.getPoolProperties());
            try
            {
                f.set(property, value);
            }
            catch (Exception e)
            {
                //有一些static final的属性不能修改。忽略。
                log.info("Set value fail. attr name:" + f.getName());
                continue;
            }
        }
        dds.setPoolProperties(property);

        // 3、从数据库中读取ip,port,dbname,user,password
        User user=userService.getUserListByCustomId(projectCode);
        String url = "jdbc:mysql://"+user.getHeroIp()+":"+ user.getHeroPort()+"/"+
                user.getHeroName()+"?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC&autoReconnect=true";
        dds.setUrl(url);
        dds.setUsername(user.getHeroUserName());
        dds.setPassword(user.getHeroUserPassword());

        return dds;
    }
}

3.DDSHolder

import org.apache.tomcat.jdbc.pool.DataSource;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Timer;

/**
 * 动态数据源管理器。
 */
public class DDSHolder {

    /**
     * 管理动态数据源列表。<工程编码,数据源>
     */
    private Map<String, DDSTimer> ddsMap = new HashMap<>();

    /**
     * 通过定时任务周期性清除不使用的数据源
     */
    private static Timer clearIdleTask = new Timer();
    static {
        clearIdleTask.schedule(new ClearIdleTimerTask(), 5000, 60 * 1000);
    };

    private DDSHolder() {

    }

    /*
     * 获取单例对象
     */
    public static DDSHolder instance() {
        return DDSHolderBuilder.instance;
    }

    /**
     * 添加动态数据源。
     *
     * @param projectCode 项目编码
     * @param dds dds
     */
    public synchronized void addDDS(String projectCode, DataSource dds) {

        DDSTimer ddst = new DDSTimer(dds);
        ddsMap.put(projectCode, ddst);
    }

    /**
     * 查询动态数据源
     *
     * @param projectCode 项目编码
     * @return dds
     */
    public synchronized DataSource getDDS(String projectCode) {

        if (ddsMap.containsKey(projectCode)) {
            DDSTimer ddst = ddsMap.get(projectCode);
            ddst.refreshTime();
            return ddst.getDds();
        }

        return null;
    }

    /**
     * 清除超时无人使用的数据源。
     */
    public synchronized void clearIdleDDS() {

        Iterator<Map.Entry<String, DDSTimer>> iter = ddsMap.entrySet().iterator();
        for (; iter.hasNext(); ) {

            Map.Entry<String, DDSTimer> entry = iter.next();
            if (entry.getValue().checkAndClose())
            {
                iter.remove();
            }
        }
    }

    /**
     * 单例构件类
     */
    private static class DDSHolderBuilder {
        private static DDSHolder instance = new DDSHolder();
    }
}

4.DDSTimer

import org.apache.tomcat.jdbc.pool.DataSource;

/**
 * 动态数据源定时器管理。长时间无访问的数据库连接关闭。
 *
 */
public class DDSTimer {

    /**
     * 空闲时间周期。超过这个时长没有访问的数据库连接将被释放。默认为10分钟。
     */
    private static long idlePeriodTime = 10 * 60 * 1000;

    /**
     * 动态数据源
     */
    private DataSource dds;

    /**
     * 上一次访问的时间
     */
    private long lastUseTime;

    public DDSTimer(DataSource dds) {
        this.dds = dds;
        this.lastUseTime = System.currentTimeMillis();
    }

    /**
     * 更新最近访问时间
     */
    public void refreshTime() {
        lastUseTime = System.currentTimeMillis();
    }

    /**
     * 检测数据连接是否超时关闭。
     *
     * @return true-已超时关闭; false-未超时
     */
    public boolean checkAndClose() {

        if (System.currentTimeMillis() - lastUseTime > idlePeriodTime)
        {
            dds.close();
            return true;
        }

        return false;
    }

    public DataSource getDds() {
        return dds;
    }
}

5.ClearIdleTimerTask

import java.util.TimerTask;

/**
 * 清除空闲连接任务。
 */
public class ClearIdleTimerTask extends TimerTask {

    @Override
    public void run() {
        DDSHolder.instance().clearIdleDDS();
    }
}

6.实现方法

DBIdentifier.setProjectCode(projectCode);

你可能感兴趣的:(2020.06.02 springboot动态配置对个数据源,数据源配置在表中)