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);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章