栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

基于AbstractRoutingDataSource实现dble配置文件多数据源自动切换,并导出表中的数据

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

基于AbstractRoutingDataSource实现dble配置文件多数据源自动切换,并导出表中的数据

背景:使用dble作为数据库中间件在业务中使用,审计数据量太大,通过dble查询导出全量数据,会导致dble内存飙升,但是,因为分库太多,所以需要开发一款可以动态切换数据源的工具将数据库导出。博文为阉割后的demo,仅供参考,源码已经上传,可以前往下载源码。

1、新建maven项目,pom引入如下

      
        
            org.springframework
            spring-context-support
            ${spring.version}
        
        
            org.springframework
            spring-jdbc
            ${spring.version}
        
        
            org.springframework
            spring-test
            ${spring.version}
        
        
        
            org.mybatis
            mybatis
            ${mybatis.version}
        
        
            org.mybatis
            mybatis-spring
            ${mybatis-spring.version}
        

        
        
            mysql
            mysql-connector-java
            5.1.34
            runtime
        
        
            com.alibaba
            druid
            1.0.14
        
        
        
            com.google.guava
            guava
            15.0
        
        
        
            junit
            junit
            4.12
            test
        
        
        
            com.alibaba
            fastjson
            1.2.28
        

        
        
            org.aspectj
            aspectjweaver
            1.8.8
        

        
        
            org.slf4j
            slf4j-api
            1.7.12
        
        
            org.slf4j
            slf4j-log4j12
            1.7.12
        
        
		
		    com.alibaba
		    easyexcel
		    1.1.2-beta5
		
		
		    org.apache.ant
		    ant
		    1.10.12
		

