业务场景,作为下游系统,对上游多个系统有读,没写操作。设计写操作可增添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);
}
}
相关阅读: