springboot实现动态数据源访问多个数据
处理的问题
需求:
老板:小江啊!由于我们的数据量太多,现在我们的数据库是这样设计,一个月一个数据库,一天一张表。你去改改吧!
我:好的老板!
于是,我在网上开始找找相应的技术。
网上动态更换数据库的方式,大量的都是编写多个配置,因为他们都是一年创建一个数据库,一个月创建一个表,有一个博主说一个项目最长的使用年限也就是十年,无非就是配置十个连接。
我一想,好像是这个道理。(有个屁道理。)我是一个月创建一个数据库,十年就是120个配置。
经过千辛万苦的查找,终于找到了如何通过Spring Boot配置动态数据源访问多个数据库
但是,我按照这样完成了后,遇到了一些问题。
我在使用这个方法之后,程序在第一次连接的时候会报一个错误配置类型不对。
所以我有找到了一个方法,多数据库的查询。
数据库的名称是有规定的。
例如
SELECT * FROM hnbd_db_business202106.t_dwxx04
但这种方法在yml的配置文件中配置连接的数据库一定要存在,还有这样的方法一个的数据库要放在一个服务器上。
下面是我的一些代码
Controller
import com.hnbd.dwxx.pojo.DeviceData;
import com.hnbd.dwxx.pojo.response.ApiResponse;
import com.hnbd.dwxx.pojo.response.ApiResponseStatus;
import com.hnbd.dwxx.pojo.response.ApiResponseWithData;
import com.hnbd.dwxx.service.DwxxService;
import com.hnbd.dwxx.untils.MyCalendarUntil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
/**
* Test
*
* @创建人 江枫沐雪
* @创建时间 2021/7/12 19:38
*/
@Slf4j
@RestController
@RequestMapping("dwxx")
public class DwxxController {
@Autowired
private DwxxService dwxxService;
/**
* 查询时间段内的所有数据信息
* @param sender 编码
* @param startTime 开始时间
* @param endTime 结束时间
* @return
*/
@GetMapping("FindListByTime")
public ApiResponse test(@RequestParam("sender") String sender,
@RequestParam("startTime") String startTime,
@RequestParam("endTime") String endTime) {
//判断传递的参数是否为空
if (StringUtils.isAllBlank(sender, startTime, endTime)) {
return ApiResponseWithData.ofStatus(ApiResponseStatus.INVALID_PARAM);
}
try {
//求两个日期的差值
int chaMonth = MyCalendarUntil.getMonthSpace(startTime, endTime);
int chaDay = MyCalendarUntil.getDateSpace(startTime, endTime);
List deviceData = new ArrayList<>();
//天数
int days = 0;
//月的差值是否正确
if (chaMonth >= 0) {
//循环,不同月份,使用不同的数据库
for (int a = 0; a <= chaMonth; a++) {
//根据不同的月份,生成不同的数据库
String newMonth = MyCalendarUntil.getMonth(startTime, a);
String dataSource = "hnbd_db_business" + newMonth;
//判断数据库是否存在
int mysqlCount = dwxxService.getTableName(dataSource, "");
//如果数据库存在
if (mysqlCount != 0) {
//
//确定循环的次数
int forNum = MyCalendarUntil.getForNum(days, chaDay, newMonth);
//循环查询数据
for (int i = 0; i <= forNum; i++) {
//生成表的 后缀
int day = MyCalendarUntil.getDay(startTime, days);
days++;
String table = "t_dwxx" + String.format("%02d", day);
//判断数据库中这个表是否存在
int count = dwxxService.getTableName(dataSource, table);
//如果表存在 更换数据库的链接 查询出对应的数据
if (count == 1) {
List list = dwxxService.FindListByTime(sender, startTime, endTime, dataSource+"."+table);
if (list != null){
for (int j = 0; j < list.size(); j++) {
deviceData.add(list.get(j));
}
}
}
}
} else {
//数据库不存在,跳出这一次循环
continue;
}
}
//返回数据
return ApiResponseWithData.ofSuccess(deviceData);
} else {
//时间差有错
return ApiResponseWithData.ofStatus(ApiResponseStatus.INVALID_PARAM);
}
} catch (ParseException e) {
log.error("求表数据失败!");
return ApiResponseWithData.ofStatus(ApiResponseStatus.INVALID_PARAM);
}
}
}
Service
import com.fasterxml.jackson.annotation.JsonFormat;
import com.hnbd.dwxx.pojo.DeviceData;
import com.hnbd.dwxx.pojo.Machine;
import com.hnbd.dwxx.pojo.TableData;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Service;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* DwxxService
*
* @创建人 江枫沐雪
* @创建时间 2021/7/9 17:24
*/
@Service
public class DwxxService {
@Autowired
private SqlSessionTemplate sqlSessionTemplate;
/**
* 查询时间段内的所有数据信息
*
* @param sender 定位方北斗卡号
* @param startTime 开始时间
* @param endTime 结束时间
* @return
*/
public List FindListByTime(String sender, String startTime, String endTime, String table) {
HashMap<String, Object> map = new HashMap<>();
map.put("sender", sender);
map.put("startTime", startTime);
map.put("endTime", endTime);
map.put("table", table);
List list = sqlSessionTemplate.selectList("DateMapper.FindListByTime", map);
return list;
}
/**
* 判断数据库中是否存在这张表
*
* @param dataSource 数据库的名字
* @param table 表名
* @return
*/
public int getTableName(String dataSource, String table) {
HashMap<String, Object> map = new HashMap<>();
map.put("dataSource", dataSource);
map.put("table", table);
int count = sqlSessionTemplate.selectOne("DateMapper.getTableName", map);
return count;
}
}
Mapper
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="DateMapper">
<!-- 查询时间段内的所有数据信息 -->
<select id="FindListByTime" resultType="map" parameterType="map">
select h.Sender sender,h.RecvTime recvTime,cast(h.PosLong/1000000 as decimal(9,6)) longitude,cast(h.PosLat/1000000 as decimal(9,6)) latitude
from ${table} h
where h.Sender =#{sender} AND date_format(h.RecvTime,'%Y-%m-%d %H:%i:%S') between #{startTime} AND #{endTime}
ORDER BY h.RecvTime
</select>
<!-- 判断数据库中是否存在这张表 -->
<select id="getTableName" resultType="int" parameterType="map">
select COUNT(*)
from information_schema.tables
where table_schema=#{dataSource}
<if test="table != null and table != '' ">
AND table_name =#{table}
</if>
</select>
</mapper>
计算出日期的工具类untils
package com.hnbd.dwxx.untils;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
/**
* MyCalendarUntil
* 计算两个日期的差值
* @创建人 江枫沐雪
* @创建时间 2021/7/12 11:38
*/
public class MyCalendarUntil {
/**
* 月份的差值
* @param startTime 开始时间
* @param endTime 结束时间
* @return 相差的月数
* @throws ParseException
*/
public static int getMonthSpace(String startTime, String endTime) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
int result = 0;
//时间写入 结束时间
Calendar cal1 = new GregorianCalendar();
cal1.setTime(sdf.parse(endTime));
//时间写入 开始时间
Calendar cal2 = new GregorianCalendar();
cal2.setTime(sdf.parse(startTime));
//求取差值
result = (cal1.get(Calendar.YEAR) - cal2.get(Calendar.YEAR)) * 12 + cal1.get(Calendar.MONTH) - cal2.get(Calendar.MONTH);
return result;
}
/**
* 天数的差值
* @param startTime 开始时间
* @param endTime 结束时间
* @return 相差的天数
* @throws ParseException
*/
public static int getDateSpace(String startTime, String endTime) throws ParseException {
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd");
Calendar calst = Calendar.getInstance();
Calendar caled = Calendar.getInstance();
calst.setTime(sf.parse(startTime));
caled.setTime(sf.parse(endTime));
//设置时间为0时
calst.set(Calendar.HOUR_OF_DAY, 0);
calst.set(Calendar.MINUTE, 0);
calst.set(Calendar.SECOND, 0);
caled.set(Calendar.HOUR_OF_DAY, 0);
caled.set(Calendar.MINUTE, 0);
caled.set(Calendar.SECOND, 0);
//得到两个日期相差的天数
int days = ((int)(caled.getTime().getTime()/1000)-(int)(calst.getTime().getTime()/1000))/3600/24;
return days;
}
/**
* 根据时间字符串,求取一个月份的第几天
* @param time 时间
* @return 天数
* @throws ParseException
*/
public static int getDay(String time) throws ParseException {
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd");
Calendar calendar = Calendar.getInstance();
calendar.setTime(sf.parse(time));
//求取第几天
int day = calendar.get(Calendar.DATE);
return day;
}
/**
* 根据时间字符串,和相加的天数,求取相应月份的第几天
* @param time 开始时间
* @param days 相加的天数
* @return
* @throws ParseException
*/
public static int getDay(String time,int days) throws ParseException {
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd");
Calendar calendar = Calendar.getInstance();
calendar.setTime(sf.parse(time));
calendar.add(Calendar.DATE,days);
//求取添加天数后 相应月份的日期。
int day = calendar.get(Calendar.DATE);
return day;
}
/**
* 根据时间字符串,和相加的月份,求取相应月份数据
* @param time 开始时间
* @param num 相加的月份
* @return 相应月份数据
* @throws ParseException
*/
public static String getMonth(String time,int num) throws ParseException {
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd");
SimpleDateFormat month = new SimpleDateFormat("yyyyMM");
//月份添加
Calendar calendar = Calendar.getInstance();
calendar.setTime(sf.parse(time));
calendar.add(Calendar.MONTH,num);
//格式转化
String format = month.format(calendar.getTime());
return format;
}
/**
* 根据时间字符串,求取月份
* @param time 时间 字符串类型
* @return 月份
* @throws ParseException
*/
public static int getMonth(String time) throws ParseException {
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd");
SimpleDateFormat month = new SimpleDateFormat("yyyyMM");
String format = month.format(sf.parse(time));
//求取月份
return Integer.parseInt(format);
}
/**
* 根据时间,求取月份
* @param time 时间 date类型
* @return 月份
*/
public static int getMonth(Date time) {
//格式转化
SimpleDateFormat month = new SimpleDateFormat("yyyyMM");
String format = month.format(time);
//求取月份
return Integer.parseInt(format);
}
/**
* 应该循环的次数
* @param days 已经循环了多少天
* @param chaDay 相差的天数
* @param newMonth 月份信息
* @return 应该循环的次数
* @throws ParseException
*/
public static int getForNum(int days,int chaDay,String newMonth) throws ParseException {
SimpleDateFormat month = new SimpleDateFormat("yyyyMM");
Calendar calendar = Calendar.getInstance();
calendar.setTime(month.parse(newMonth));
int i = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
int cha = chaDay - days;
return cha >= i ? i : cha;
}
}
总结
如果还想动态的更换连接的服务器。我建议使用Sharding-JDBC