多数据源查询

业务场景,作为下游系统,对上游多个系统有读,没写操作。设计写操作可增添api,涉及事务可查看相关阅读。

依赖:

       <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.8</version>
        </dependency>
package org.demo.spring.mysql;


import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;

/**
 * 多数据源配置
 */
@Configuration
public class JdbcTemplateDemoConfig {

    /**
     * <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
     * <property name="url" value="${trade.jdbc.url}"/>
     * <property name="username" value="${trade.jdbc.user}"/>
     * <property name="password" value="${trade.jdbc.password}"/>
     * <property name="initialSize" value="20"/>
     * <property name="minIdle" value="10"/>
     * <property name="maxActive" value="100"/>
     * <property name="maxWait" value="30"/>
     * <property name="timeBetweenEvictionRunsMillis" value="60000"/>
     * <property name="minEvictableIdleTimeMillis" value="300000"/>
     * <property name="validationQuery" value="select 1 from dual"/>
     * <property name="testWhileIdle" value="true"/>
     * <property name="testOnBorrow" value="false"/>
     * <property name="testOnReturn" value="false"/>
     * <property name="poolPreparedStatements" value="false"/>
     *
     * @return
     */

    // 数据源之一
    @Bean
    public JdbcTemplate appJdbcTemplate() {
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/app?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        dataSource.setInitialSize(20);
        dataSource.setMinIdle(10);
        dataSource.setMaxActive(100);
        dataSource.setTimeBetweenEvictionRunsMillis(60000);
        dataSource.setMinEvictableIdleTimeMillis(300000);
        dataSource.setValidationQuery("select 1 from dual");
        dataSource.setTestWhileIdle(true);
        dataSource.setTestOnBorrow(false);
        dataSource.setTestOnReturn(false);
        dataSource.setPoolPreparedStatements(false);
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }


}
package org.demo.spring.mysql;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * Description:
 * mysql 多数据库查询执行器
 */
@Component
@Slf4j(topic = "mysqlLogger")
public class SimpleSqlRunner implements InitializingBean {

    private final Map<String, JdbcTemplate> jdbcTemplateMap = new ConcurrentHashMap<>(24);
    private final Map<String, String[]> simpleTablePrimeKeyMap = new ConcurrentHashMap<>(24);

    @Resource
    private JdbcTemplate appJdbcTemplate;


    @Override
    public void afterPropertiesSet() {
        jdbcTemplateMap.put("app", appJdbcTemplate);
        jdbcTemplateMap.put("ser", appJdbcTemplate);
    }

    /**
     * 执行简表Mysql查询
     *
     * @param dbName    数据库名
     * @param tableName 表名
     * @param sql       Sql脚本
     * @return 数据列表
     */
    public List<Map<String, Object>> findBySql(String dbName, String tableName, String sql) {
        long startTime = System.currentTimeMillis();
        try {
            List<Map<String, Object>> dataMapList = this.jdbcTemplateMap.get(dbName).query(sql, new OColumnMapRowMapper());
            return dataMapList;
        } finally {
            log.info("Mysql execute sql[{}] cost [{}]ms.", sql, (System.currentTimeMillis() - startTime));
        }
    }

    /**
     * 根据主键批量查询MySql
     *
     * @param dbName    数据库
     * @param tableName 表
     * @param ids       主键ID值列表
     * @return 数据列表
     */
    public List<Map<String, Object>> findByIds(String dbName, String tableName, List<String> ids) {
        String[] primeKeys = this.getPrimeKeys(dbName, tableName);
        if (ArrayUtils.isEmpty(primeKeys)) {
            log.error("According to the primary key query [{}.{}] failed, the related table information configuration" +
                    " was not found.", dbName, tableName);
            return Collections.emptyList();
        }
        if (primeKeys.length == 1) {
            StringBuilder sqlBuilder = new StringBuilder("select * from ").append(dbName).append(".").append(tableName);
            sqlBuilder.append(" where ").append(primeKeys[0]).append(" in (");
            ids.forEach(id -> sqlBuilder.append("'").append(id).append("',"));
            String executeSql = StringUtils.removeEnd(sqlBuilder.toString(), ",") + ")";
            List<Map<String, Object>> dataList = findBySql(dbName, tableName, executeSql);
            return ids.stream().map(id -> {
                if (CollectionUtils.isEmpty(dataList)) {
                    return Collections.<String, Object>emptyMap();
                } else {
                    return dataList.stream().filter(data -> data.get(primeKeys[0]).toString().equalsIgnoreCase(id))
                            .findFirst()
                            .orElse(Collections.emptyMap());
                }
            }).collect(Collectors.toList());
        } else {
            return ids.stream().map(id -> findById(dbName, tableName, id)).collect(Collectors.toList());
        }
    }

    /**
     * 根据主键查询Mysql
     *
     * @param dbName    数据库
     * @param tableName 表
     * @param id        主键值
     * @return 数据
     */
    public Map<String, Object> findById(String dbName, String tableName, String id) {
        String[] primeKeys = this.getPrimeKeys(dbName, tableName);
        if (ArrayUtils.isEmpty(primeKeys)) {
            log.error("According to the primary key query [{}.{}] failed, the related table information configuration" +
                    " was not found.", dbName, tableName);
            return Collections.emptyMap();
        }
        StringBuilder sqlBuilder = new StringBuilder();
        sqlBuilder.append("select * from ").append(dbName).append(".").append(tableName).append(" where ");
        if (primeKeys.length == 1) {
            sqlBuilder.append(primeKeys[0]).append("='").append(id).append("'");
        } else {
            String[] idValues = this.getIdValues(id);
            for (int i = 0; i < primeKeys.length; i++) {
                String primeKey = primeKeys[i];
                String primeValue = idValues[i];
                sqlBuilder.append(primeKey).append("='").append(primeValue).append("'");
                if (i < primeKeys.length - 1) {
                    sqlBuilder.append(" and ");
                }
            }
        }
        List<Map<String, Object>> dataList = this.findBySql(dbName, tableName, sqlBuilder.toString());
        return CollectionUtils.isEmpty(dataList) ? Collections.emptyMap() : dataList.get(0);
    }


    /**
     * 执行统计查询
     *
     * @param dbName    数据库
     * @param tableName 表
     * @param countSql  Sql
     * @return 数量
     */
    public int countBySql(String dbName, String tableName, String countSql) {
        long startTime = System.currentTimeMillis();
        try {
            String totalCount = this.jdbcTemplateMap.get(dbName).queryForObject(countSql, String.class);
            return NumberUtils.toInt(totalCount, 0);
        } finally {
            log.info("Mysql execute sql[{}] cost [{}]ms.", countSql, (System.currentTimeMillis() - startTime));
        }
    }

    /**
     * 获取主键
     * getPrimaryKeys
     *
     * @param dbName    数据库
     * @param tableName 表
     * @return 主键列表
     */
    public String[] getPrimeKeys(String dbName, String tableName) {
        String cacheKey = dbName + "." + tableName;
        if (simpleTablePrimeKeyMap.containsKey(cacheKey)) {
            return simpleTablePrimeKeyMap.get(cacheKey);
        } else {
            return getPrimeKeysFromMysql(dbName, tableName);
        }
    }

    private synchronized String[] getPrimeKeysFromMysql(String dbName, String tableName) {
        String cacheKey = dbName + "." + tableName;
        if (simpleTablePrimeKeyMap.containsKey(cacheKey)) {
            return simpleTablePrimeKeyMap.get(cacheKey);
        } else {
            try {
                DataSource dataSource = this.jdbcTemplateMap.get(dbName).getDataSource();
                if (dataSource == null) {
                    throw new SQLException("无法获取数据库[" + dbName + "]连接");
                }
                Connection connection = dataSource.getConnection();
                ResultSet resultSet = connection.getMetaData().getPrimaryKeys(dbName, null, tableName);
                resultSet.last();
                String[] primeKeys = new String[resultSet.getRow()];
                resultSet.beforeFirst();
                int index = 0;
                while (resultSet.next()) {
                    String columnName = resultSet.getString("COLUMN_NAME");
                    primeKeys[index++] = columnName.toLowerCase();
                }
                log.info("获取数据库表[{}.{}]的主键为:{}", dbName, tableName, primeKeys);
                simpleTablePrimeKeyMap.put(cacheKey, primeKeys);
                return primeKeys;
            } catch (SQLException e) {
                log.error("获取数据库表[{}.{}]的主键异常,", dbName, tableName, e);
                return null;
            }
        }
    }

    private String[] getIdValues(String id) {
        if (id.contains(";")) {
            return StringUtils.split(id, ";");
        }
        return new String[]{id};
    }


}
package org.demo.spring.mysql;

import org.springframework.jdbc.core.ColumnMapRowMapper;

/**
 * 
 * 列转换器,解决列大小写的问题
 *
 */
public class OColumnMapRowMapper extends ColumnMapRowMapper {

    @Override
    protected String getColumnKey(String columnName) {
        return super.getColumnKey(columnName).toLowerCase();
    }
}

单测:

public class SimpleSqlRunnerTest extends BaseTest{

    @Autowired
    private SimpleSqlRunner simpleSqlRunner;

    @Test
    public void test(){
        String dbName = "app";
        String tableName = "user";
        int pageSize = 10;
        int start = 0;
        String tbName = dbName + "." + tableName;
        String where = "age>5";
        String querySql = String.format("select * from %s WHERE %s limit %d,%d",
                tbName, where, start, pageSize);
        List<Map<String, Object>> dataMapList = this.simpleSqlRunner.findBySql(dbName, tableName, querySql);
        System.out.println(dataMapList);


        String[] primeKeys = simpleSqlRunner.getPrimeKeys(dbName, tableName);
        System.out.println(primeKeys);

    }
}

相关阅读:

JdbcTemplate多数据源详解

JDBCTemplate+事务管理篇

猜你喜欢

转载自blog.csdn.net/meser88/article/details/120973766