項目中需要記錄接口調用的日志,由於目前數量不大,決定根據年進行動態創建表(invoke_interface_log_2019)。使用MyBatis實現(動態SQL)。
1、Dao.xml
<mapper namespace="com.xxx.xxx.dao.manager.system.InvokeInterfaceLogDao"> <!-- 根據表名統計已存在的數量 --> <select id="countTables" parameterType="string" resultType="int" databaseId="mysql"> SELECT count(1) FROM information_schema.TABLES WHERE LCASE(table_name) = #{tableName} </select> <!-- 創建表SQL,參數為表名 --> <update id="createTable" parameterType="string" databaseId="mysql"> CREATE TABLE ${tableName} ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵', `client_id` varchar(50) DEFAULT NULL COMMENT '客戶端標識', `interface_type` tinyint(4) unsigned DEFAULT NULL COMMENT '接口歸類', `interface_name` varchar(50) DEFAULT NULL COMMENT '接口名稱', `invoke_time` datetime DEFAULT NULL COMMENT '調用時間', `is_success` tinyint(1) unsigned DEFAULT '0' COMMENT '是否成功', `fail_type` tinyint(4) unsigned DEFAULT NULL COMMENT '失敗類型', `fail_msg` varchar(255) DEFAULT NULL COMMENT '失敗信息', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='調用接口日志表'; </update> <insert id="insert" parameterType="com.xxx.xxx.xxx.manager.system.InvokeInterfaceLogDO" databaseId="mysql"> INSERT INTO ${tableName} ( client_id, interface_type, interface_name, invoke_time, is_success, fail_type, fail_msg ) VALUES ( #{invokeInterfaceLog.clientId}, #{invokeInterfaceLog.interfaceType}, #{invokeInterfaceLog.interfaceName}, #{invokeInterfaceLog.invokeTime}, #{invokeInterfaceLog.success}, #{invokeInterfaceLog.failType}, #{invokeInterfaceLog.failMsg} ) </insert> </mapper>
2、Dao.java
/** * 接口調用日志數據訪問層接口 * * @author: yemingxiang * @date: 2019/05/28 */ public interface InvokeInterfaceLogDao { /** * 統計存在的表數量 * * @param tableName 表名 * @return 存在的表數量 */ int countTables(String tableName); /** * 創建表 * * @param tableName 表名 */ void createTable(@Param("tableName") String tableName); /** * 新增 * * @param tableName 表名 * @param invokeInterfaceLogDO 調用接口日志數據對象 * @return 新增成功的記錄數 */ int insert(@Param("tableName") String tableName, @Param("invokeInterfaceLog") InvokeInterfaceLogDO invokeInterfaceLogDO); }
3、業務層實現如下:
/** * 調用接口日志業務邏輯層實現 * * @author yemingxiang * @date 2019/05/28 */ @Service public class InvokeInterfaceLogServiceImpl implements InvokeInterfaceLogService { private static final Logger logger = LoggerFactory.getLogger(InvokeInterfaceLogServiceImpl.class); @Autowired private InvokeInterfaceLogDao invokeInterfaceLogDao; @Override public boolean save(InvokeInterfaceLogDO invokeInterfaceLogDO) { int currentYear = LocalDate.now().getYear(); String key = String.format("cs:system:invokeInterfaceLog:%s:string", currentYear); logger.info("接口調用日志表Redis Key:" + key); String tableName = RedisUtils.getString(key); logger.info("接口調用日志表表名:" + tableName); if (tableName != null) { return invokeInterfaceLogDao.insert(tableName, invokeInterfaceLogDO) == 1; } String newTableName = String.format("invoke_interface_log_%s", currentYear); RedisUtils redisUtils = new RedisUtils(newTableName); try { if (redisUtils.lock()) { logger.info("獲得Redis鎖"); // 第二層判斷,防范作用 tableName = RedisUtils.getString(key); logger.info("再次獲取接口調用日志表表名:" + tableName); if (tableName != null) { return invokeInterfaceLogDao.insert(tableName, invokeInterfaceLogDO) == 1; } int count = invokeInterfaceLogDao.countTables(newTableName); if (count == 0) { logger.info("創建接口調用日志表,並將表名保存到Redis"); invokeInterfaceLogDao.createTable(newTableName); RedisUtils.set(key, newTableName); } return invokeInterfaceLogDao.insert(newTableName, invokeInterfaceLogDO) == 1; } } catch (InterruptedException e) { logger.error("新增調用接口日志時獲取Redis鎖失敗_" + e.getMessage(), e); } finally { redisUtils.unlock(); logger.info("釋放Redis鎖"); } return false; } }