
Sharing-JDBC实现,《Sharing-JDBC实现》按月分表就每月底创建第二月的表,按日分表就每天晚创建后几天的表;在resources下创建sys_operate_log.sql文件,内容如下:
通过%s通配符用于替换操作;
-- 创建表:sys_operate_log_yearMonth
CREATE TABLE `%s`
(
`id` bigint NOT NULL COMMENT '主键',
`model` varchar(20) DEFAULT NULL COMMENT '模块名称',
`name` varchar(20) DEFAULT NULL COMMENT '操作名称',
`ip` varchar(20) DEFAULT NULL COMMENT '请求ip',
`service_name` varchar(50) DEFAULT NULL COMMENT '当前服务名称',
`browser` varchar(50) DEFAULT NULL COMMENT '操作浏览器',
`os` varchar(50) DEFAULT NULL COMMENT '操作系统',
`url` varchar(500) DEFAULT NULL COMMENT '请求url',
`user_id` bigint DEFAULT NULL COMMENT '操作人ID',
`user_name` varchar(100) DEFAULT NULL COMMENT '操作人名称',
`http_method` varchar(20) DEFAULT NULL COMMENT '请求类型',
`request_params` text COMMENT '请求参数',
`response_params` text COMMENT '响应参数',
`operate_type` varchar(200) DEFAULT '' COMMENT '方法名称',
`operate_event` varchar(200) DEFAULT NULL COMMENT '类名称',
`is_success` int DEFAULT NULL COMMENT '是否成功:1-成功 2-失败',
`error_code` varchar(10) DEFAULT NULL COMMENT '错误代码',
`error_msg` varchar(200) DEFAULT NULL COMMENT '错误信息',
`time` bigint DEFAULT NULL COMMENT '响应时长(ms)',
`error_trace` text COMMENT '错误堆栈信息',
`create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '操作时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3
ShardTableHandler类中会检测指定创建的表是否存在,只有不存在时才会执行创建指令。
ShardTableHandler
import com.zaxxer.hikari.HikariDataSource;
import org.apache.commons.io.FileUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.ResourceUtils;
import javax.annotation.Resource;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.sql.*;
/**
* @Author: LiHuaZhi
* @Description:
**/
@Component
public class ShardTableHandler {
/**
* 引入数据源,也可以是 DruidDataSource,此处根据项目配置来定
*/
@Resource
private HikariDataSource dataSource;
public static final String TABLE_NAME_PREFIX = "sys_operate_log_";
/**
* 验证表是否存在,按月维度进行分表
*/
public void checkExistTable(String tableName) {
Connection conn = null;
try {
conn = dataSource.getConnection();
String[] types = new String[]{"TABLE", "VIEW"};
// 查询当前数据源下的表数据
ResultSet rs = conn.getMetaData().getTables(conn.getCatalog(), conn.getSchema(), "%", types);
boolean exist = false;
// 判断是否存在
while (rs.next()) {
String tableStr = rs.getString("TABLE_NAME");
if (tableStr.equals(tableName)) {
exist = true;
break;
}
}
// 不存在则创建
if (!exist) {
System.out.println("创建表");
// 获取文件的URL
File file = ResourceUtils.getFile("classpath:sys_operate_log.sql");
// 转成string输入文本
String content = FileUtils.readFileToString(file, StandardCharsets.UTF_8);
// 获取sql
String sql = String.format(content, tableName);
PreparedStatement ps = conn.prepareStatement(sql);
ps.execute();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (conn != null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
在项目启动时,就是调用
ShardTableHandler的checkExistTable()方法检测是否进行表的创建。
ShardTableRunner
import com.lhz.demo.handler.ShardTableHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.time.LocalDate;
/**
* @Author: LiHuaZhi
* @Description: 启动完成后加载
**/
@Component
@Slf4j
public class ShardTableRunner implements ApplicationRunner {
@Resource
private ShardTableHandler shardTableHandler;
@Override
public void run(ApplicationArguments args) throws Exception {
log.info("==== 系统运行开始 ====");
// 获取当前年月
int year = LocalDate.now().getYear();
int month = LocalDate.now().getMonthValue();
String monthStr = month > 10 ? month + "" : "0" + month;
// 比如名称为:sys_operate_log_202209,及表示存储2022年9月数据的表
String tableName = ShardTableHandler.TABLE_NAME_PREFIX + year + monthStr;
shardTableHandler.checkExistTable(tableName);
}
}
虽然项目在启动时会进行一次
数据表是否存在的检测,但是为了包装实时性,可以通过定时任务进行数据表的创建,具体的创建频次可以根据业务实际情况来的。
ShardTableTask
import com.lhz.demo.handler.ShardTableHandler;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.time.LocalDate;
/**
* @Author: LiHuaZhi
* @Date: 2022/9/12 10:56
* @Description:
**/
@Component
public class ShardTableTask {
@Resource
private ShardTableHandler shardTableHandler;
/**
* 每月最后一天23:00执行任务 生成下月的表
*
* @throws Exception
*/
@Scheduled(cron = "0 0 23 L 1/1 ?")
public void shardTableTask() throws Exception {
// 获取当前年月
int year = LocalDate.now().getYear();
int month = LocalDate.now().getMonthValue();
// 月份 `+1`,创建下月表数据
month = month >= 12 ? 1 : month + 1;
String monthStr = month >= 10 ? month + "" : "0" + month;
// 比如名称为:sys_operate_log_202209,及表示存储2022年9月数据的表
String tableName = ShardTableHandler.TABLE_NAME_PREFIX + year + monthStr;
shardTableHandler.checkExistTable(tableName);
}
}

LogMapper.xml
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lhz.demo.mapper.LogMapper">
<select id="listOperateLog"
resultType="com.lhz.demo.entity.OperateLog">
select
a.*
from sys_operate_log_${suffix} a
<where>
<if test="param.model != null and param.model != ''">
and instr(a.model,#{param.model})
if>
<if test="param.name != null and param.name != ''">
and instr(a.name,#{param.name})
if>
<if test="param.userName != null and param.userName != ''">
and instr(a.user_name,#{param.userName})
if>
<if test="param.httpMethod != null">
and http_method =#{param.httpMethod}
if>
<if test="param.isSuccess != null">
and is_success =#{param.isSuccess}
if>
<if test="param.startTime != null and param.endTime != null">
and create_time BETWEEN #{param.startTime} and #{param.endTime}
if>
where>
select>
<insert id="insertOperateLog">
insert into sys_operate_log_${suffix}(id, model, name, ip, browser, os, url,
user_id, user_name, http_method,
request_params, response_params, is_success,
error_code, error_msg, time, error_trace, create_time)
values (#{log.id}, #{log.model}, #{log.name}, #{log.ip}, #{log.browser}, #{log.os},
#{log.url}, #{log.userId}, #{log.userName}, #{log.httpMethod},
#{log.requestParams}, #{log.responseParams}, #{log.isSuccess}, #{log.errorCode},
#{log.errorMsg}, #{log.time}, #{log.errorTrace}, #{log.createTime})
insert>
mapper>
public class ShardTableService {
@Resource
private LogMapper logMapper;
/**
* 测试查询
*
* @param param
* @return
*/
public List<OperateLog> list(OperateLogParam param) {
// 获取当前年月
int year = LocalDate.now().getYear();
int month = LocalDate.now().getMonthValue();
String monthStr = month > 10 ? month + "" : "0" + month;
String suffix = year + "" + monthStr;
return logMapper.listOperateLog(suffix, param);
}
/**
* 测试新增
*
* @return
*/
public String add() {
// 获取当前年月
int year = LocalDate.now().getYear();
int month = LocalDate.now().getMonthValue();
String monthStr = month > 10 ? month + "" : "0" + month;
String suffix = year + "" + monthStr;
// 模拟实体类
OperateLog operateLog = new OperateLog();
long id = new Random().nextInt(999999999);
operateLog.setId(id);
operateLog.setIp("127.0.0.1");
// 省略其他字段数据
logMapper.insertOperateLog(suffix, operateLog);
return "success";
}
}
