需求
记录用户的增删改操作到数据库:
- 是谁修改的
- 修改了哪张表
- 修改了哪条数据
- 操作的时间
开发环境
- Springboot
- Mybatis
- Lombok
依赖
<!-- Mybatis依赖 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.1</version>
</dependency>
<!-- mysql依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- AOP依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- Lombok依赖 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- 连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
数据库
用户表
CREATE TABLE `user` (
`oid` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用户主键',
`userName` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户名',
PRIMARY KEY (`oid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
日志表
CREATE TABLE `log` (
`oid` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`tableName` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '表名',
`tableId` int(11) NOT NULL COMMENT '操作那个表中的主键',
`type` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '操作方式',
`userId` bigint(20) NOT NULL COMMENT '操作用户id',
`time` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '操作时间',
PRIMARY KEY (`oid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
实体类
用户
/**
* @author dclup
* @Description 用户实体类
**/
@Data
public class UserDO {
/**
* 用户id
*/
private Long userId;
/**
* 用户姓名
*/
private String userName;
}
日志
/**
* @author dclup
* @Description 日志实体类
**/
@Data
public class LogDO {
/**
* 主键
*/
private Long oid;
/**
* 表名
*/
private String tableName;
/**
* 操作那个表中的主键
*/
private Long tableId;
/**
* 操作方式
*/
private String type;
/**
* 操作时间
*/
private String time;
/**
* 操作人id
*/
private Long userId;
}
枚举类
使用枚举类限定类型,防止瞎传。
主要用来存放==数据库表名==、==操作类型==和==操作那张表的主键id==。
此项目只用到了user
的相关信息,department
只是用来举例。
数据库表名
/**
* @author dclup
* @Description 数据库表名
**/
public enum TableName {
UNKNOWN("unknown"),
USER("user"),
DEPARTMENT("department");
private final String name;
TableName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
操作类型
/**
* @author dclup
* @Description 操作类型名称
**/
public enum TypeName {
UNKNOWN("unknown"),
INSERT("insert"),
UPDATE("update"),
DELETE("delete");
private final String name;
TypeName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
id名称
/**
* @author dclup
* @Description 某个表主键名称
**/
public enum IdName {
UNKNOWN("unknown"),
USERID("userId"),
DEPARTMENTID("departmentId");
private final String name;
IdName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
以下是删改查的基本实现(可以不用看):基本实现开始
Controller
/**
* @author dclup
* @Description 用户信息
**/
@RestController
public class UserController {
@Autowired
private UserService userService;
/**
* 获取一位用户信息
*
* @param userId 用户id
* @return
*/
@GetMapping("/user/{userId}")
public Map<String, Object> getByPrimaryKey(
@PathVariable("userId") Long userId) {
return userService.getByPrimaryKey(userId);
}
/**
* 修改一位用户信息
* @param userDO
* @param userId userId 用户id
* @return
*/
@PutMapping("/user/{userId}")
@Log(table = TableNameEnum.USER,type = TypeNameEnum.UPDATE)
public Map<String, Object> updateByPrimaryKey(
@RequestBody UserDO userDO,
@PathVariable("userId") Long userId) {
userDO.setUserId(userId);
return userService.updateByPrimaryKey(userDO);
}
/**
* 删除一位用户信息
* @param userId userId 用户id
* @return
*/
@DeleteMapping("/user/{userId}")
@Log(table = TableNameEnum.USER,type = TypeNameEnum.DELETE)
public Map<String, Object> deleteByPrimaryKey(
@PathVariable("userId") Long userId
) {
return userService.deleteByPrimaryKey(userId);
}
}
Service
用户
/**
* @author dclup
* @create 2020-04-27 09:58
**/
public interface UserService {
/**
* 获取用户信息
* @param userId
* @return
*/
Map<String, Object> getByPrimaryKey(Long userId);
/**
* 修改用户信息
* @param userDO
* @return
*/
Map<String, Object> updateByPrimaryKey(UserDO userDO);
/**
* 删除用户信息
* @param userId
* @return
*/
Map<String, Object> deleteByPrimaryKey(Long userId);
}
日志
/**
* @author dclup
* @Description 日志服务层
**/
public interface LogService {
/**
* 插入日志
* @param logDO
* @return
*/
void insertLog(LogDO logDO);
}
ServiceImpl
用户
/**
* @author dclup
* @Description 用户信息管理 服务层实现
**/
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
/**
* 获取用户信息
*
* @param userId
* @return
*/
@Override
public Map<String, Object> getByPrimaryKey(Long userId) {
return userMapper.getByPrimaryKey(userId);
}
/**
* 修改用户信息
*
* @param userDO
* @return
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Map<String, Object> updateByPrimaryKey(UserDO userDO) {
if (userMapper.updateByPrimaryKey(userDO) > 0){
return userMapper.getByPrimaryKey(userDO.getUserId());
}
HashMap<String, Object> result = new HashMap<>();
result.put("result","修改失败");
return result;
}
/**
* 删除用户信息
*
* @param userId
* @return
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Map<String, Object> deleteByPrimaryKey(Long userId) {
HashMap<String, Object> result = new HashMap<>();
if (userMapper.deleteByPrimaryKey(userId) > 0){
result.put("result","删除成功");
}else {
result.put("result","删除失败");
}
return result;
}
}
日志
/**
* @author dclup
* @Description 日志信息管理 服务层实现
**/
@Service
public class LogServiceImpl implements LogService {
@Autowired
private LogMapper logMapper;
/**
* 插入日志
*
* @param logDO
* @return
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void insertLog(LogDO logDO) {
if (logMapper.insertLog(logDO) > 0){
System.out.println("日志插入成功");
return;
}
System.out.println("日志插入失败");
}
}
Dao 接口
用户
/**
* @author dclup
* @Description 用户信息管理数据层
**/
@Mapper
@Repository
public interface UserMapper {
/**
* 获取用户信息
*
* @param userId
* @return
*/
Map<String,Object> getByPrimaryKey(@Param("userId") Long userId);
/**
* 修改用户信息
*
* @param userDO
* @return
*/
Integer updateByPrimaryKey(UserDO userDO);
/**
* 删除用户信息
*
* @param userId
* @return
*/
Integer deleteByPrimaryKey(@Param("userId") Long userId);
}
日志
/**
* @author dclup
* @Description 日志管理数据层
**/
@Mapper
@Repository
public interface LogMapper {
/**
* 插入操作日志
* @param logDO
* @return 受影响行数
*/
Integer insertLog(LogDO logDO);
}
Mapper XML
用户
<mapper namespace="com.dclup.dao.UserMapper">
<select id="getByPrimaryKey" resultType="java.util.Map">
SELECT userName
FROM user
WHERE oid = #{userId,jdbcType=BIGINT}
</select>
<update id="updateByPrimaryKey">
UPDATE user
SET userName = #{userName,jdbcType=VARCHAR}
WHERE oid = #{userId,jdbcType=BIGINT}
</update>
<delete id="deleteByPrimaryKey">
DELETE
FROM user
WHERE oid = #{userId,jdbcType=BIGINT}
</delete>
</mapper>
日志
<mapper namespace="com.dclup.dao.LogMapper">
<insert id="insertLog">
INSERT INTO log
(tableName, tableId, type, `time`, userId)
VALUES (#{tableName,jdbcType=VARCHAR},
#{tableId,jdbcType=BIGINT},
#{type,jdbcType=VARCHAR},
#{time,jdbcType=VARCHAR},
#{userId,jdbcType=BIGINT})
</insert>
</mapper>
以上是删改查的基本实现(可以不用看):基本实现结束
自定义Log注解
- 使用==枚举类==限定类型
- 此注解限定为==方法级别==
/**
* @author dclup
* @Description 日志注解
**/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
/**
* 操作方式 默认unknown
*/
TypeNameEnum type() default TypeNameEnum.UNKNOWN;
/**
* 操作的表名 默认unknown
*/
TableNameEnum table() default TableNameEnum.UNKNOWN;
/**
* 实体类的主键名 默认unknown
*/
IdNameEnum id() default IdNameEnum.UNKNOWN;
}
定义切面
- 使用
@Aspect
注解定义切面 - 切点:使用
@Log
注解的方法上 - 通知:
@AfterReturning
在原方法执行成功之后,才调用此通知(才执行插入日志操作)
/**
* @author dclup
* @Description 日志切面
**/
@Aspect
@Component
public class LogAspect {
@Autowired
private LogService logService;
/**
* 定义切点
*/
@Pointcut("@annotation(com.dclup.annotation.Log)")
// @Pointcut("execution(* com.dclup.service.impl.*.*(..))")
public void pointcutLog() {
}
/**
* 在原方法成功执行之后,再调用此通知。
* @param joinPoint
*/
@AfterReturning("pointcutLog()")
public void afterReturning(JoinPoint joinPoint){
insertLog(joinPoint);
}
/**
* 进行插入日志
*
* @param joinPoint
*/
private void insertLog(JoinPoint joinPoint) {
// 获取切点方法上的注解
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
Log annotation = method.getAnnotation(Log.class);
// 获取表名和操作类型
LogDO logDO = new LogDO();
logDO.setTableName(annotation.table().getName());
logDO.setType(annotation.type().getName());
// 获取当前用户id
/*
ServletRequestAttributes requestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
Object userIdObject = requestAttributes.getRequest().getAttribute("currentUserId");
Long currentUserId = Long.valueOf(String.valueOf(userIdObject));
logDO.setUserId(currentUserId);
*/
// 模拟获取当前用户
logDO.setUserId(10086L);
// 获取当前时间
LocalDateTime nowTime = LocalDateTime.now();
String formatNowTime = nowTime.format(DateTimeFormatter.ofPattern("yyyy:MM:dd HH:mm:ss"));
logDO.setTime(formatNowTime);
//获取方法参数值(按方法定义参数的顺序:google查询)
Object[] args = joinPoint.getArgs();
//获取Controller中传入参数(按方法定义参数的顺序:手动测试)
LocalVariableTableParameterNameDiscoverer nameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
String[] parameterNames = nameDiscoverer.getParameterNames(method);
//获取idName的值
if (parameterNames!=null && args !=null){
for (int i = 0; i <parameterNames.length ; i++) {
if (IdNameEnum.USER_ID.getName().equals(parameterNames[i])){
logDO.setTableId(Long.valueOf(String.valueOf(args[i])));
}
}
}
//保存日志
logService.insertLog(logDO);
}
}
测试
User中的数据
postMan测试
http://localhost:9999/user/1
{
"userName": "dclup"
}