2、新建applicationContext-jdbc.xml配置文件,自定义DynamicDataSource





    
    
    

    
    
        
         DB_SET=new HashSet<>();		//其他库
	public static  Set LOG1_DB_SET=new HashSet<>();	//log1对应的库
	public static  Set LOG2_DB_SET=new HashSet<>();	//log2对应的库
	private static final String URL_PRE="jdbc:mysql://";
	private static final String URL_SUFF="?useUnicode=true&characterEncoding=utf-8&autoReconnect=true";

	public static final String DB_PRE="dn";
	public static final String LOG1_DB_PRE="log1dn";
	public static final String LOG2_DB_PRE="log2dn";

	@Override
	protected Object determineCurrentLookupKey() {
		String dataSource = getDataSource();
		_log.debug("the current datasource is :{}", dataSource);
		return dataSource;
	}

	
	public static void setDataSource(String dataSource) {
		contextHolder.set(dataSource);
	}

	
	public static String getDataSource() {
		return contextHolder.get();
	}

	
	public static void clearDataSource() {
		contextHolder.remove();
	}

	public void init(){
		_log.info(">> >> >>初始化所有数据源...");
		List dataNodeList= SchemaLoaderUtil.getDataNodeList();
		String path= DynamicDataSource.class.getClassLoader().getResource("jdbc.properties").getPath();
		PropertiesFileUtil propertiesFileUtil= PropertiesFileUtil.getInstance(path);
		Map dataSourceMap=new HashMap<>();
		//初始化数据源
		for(DataNode dataNode:dataNodeList){
			DruidDataSource druidDataSource=new DruidDataSource();
			druidDataSource.setDriverClassName(propertiesFileUtil.get("jdbc.driver"));
			druidDataSource.setUrl(URL_PRE+dataNode.getDataHost().getWriteHostUrl()+"/"+dataNode.getDbName()+URL_SUFF);
			druidDataSource.setUsername(dataNode.getDataHost().getUserName());
			druidDataSource.setPassword(dataNode.getDataHost().getPassword());

			druidDataSource.setInitialSize(propertiesFileUtil.getInt("druid.initialSize"));
			druidDataSource.setMinIdle(propertiesFileUtil.getInt("druid.minIdle"));
			druidDataSource.setMaxActive(propertiesFileUtil.getInt("druid.maxActive"));
			druidDataSource.setMaxWait(propertiesFileUtil.getInt("druid.maxWait"));
			druidDataSource.setTimeBetweenEvictionRunsMillis(propertiesFileUtil.getInt("druid.timeBetweenEvictionRunsMillis"));
			druidDataSource.setMinEvictableIdleTimeMillis(propertiesFileUtil.getInt("druid.minEvictableIdleTimeMillis"));
			druidDataSource.setValidationQuery(propertiesFileUtil.get("druid.validationQuery"));
			druidDataSource.setTestWhileIdle(propertiesFileUtil.getBool("druid.testWhileIdle"));
			druidDataSource.setTestonBorrow(propertiesFileUtil.getBool("druid.testOnBorrow"));
			druidDataSource.setTestonReturn(propertiesFileUtil.getBool("druid.testOnReturn"));
			try {
				druidDataSource.setFilters(propertiesFileUtil.get("druid.filters"));
				druidDataSource.init();
				dataSourceMap.put(dataNode.getName(),druidDataSource);
				if(dataNode.getName().startsWith(DB_PRE)&&!dataNode.getName().equals(STANDALONE_DB)){
					DB_SET.add(dataNode.getName());
				}else if(dataNode.getName().startsWith(LOG1_DB_PRE)){
					LOG1_DB_SET.add(dataNode.getName());
				}else if(dataNode.getName().startsWith(LOG2_DB_PRE)){
					LOG2_DB_SET.add(dataNode.getName());
				}
				_log.info("数据库:{} 初始化成功!",dataNode.getName());
			} catch (SQLException e) {
				e.printStackTrace();
				_log.error("数据库:{} 初始化失败,程序中断!",dataNode.getName());
				System.exit(1);
			}
		}
		 setTargetDataSources(dataSourceMap);
		_log.info(">> >> >>所有数据源初始结束");
	}

	@Override
	public void afterPropertiesSet() {
		init();
		super.afterPropertiesSet();
	}

	public static void main(String[] args) {
		DynamicDataSource dynamicDataSource = new DynamicDataSource();
		dynamicDataSource.init();
	}


}

        解析配置文件的getDataNodeList方法代码如下:

	
    public static List getDataNodeList() {
        Map dataHostMap = new HashMap<>();
        List dataNodeList = new ArrayList<>();
        documentBuilderFactory documentBuilderFactory = documentBuilderFactory.newInstance();
        InputStream in = null;
        try {
            documentBuilderFactory.setValidating(false);
            documentBuilderFactory.setNamespaceAware(false);
            documentBuilderFactory.setFeature("http://xml.org/sax/features/namespaces", false);
            documentBuilderFactory.setFeature("http://xml.org/sax/features/validation", false);
            documentBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false);
            documentBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
            documentBuilder documentBuilder = documentBuilderFactory.newdocumentBuilder();
            in = SchemaLoaderUtil.class.getClassLoader().getResourceAsStream("schema.xml");
            document document = documentBuilder.parse(in);
            Element rootElement = document.getdocumentElement();
            //获取dataHost
            NodeList dataHostNList = rootElement.getElementsByTagName("dataHost");
            for (int i = 0; i < dataHostNList.getLength(); i++) {
                Element dataHostEl = (Element) dataHostNList.item(i);
                DataHost dataHost = wrappDataHost(dataHostEl);
                dataHostMap.put(dataHost.getName(), dataHost);
            }
            //获取dataNode
            NodeList dataNodeNList = rootElement.getElementsByTagName("dataNode");
            for (int i = 0; i < dataNodeNList.getLength(); i++) {
                Element dataHostEl = (Element) dataNodeNList.item(i);
                List dataNodeListItem = wrappDataNode(dataHostEl, dataHostMap);
                dataNodeList.addAll(dataNodeListItem);
            }
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (SAXException e) {
            e.printStackTrace();
        } finally {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return dataNodeList;

    }

    public static DataHost wrappDataHost(Element dataHostEl) {
        DataHost dataHost = new DataHost();
        dataHost.setName(dataHostEl.getAttribute("name"));
        NodeList writeHostNode = dataHostEl.getElementsByTagName("writeHost");
        if (writeHostNode.getLength() > 0) {
            Element writeHostEl = (Element) writeHostNode.item(0);
            dataHost.setWriteHostUrl(writeHostEl.getAttribute("url"));
            dataHost.setUserName(writeHostEl.getAttribute("user"));
            dataHost.setPassword(writeHostEl.getAttribute("password"));
            NodeList readNodeList = writeHostEl.getElementsByTagName("readHost");
            if (readNodeList.getLength() > 0) {
                Element readHostEl = (Element) readNodeList.item(0);
                dataHost.setReadHostUrl(readHostEl.getAttribute("url"));
            }
        }
        return dataHost;
    }

    public static List wrappDataNode(Element dataNodeEl, Map dataHostMap) {
        List dataNodeList = new ArrayList<>();
        String nameM = dataNodeEl.getAttribute("name");
        String databaseNameM = dataNodeEl.getAttribute("database");
        String dataHost = dataNodeEl.getAttribute("dataHost");
        if (nameM.equals("dndb0")) {
            DataNode dataNode = new DataNode();
            dataNode.setName(nameM);
            dataNode.setDbName(databaseNameM);
            dataNode.setDataHost(dataHostMap.get(dataHost));
            dataNodeList.add(dataNode);
            return dataNodeList;
        }
        String namePre = nameM.split("\$")[0];
        String databaseNameMPre = databaseNameM.split("\$")[0];
        String numScope = nameM.substring(nameM.indexOf("$") + 1);
        String[] dbBanners = numScope.split("-");
        int startN = Integer.valueOf(dbBanners[0]);
        int endN = Integer.valueOf(dbBanners[1]);
        for (int i = startN; i <= endN; i++) {
            DataNode dataNode = new DataNode();
            dataNode.setName(namePre + i);
            dataNode.setDbName(databaseNameMPre + i);
            dataNode.setDataHost(dataHostMap.get(dataHost));
            dataNodeList.add(dataNode);
        }
        return dataNodeList;
    }

 5、定义导出的模板,在模板中定义数据源选择的规则init()方法,定义数据导出的规则,代码如下

package com.ds.tools.cmd;

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.metadata.Sheet;
import com.ds.tools.datasource.DynamicDataSource;
import com.ds.tools.model.DBoffset;
import com.ds.tools.util.DateUtil;
import com.ds.tools.util.FileUtil;
import com.ds.tools.util.ZipUtil;

public abstract class ExportTemplate{
    private final static Logger _log = LoggerFactory.getLogger(ExportTemplate.class);
    //校验参数
    private final static int MAX_ROW = 1000000;
    Stack stack;
    int DBType;			//0:公共库,1:log1库;2:log2库
    String fileNamePre;
    Class  entityClass;

    public void init() {
        stack = new Stack<>();
        if (DBType == 0) {
            stack.push(new DBoffset(DynamicDataSource.STANDALONE_DB));
        } else if (DBType == 1) {
        	for (String logDb : DynamicDataSource.LOG1_DB_SET) {
                stack.push(new DBoffset(logDb));
            }
        } else if (DBType == 2) {
            for (String logDb : DynamicDataSource.LOG2_DB_SET) {
                stack.push(new DBoffset(logDb));
            }
        } else {
        	for (String otherDb : DynamicDataSource.DB_SET) {
                stack.push(new DBoffset(otherDb));
            }
        }
    }

    
    public abstract void setDBType();

    
    public abstract void setFileNamePre();


    
    public abstract void setEntityClass();

    
    public abstract void validateArgs(String[] args);


    
    public abstract List queryList(String[] args,int startOffset);


    public void exprot(String[] args) {
        validateArgs(args);
        long startTime=System.currentTimeMillis();
        setDBType();
        setFileNamePre();
        setEntityClass();
        init();
        List fileList = new ArrayList<>();
        int fileCount = 1;
        long rowCount = 0;
        boolean needCreatFileFlag = true;
        String fileName = null;
        OutputStream out = null;
        ExcelWriter writer = null;
        Sheet sheet1 = null;
        String dataDir= FileUtil.getDataDir();
        String fileTime = DateUtil.getTime();
        try {
            while (!stack.empty()) {
                DBoffset dBoffset = stack.pop();
                if (needCreatFileFlag) {
                    fileName = fileNamePre + fileTime + "_" + fileCount + ".xlsx";
                    String filePath=dataDir+File.separator+fileName;
                    fileList.add(new File(filePath));
                    out = new FileOutputStream(filePath);
                    writer = EasyExcelFactory.getWriter(out);
                    sheet1 = new Sheet(1, 0, entityClass, "sheet1", null);
                    sheet1.setAutoWidth(Boolean.TRUE);
                }
                DynamicDataSource.setDataSource(dBoffset.getDbName());//动态选择数据源
                _log.debug("当前数据源:{}", dBoffset.getDbName());
                int startOffset = 0;
                while (true) {
                    //单个sheet最大写入100万
                    if (rowCount > MAX_ROW) {
                        //关闭当前文件,重新压栈
                        writer.finish();
                        out.close();
                        fileCount++;
                        rowCount = 0;
                        needCreatFileFlag = true;
                        dBoffset.setOffset(startOffset);
                        stack.push(dBoffset);
                        break;
                    }

                    //重新压栈的需要跳步走
                    needCreatFileFlag = false;
                    if (dBoffset.getOffset() > 0&&rowCount==0) {
                        startOffset = dBoffset.getOffset();
                    }

                    List logList =queryList(args,startOffset);
                    writer.write(logList, sheet1);
                    rowCount = rowCount + logList.size();
                    if (logList.size() < 1000) {
                        break;
                    }
                    startOffset = startOffset + 1000;
                    _log.info(fileName + "已写行数:{}", rowCount);
                }
            }
            writer.finish();
            out.close();
            //压缩文件
            File file = new File(dataDir+File.separator+fileNamePre + fileTime + ".zip");
            ZipUtil.zipFiles(fileList, file);
            //删除源文件
            for (File fileItem : fileList) {
                fileItem.delete();
            }
            _log.info("导出结束!耗时:"+(System.currentTimeMillis()-startTime)/1000/60+"分钟");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


}

6、定义各个导出数据的入口,继承导出模板设置数据源类型,到这里为止,所有数据源动态切换的功能全部完成;代码如下:

package com.ds.tools.cmd;

import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.ds.tools.model.Log1;
import com.ds.tools.service.Log1Service;
import com.ds.tools.util.SpringContextUtil;

public class LogExportorTest1 extends ExportTemplate {

    private final static Logger _log = LoggerFactory.getLogger(LogExportorTest1.class);

    private Log1Service log1Service;


    @Override
    public void setDBType() {
        this.DBType = 1;//设置类型
    }

    @Override
    public void setFileNamePre() {
        this.fileNamePre = "Log1导出";
    }

    @Override
    public void setEntityClass() {
        this.entityClass = Log1.class;
    }

    @Override
    public void validateArgs(String[] args) {
        //init spring 容器
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        log1Service = SpringContextUtil.getBean(Log1Service.class);
    }

    @Override
    public List queryList(String[] args,int startOffset) {
        Integer days=args.length==2?Integer.valueOf(args[1]):0;
        List logList = log1Service.getLog1ByOffset(startOffset, 1000);
        return logList;
    }
}

7、使用main方法直接调用导出数据的入口,则会开启导出;代码如下:

public static void main(String[] args) {
    	System.out.println("开始导出Log1...");
        LogExportorTest1 log1Exportor = new LogExportorTest1();
        log1Exportor.exprot(null);//调用ExportTemplate的exprot方法执行导出
	}

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/590896.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号