# 深度排查 MyBatis-Plus 自动填充不生效问题 ## 问题概述 尽管对 MyBatis-Plus 的自动填充处理器进行了多次优化和配置,但 `create_by` 和 `create_time` 字段仍然没有被自动填充。 ## 深度排查步骤 ### 1. 检查 AOP 代理是否生效 MyBatis-Plus 的自动填充功能依赖于 AOP 代理。如果实体类的方法被直接调用而非通过代理调用,自动填充可能不会生效。 ### 2. 验证 Service 层实现 确保使用的是 MyBatis-Plus 提供的通用 Service 方法,而不是自定义的 SQL。 ### 3. 检查 @TableField 注解配置 确认实体类中的字段注解配置正确。 ### 4. 检查事务配置 某些事务配置可能会影响 AOP 代理的生效。 ## 解决方案 ### 方案一:在 Service 层手动设置审计字段 创建一个工具类来统一处理审计字段的设置: ```java @Component public class AuditFieldUtil { public static void setCreateInfo(Object entity) { if (entity == null) return; try { LoginUser loginUser = SecurityUtils.getLoginUser(); String username = loginUser != null ? loginUser.getUsername() : "system"; Date currentTime = new Date(); // 使用反射设置字段值 Field createByField = getField(entity.getClass(), "createBy"); if (createByField != null) { createByField.setAccessible(true); if (createByField.get(entity) == null || "".equals(createByField.get(entity))) { createByField.set(entity, username); } } Field createTimeField = getField(entity.getClass(), "createTime"); if (createTimeField != null) { createTimeField.setAccessible(true); if (createTimeField.get(entity) == null) { createTimeField.set(entity, currentTime); } } // 处理下划线命名的字段 Field createByFieldUnderscore = getField(entity.getClass(), "create_by"); if (createByFieldUnderscore != null) { createByFieldUnderscore.setAccessible(true); if (createByFieldUnderscore.get(entity) == null || "".equals(createByFieldUnderscore.get(entity))) { createByFieldUnderscore.set(entity, username); } } Field createTimeFieldUnderscore = getField(entity.getClass(), "create_time"); if (createTimeFieldUnderscore != null) { createTimeFieldUnderscore.setAccessible(true); if (createTimeFieldUnderscore.get(entity) == null) { createTimeFieldUnderscore.set(entity, currentTime); } } } catch (Exception e) { System.err.println("设置审计字段时发生异常: " + e.getMessage()); } } private static Field getField(Class clazz, String fieldName) { try { return clazz.getDeclaredField(fieldName); } catch (NoSuchFieldException e) { if (clazz.getSuperclass() != null) { return getField(clazz.getSuperclass(), fieldName); } return null; } } } ``` 然后在 Service 实现中使用: ```java @Service public class PractitionerServiceImpl extends ServiceImpl implements IPractitionerService { @Autowired private AuditFieldUtil auditFieldUtil; @Override @Transactional public boolean save(Practitioner entity) { // 在保存前手动设置审计字段 auditFieldUtil.setCreateInfo(entity); return super.save(entity); } @Override @Transactional public boolean saveBatch(Collection entityList) { entityList.forEach(auditFieldUtil::setCreateInfo); return super.saveBatch(entityList); } } ``` ### 方案二:重写 BaseMapper 方法 如果 Service 层的方法不起作用,可以直接在 Mapper 层处理: ```java @Mapper public interface PractitionerMapper extends BaseMapper { @Insert({ "" }) @Options(useGeneratedKeys = true, keyProperty = "id") int insertWithAuditFields(Practitioner record); } ``` ### 方案三:使用 MyBatis 拦截器 创建一个 MyBatis 拦截器来自动填充字段: ```java @Intercepts({ @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}) }) @Component public class AuditFieldInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { Object[] args = invocation.getArgs(); MappedStatement ms = (MappedStatement) args[0]; Object parameter = args[1]; String sqlCommandType = ms.getSqlCommandType().toString(); if ("INSERT".equals(sqlCommandType)) { setCreateAuditFields(parameter); } else if ("UPDATE".equals(sqlCommandType)) { setUpdateAuditFields(parameter); } return invocation.proceed(); } private void setCreateAuditFields(Object parameter) { if (parameter == null) return; try { LoginUser loginUser = SecurityUtils.getLoginUser(); String username = loginUser != null ? loginUser.getUsername() : "system"; Date currentTime = new Date(); // 设置 createBy 和 createTime setFieldValue(parameter, "createBy", username); setFieldValue(parameter, "create_time", username); setFieldValue(parameter, "createTime", currentTime); setFieldValue(parameter, "create_time", currentTime); } catch (Exception e) { e.printStackTrace(); } } private void setUpdateAuditFields(Object parameter) { if (parameter == null) return; try { LoginUser loginUser = SecurityUtils.getLoginUser(); String username = loginUser != null ? loginUser.getUsername() : "system"; Date currentTime = new Date(); // 设置 updateBy 和 updateTime setFieldValue(parameter, "updateBy", username); setFieldValue(parameter, "update_by", username); setFieldValue(parameter, "updateTime", currentTime); setFieldValue(parameter, "update_time", currentTime); } catch (Exception e) { e.printStackTrace(); } } private void setFieldValue(Object obj, String fieldName, Object value) { try { Field field = getField(obj.getClass(), fieldName); if (field != null) { field.setAccessible(true); if (field.get(obj) == null) { // 只在原值为 null 时设置 field.set(obj, value); } } } catch (Exception e) { // 忽略无法设置的字段 } } private Field getField(Class clazz, String fieldName) { try { return clazz.getDeclaredField(fieldName); } catch (NoSuchFieldException e) { if (clazz.getSuperclass() != null) { return getField(clazz.getSuperclass(), fieldName); } return null; } } @Override public Object plugin(Object target) { if (target instanceof Executor) { return Plugin.wrap(target, this); } else { return target; } } @Override public void setProperties(Properties properties) {} } ``` ## 推荐实施顺序 1. 首先尝试方案一(Service 层手动设置),这是最简单且可控的方式 2. 如果方案一不行,尝试方案三(MyBatis 拦截器),它在更底层起作用 3. 方案二是最后的选择,需要重写具体的插入逻辑 ## 验证方法 创建一个测试来验证自动填充是否生效: ```java @SpringBootTest public class AuditFieldTest { @Autowired private IPractitionerService practitionerService; @Test public void testAuditFieldFill() { Practitioner practitioner = new Practitioner(); practitioner.setName("Test Practitioner"); // 记录保存前的值 System.out.println("保存前 - createBy: " + practitioner.getCreateBy()); System.out.println("保存前 - createTime: " + practitioner.getCreateTime()); boolean success = practitionerService.save(practitioner); // 从数据库重新查询以验证 Practitioner saved = practitionerService.getById(practitioner.getId()); System.out.println("保存后 - createBy: " + saved.getCreateBy()); System.out.println("保存后 - createTime: " + saved.getCreateTime()); Assertions.assertTrue(success); Assertions.assertNotNull(saved.getCreateBy()); Assertions.assertNotNull(saved.getCreateTime()); } } ``` 通过这些方案,应该能够解决自动填充不生效的问题。