java异常:java.lang.NoSuchFieldException: DEFAULT_PARAM

本文解决了一个在Struts2.3.15.3版本中使用struts2-json-plugin-2.3.15.3.jar时启动Tomcat出现的问题。原因是该版本的struts2-json-plugin中的JSONResult类缺少DEFAULT_PARAM属性。解决方案包括调整log4j配置或将Struts2升级到2.3.16版本。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

启动tomcat报[img]
[img]http://dl2.iteye.com/upload/attachment/0098/0493/ee9417c6-40f9-3785-80de-45ef9478aa3d.png[/img]
[/img]
项目用的是struts 2.3.15.3 这个版本,项目中有用到 struts2-json-plugin-2.3.15.3.jar 这个jar包。解决办法把log4j的配置文件有DEBUG级别改为INFO或更高级别。或者使用新的struts2版本是2.3.16。
后来百度了一下,原来是 2.3.15这个版本的一个bug问题,在org.apache.struts2.json.JSONResult 这个类中 是没有这个 DEFAULT_PARAM 属性的。
package com.jxyunge.sign.aspect; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.NullNode; import com.jxyunge.sign.enums.sys.OperationTypeEnum; import com.jxyunge.sign.mybatis.entity.sys.Admin; import com.jxyunge.sign.mybatis.entity.sys.SysLog; import com.jxyunge.sign.mybatis.service.sys.AdminService; import com.jxyunge.sign.mybatis.service.sys.EntityService; import com.jxyunge.sign.mybatis.service.sys.SysLogService; import com.jxyunge.sign.request.input.common.PageParam; import com.jxyunge.sign.tools.IpHelper; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; /** * @author lgh */ @Slf4j @Aspect @AllArgsConstructor @Component public class SysLogAspect { private final AdminService adminService; private final SysLogService sysLogService; private final EntityService entityService; private ObjectMapper objectMapper; @Around("@annotation(sysLogAnnotation)") public Object logAround(ProceedingJoinPoint joinPoint, com.jxyunge.sign.annotation.SysLog sysLogAnnotation) throws Throwable { log.info("开始执行日志记录"); long beginTime = System.currentTimeMillis(); OperationTypeEnum operationType = sysLogAnnotation.type(); // 获取旧数据 Object oldEntity = getOldEntity(joinPoint, sysLogAnnotation); // 执行目标方法 Object result = joinPoint.proceed(); long time = System.currentTimeMillis() - beginTime; // 获取新数据 Object newEntity = extractEntityFromResponse(result); // 构建日志记录 SysLog sysLog = buildSysLog(joinPoint, sysLogAnnotation, time, oldEntity, newEntity, operationType); // 异步保存日志 CompletableFuture.runAsync(() -> sysLogService.save(sysLog)); return result; } private Object getOldEntity(ProceedingJoinPoint joinPoint, com.jxyunge.sign.annotation.SysLog annotation) { if (annotation.type() == OperationTypeEnum.CREATE) return null; try { Object idValue = getArgValue(joinPoint, annotation.idParam()); if (idValue == null) return null; // 必须指定 entityClass,否则无法获取完整实体 if (annotation.entityClass() == null || annotation.entityClass() == Object.class) { log.warn("SysLog 注解中未指定 entityClass,无法获取旧实体数据"); return null; } return entityService.getById(annotation.entityClass(), idValue.toString()); } catch (Exception e) { log.error("获取旧实体失败", e); return null; } } private Object getArgValue(ProceedingJoinPoint joinPoint, String paramName) { if (StringUtils.isBlank(paramName)) return null; MethodSignature signature = (MethodSignature) joinPoint.getSignature(); String[] paramNames = signature.getParameterNames(); Object[] args = joinPoint.getArgs(); // 支持嵌套属性,如"entity.id" if (paramName.contains(".")) { String[] parts = paramName.split("\\."); String rootParam = parts[0]; Object rootObj = null; for (int i = 0; i < paramNames.length; i++) { if (rootParam.equals(paramNames[i])) { rootObj = args[i]; break; } } if (rootObj == null) return null; // 递归获取嵌套属性 try { for (int i = 1; i < parts.length; i++) { String fieldName = parts[i]; Field field = findField(rootObj.getClass(), fieldName); if (field == null) { log.warn("字段 {} 在类 {} 及其父类中未找到", fieldName, rootObj.getClass().getName()); rootObj = null; break; } field.setAccessible(true); rootObj = field.get(rootObj); if (rootObj == null) break; } return rootObj; } catch (Exception e) { log.error("获取嵌套参数失败: {}", paramName, e); return null; } } else { // 原有逻辑,直接匹配参数名 for (int i = 0; i < paramNames.length; i++) { if (paramName.equals(paramNames[i])) { return args[i]; } } return null; } } private Field findField(Class<?> clazz, String fieldName) { while (clazz != null) { try { return clazz.getDeclaredField(fieldName); } catch (NoSuchFieldException e) { clazz = clazz.getSuperclass(); } } return null; } private Object extractEntityFromResponse(Object result) { if (result == null) return null; // 1. 尝试从通用响应结构获取 try { Method getData = result.getClass().getMethod("getData"); Object data = getData.invoke(result); if (data != null) return data; } catch (NoSuchMethodException ignored) { } catch (Exception e) { log.warn("getData调用异常", e); } // 2. 尝试直接访问data字段 try { Field dataField = result.getClass().getDeclaredField("data"); dataField.setAccessible(true); Object data = dataField.get(result); if (data != null) return data; } catch (NoSuchFieldException ignored) { } catch (Exception e) { log.warn("data字段访问异常", e); } // 3. 返回原始结果 return result; } private SysLog buildSysLog(ProceedingJoinPoint joinPoint, com.jxyunge.sign.annotation.SysLog annotation, long time, Object oldEntity, Object newEntity, OperationTypeEnum operationType) { SysLog sysLog = new SysLog(); // 1. 设置基础字段(原有逻辑) // 注解描述 sysLog.setOperation(annotation.value()); // 请求方法名 String className = joinPoint.getTarget().getClass().getName(); String methodName = joinPoint.getSignature().getName(); sysLog.setMethod(className + "." + methodName + "()"); // 请求参数 Object[] args = joinPoint.getArgs(); if(args != null && args.length > 0) { String params = null; try { if(args.length > 0 && args[0] != null && Objects.equals(args[0].getClass().getName(), PageParam.class.getName())){ PageParam<Object> pageParam = (PageParam<Object>) args[0]; List<Object> records = pageParam.getRecords(); List<Object> filteredRecords = filterSerializableObjects(records); params = objectMapper.writeValueAsString(filteredRecords); } else { Object[] filteredArgs = filterSerializableObjects(args); params = objectMapper.writeValueAsString(filteredArgs); } } catch (Exception e) { params = getSafeParamRepresentation(args); log.warn("参数序列化失败: {}", e.getMessage()); } sysLog.setParams(params); } // IP地址 sysLog.setIp(IpHelper.getIpAddr()); // 业务ID(从新实体中提取) String businessId = extractIdFromResult(newEntity != null ? newEntity : oldEntity); if (businessId != null) { sysLog.setBizId(businessId); } // 用户名 Admin admin = adminService.getAdminFromReq(); if (!Objects.isNull(admin)) { sysLog.setUsername(admin.getUsername()); } // 执行时间和创建时间 sysLog.setTime(time); sysLog.setCreateDate(new Date()); // 2. 新增操作日志字段 sysLog.setOperationType(OperationTypeEnum.valueOf(operationType.name())); sysLog.setBeforeData(toJsonSafe(oldEntity)); sysLog.setAfterData(toJsonSafe(newEntity)); sysLog.setChangeDetails(compareChanges(oldEntity, newEntity)); return sysLog; } private String toJsonSafe(Object obj) { if (obj == null) return null; try { return objectMapper.writeValueAsString(obj); } catch (JsonProcessingException e) { return "序列化失败: " + e.getMessage(); } } private String compareChanges(Object oldObj, Object newObj) { if (oldObj == null && newObj == null) return "[]"; try { JsonNode oldTree = oldObj != null ? objectMapper.valueToTree(oldObj) : objectMapper.createObjectNode(); JsonNode newTree = newObj != null ? objectMapper.valueToTree(newObj) : objectMapper.createObjectNode(); List<Map<String, Object>> changes = new ArrayList<>(); Set<String> allFields = new HashSet<>(); oldTree.fieldNames().forEachRemaining(allFields::add); newTree.fieldNames().forEachRemaining(allFields::add); for (String field : allFields) { JsonNode oldVal = oldTree.get(field); JsonNode newVal = newTree.get(field); if (oldVal == null) oldVal = NullNode.getInstance(); if (newVal == null) newVal = NullNode.getInstance(); if (!oldVal.equals(newVal)) { // 使用 HashMap 替代 Map.of,避免 null 值导致的 NullPointerException Map<String, Object> change = new HashMap<>(); change.put("field", field != null ? field : "null"); // 处理可能的 null 值 String oldText = oldVal.isNull() ? null : (oldVal.isTextual() ? oldVal.asText() : oldVal.toString()); String newText = newVal.isNull() ? null : (newVal.isTextual() ? newVal.asText() : newVal.toString()); change.put("oldValue", oldText); change.put("newValue", newText); changes.add(change); } } return objectMapper.writeValueAsString(changes); } catch (Exception e) { log.error("比较变更详情时发生异常: {}", e.getMessage(), e); // 更详细的异常记录 return String.format("[{\"error\":\"%s\"}]", e.getMessage()); } } private String extractIdFromResult(Object result) { if (result == null) return null; // 尝试从常见ID字段获取 String[] idFieldNames = {"id", "Id", "ID", "uuid", "key"}; for (String fieldName : idFieldNames) { try { Field field = result.getClass().getDeclaredField(fieldName); field.setAccessible(true); Object value = field.get(result); if (value != null) return value.toString(); } catch (NoSuchFieldException ignored) { } catch (Exception e) { log.warn("ID字段[{}]访问异常", fieldName, e); } } // 尝试通过Getter方法获取 try { Method getId = result.getClass().getMethod("getId"); Object idValue = getId.invoke(result); if (idValue != null) return idValue.toString(); } catch (NoSuchMethodException ignored) { } catch (Exception e) { log.warn("getId方法调用异常", e); } return null; } // 过滤可序列化对象的方法 private List<Object> filterSerializableObjects(List<Object> objects) { if (objects == null) return new ArrayList<>(); return objects.stream() .filter(this::isSerializable) .collect(Collectors.toList()); } private Object[] filterSerializableObjects(Object[] objects) { if (objects == null) return new Object[0]; return Arrays.stream(objects) .filter(this::isSerializable) .toArray(); } // 检查对象是否可序列化 private boolean isSerializable(Object obj) { if (obj == null) return true; try { // 尝试快速判断是否为常见不可序列化类型 Class<?> clazz = obj.getClass(); String className = clazz.getName(); // 过滤常见的不可序列化类 if (className.contains("java.util.Collections$") || className.contains("HttpServletRequest") || className.contains("HttpServletResponse") || className.contains("Session") || className.contains("ServletContext") || className.startsWith("org.apache.catalina") || className.startsWith("org.springframework.security")) { return false; } // 简单测试序列化 objectMapper.writeValueAsString(obj); return true; } catch (Exception e) { return false; } } // 获取安全的参数表示 private String getSafeParamRepresentation(Object[] args) { if (args == null || args.length == 0) { return "[]"; } List<String> paramInfos = new ArrayList<>(); for (int i = 0; i < args.length; i++) { if (args[i] != null) { String className = args[i].getClass().getSimpleName(); paramInfos.add("arg" + i + ": " + className); } else { paramInfos.add("arg" + i + ": null"); } } try { return objectMapper.writeValueAsString(paramInfos); } catch (Exception e) { return paramInfos.toString(); } } } package com.jxyunge.sign.annotation; import com.jxyunge.sign.enums.sys.OperationTypeEnum; import java.lang.annotation.*; /** * 自定义日志注解 * @author linyan */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface SysLog { String value() default ""; OperationTypeEnum type() default OperationTypeEnum.UPDATE; // 操作类型 // 新增ID参数名(用于获取旧数据) String idParam() default ""; // 新增实体类参数 Class<?> entityClass() default Object.class; } package com.jxyunge.sign.mybatis.entity.sys; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.jxyunge.sign.annotation.SearchParam; import com.jxyunge.sign.enums.sys.OperationTypeEnum; import com.jxyunge.sign.enums.sys.SearchConditionEnum; import com.jxyunge.sign.mybatis.entity.BaseEntity; import lombok.Data; import java.io.Serializable; import java.util.Date; import java.util.List; /** * 系统日志 * @author zanmall */ @Data @TableName("tb_sys_log") public class SysLog implements Serializable { private static final long serialVersionUID = 1L; /** * 主键 */ @SearchParam(name = {"主键"},condition = {SearchConditionEnum.eq}) @TableId(type = IdType.ASSIGN_UUID) private String id; /** * 用户名 */ private String username; /** * 用户操作 */ private String operation; /** * 请求方法 */ private String method; /** * 请求参数 */ private String params; /** * 执行时长(毫秒) */ private Long time; /** * IP地址 */ private String ip; /** * 创建时间 */ private Date createDate; /** * 业务id */ private String bizId; // 新增字段 private OperationTypeEnum operationType; // 操作类型 private String beforeData; // 变更前数据(JSON) private String afterData; // 变更后数据(JSON) private String changeDetails; // 变更详情(JSON) } // 文件路径: D:\develop-tools\code\ideaworkplace\zhongbang-scm\zhongbang-scm-service\src\main\java\com\jxyunge\sign\mybatis\service\impl\sys\EntityServiceImpl.java package com.jxyunge.sign.mybatis.service.impl.sys; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.jxyunge.sign.mybatis.entity.scm.sys.Order; import com.jxyunge.sign.mybatis.service.sys.EntityService; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Service; import java.io.Serializable; import java.util.HashMap; import java.util.Map; /** * @author xiaoda * @create 2025-07-30 10:38 */ @Service public class EntityServiceImpl implements EntityService, ApplicationContextAware { private ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } @Override public <T> T getById(Class<T> entityClass, Serializable id) { try { // 尝试多种方式获取 Mapper BaseMapper<T> mapper = findMapperForEntity(entityClass); if (mapper != null) { return mapper.selectById(id); } throw new RuntimeException("未找到处理实体 " + entityClass.getSimpleName() + " 的 Mapper"); } catch (Exception e) { throw new RuntimeException("获取实体失败: " + entityClass.getSimpleName() + ", ID: " + id, e); } } @Override public Object getById(String id) { // 对于没有前缀的普通ID,我们无法自动识别实体类型 // 这种情况应该始终通过带 entityClass 参数的 getById 方法来调用 throw new UnsupportedOperationException("无法通过普通ID自动识别实体类型,请使用 getById(Class<T> entityClass, Serializable id) 方法"); } /** * 查找处理指定实体的 Mapper */ private <T> BaseMapper<T> findMapperForEntity(Class<T> entityClass) { // 尝试多种可能的命名方式,按照项目实际命名规则排序 String[] possibleNames = getPossibleMapperNames(entityClass); for (String mapperBeanName : possibleNames) { if (applicationContext.containsBean(mapperBeanName)) { try { return applicationContext.getBean(mapperBeanName, BaseMapper.class); } catch (Exception ignored) { // 如果获取失败,继续尝试下一个名称 } } } return null; } /** * 获取可能的 Mapper Bean 名称(按照项目实际命名规则排序) */ private String[] getPossibleMapperNames(Class<?> entityClass) { String simpleName = entityClass.getSimpleName(); return new String[] { "sys" + simpleName + "Mapper", // sys前缀: sysOrderMapper, sysGoodsMapper Character.toLowerCase(simpleName.charAt(0)) + simpleName.substring(1) + "Mapper", // 首字母小写: orderMapper, goodsMapper simpleName.toLowerCase() + "Mapper", // 全小写: ordermapper, goodsmapper "tb" + simpleName + "Mapper", // tb前缀: tbOrderMapper, tbGoodsMapper simpleName + "Mapper" // 原样: OrderMapper, GoodsMapper }; } // 简单实体类(如果需要的话,但建议移除) private static class SimpleEntity { private String id; public SimpleEntity(String id) { this.id = id; } public String getId() { return id; } public void setId(String id) { this.id = id; } } } @SysLog(value = "修改订单", type = OperationTypeEnum.UPDATE,idParam = "entity.id",entityClass = Order.class) @PostMapping(value = "/updateOrder") @Operation(summary = "修改订单") public ApiOutput<OrderAndDetailsOutput> updateOrder(HttpServletRequest request, @RequestBody OrderAddInput entity) { Admin admin = getAdminFromReq(request); if (BeanUtil.isEmpty(entity)){ return ApiOutput.err(ErrorCodeEnum.PARAMETER_IS_EMPTY); } OrderAndDetailsOutput save = sysOrderService.updateOrder(entity,admin); return ApiOutput.ok(save); } 这些是我的操作日志实现相关的代码,可以查看新增,删除,编辑记录,点击详情可查看明细(查看变更字段的变化),请你帮我分析一下,这些代码是否能实现这些功能,下面的数据是我在测试修改订单时数据库中的数据,看看有什么问题 修改日志的before_data里面的数据是:{"id":"ORD4103888","createTime":"2025-07-30 13:37:04","modifyTime":"2025-07-30 17:03:38","merchantId":"83210837","operationTimeId":"83953091","deliveryStartTime":"2025-07-24T06:00:27.000+00:00","deliveryEndTime":"2025-07-24T06:00:27.000+00:00","status":0,"payStatus":0,"outStatus":0,"payType":0,"type":0,"remark":"","deliveryMethod":0,"total":180.00,"receiver":"达","receiverPhone":"78945632112","receiverAddress":"","lastOperation":"","signMethod":"0","sortingTaskId":"","source":0,"printCount":0,"printTime":null,"receiveTime":null,"creatorId":"1922052782395863042","loadingStatus":0,"pickupPointId":null,"refundStatus":0,"dateType":0,"dateStart":null,"dateEnd":null,"searchType":0,"search":null,"geotags":null,"lineId":null,"quotationId":null,"driverId":null,"printed":null,"hasRemark":null,"isCurrentPrice":null,"creator":null,"operatorId":"1922052782395863042","operator":null,"merchantName":null,"itemCount":null,"storeCode":null,"accountId":null,"account":null,"merchantLabel":null,"settlementMethod":null,"driverName":null,"packageStatus":0} 而after_data里面的数据是:,{"id":"ORD4103888","createTime":"2025-07-30 13:37:04","modifyTime":"2025-07-30 17:10:32","merchantId":"83210837","operationTimeId":"83953091","deliveryStartTime":"2025-07-24T06:00:01.861+00:00","deliveryEndTime":"2025-07-24T06:00:01.861+00:00","status":0,"payStatus":0,"outStatus":0,"payType":0,"type":0,"remark":"","deliveryMethod":0,"total":180,"receiver":"达","receiverPhone":"78945632112","receiverAddress":"","lastOperation":"","signMethod":"0","sortingTaskId":"","source":0,"printCount":0,"printTime":null,"receiveTime":null,"creatorId":"1922052782395863042","loadingStatus":0,"pickupPointId":null,"refundStatus":0,"dateType":0,"dateStart":null,"dateEnd":null,"searchType":0,"search":null,"geotags":null,"lineId":null,"quotationId":null,"driverId":null,"printed":null,"hasRemark":null,"isCurrentPrice":null,"creator":null,"operatorId":"1922052782395863042","operator":null,"merchantName":null,"itemCount":null,"storeCode":null,"accountId":null,"account":null,"merchantLabel":null,"settlementMethod":null,"driverName":null,"orderDetailsList":[{"id":"84158442","createTime":"2025-07-30 13:44:12","modifyTime":"2025-07-3017:10:32","orderId":"ORD4103888","goodsId":"83222762","goodsSpecificationId":"83271360","quotationId":"83257135","number":9.00,"basicNumber":18,"salesUnit":"斤","categoryId":"1940302497016995842","basicUnit":"斤","price":20.00,"basicPrice":null,"remark":"","amount":180.00,"goodsName":"排骨","specificationName":null,"merchantName":null,"quotationName":"测试报价单01","image":null,"goodsSpecificationName":"排骨","salesUnitQuantity":1.00,"classificationName":"肉类/猪肉/排骨"}],"packageStatus":0} change_details里面的数据是:[{"newValue":"2025-07-30 17:10:32","field":"modifyTime","oldValue":"2025-07-30 17:03:38"},{"newValue":"2025-07-24T06:00:01.861+00:00","field":"deliveryEndTime","oldValue":"2025-07-24T06:00:27.000+00:00"},{"newValue":"2025-07-24T06:00:01.861+00:00","field":"deliveryStartTime","oldValue":"2025-07-24T06:00:27.000+00:00"},{"newValue":"[{\"id\":\"84158442\",\"createTime\":\"2025-07-30 13:44:12\",\"modifyTime\":\"2025-07-30 17:10:32\",\"orderId\":\"ORD4103888\",\"goodsId\":\"83222762\",\"goodsSpecificationId\":\"83271360\",\"quotationId\":\"83257135\",\"number\":9,\"basicNumber\":18,\"salesUnit\":\"斤\",\"categoryId\":\"1940302497016995842\",\"basicUnit\":\"斤\",\"price\":2E+1,\"basicPrice\":null,\"remark\":\"\",\"amount\":1.8E+2,\"goodsName\":\"排骨\",\"specificationName\":null,\"merchantName\":null,\"quotationName\":\"测试报价单01\",\"image\":null,\"goodsSpecificationName\":\"排骨\",\"salesUnitQuantity\":1,\"classificationName\":\"肉类/猪肉/排骨\"}]","field":"orderDetailsList","oldValue":null}]
最新发布
07-31
package com.kucun.Service; import java.io.Serializable; import java.lang.reflect.Field; import java.util.*; import java.util.stream.Collectors; import javax.persistence.EntityNotFoundException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; import org.springframework.data.domain.Example; import org.springframework.data.domain.ExampleMatcher; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Service; import com.kucun.data.entity.*; import com.kucun.dataDo.*; @Service public class AppService { // 自动注入所有依赖 @Autowired private KucunRepository kucunRepository; @Autowired private DynamicRepositoryService repositoryService; @Autowired private EntityDependencyService dependencyService; /** * 获取所有实体类型的数据 * @return 包含所有实体数据的Map */ public Information getAllData() { Map<String, Object> response = new HashMap<>(); repositoryService.getRepositoryNameMap().forEach((key, repo) -> response.put(key + "s", repo.findAll()) ); return Information.NewSuccess(response); } /** * 获取指定实体类型的所有数据 * @param entityName 实体名称 * @return 实体数据列表 */ public Information getEntityData(String entityName) { JpaRepository<?, ?> repo = repositoryService.getJpaRepository(entityName); return Information.NewSuccess(repo.findAll()); } /** * 添加新实体 * @param entity 要添加的实体对象 * @return 添加结果 */ public Information addEntity(Object entity) { try { // 处理关联字段 handleAssociations(entity); // 获取对应的Repository并保存 JpaRepository<Object, Serializable> repo = (JpaRepository<Object, Serializable>) repositoryService.getJpaRepository(entity.getClass()); Object savedEntity = repo.save(entity); // 如果是板材,初始化库存 if (savedEntity instanceof Bancai) { initializeKucunForBancai((Bancai) savedEntity); } return Information.NewSuccess(savedEntity); } catch (Exception ex) { return Information.Newfail(500, "创建失败: " + ex.getMessage(), null); } } /** * 更新实体 * @param entity 包含ID的实体对象 * @return 更新结果 */ public Information updateEntity(Object entity) { if (entity == null) { return Information.Newfail(403, "参数为空", null); } try { // 获取ID字段 Field idField = entity.getClass().getDeclaredField("id"); idField.setAccessible(true); Object idValue = idField.get(entity); if (idValue == null) { return Information.Newfail(403, "ID为空", null); } // 获取Repository和现有实体 JpaRepository<Object, Serializable> repo = (JpaRepository<Object, Serializable>) repositoryService.getJpaRepository(entity.getClass()); Object existingEntity = repo.findById((Serializable) idValue) .orElseThrow(() -> new RuntimeException("实体不存在")); // 复制非空属性并保存 copyNonNullProperties(entity, existingEntity); return Information.NewSuccess(repo.save(existingEntity)); } catch (Exception ex) { return Information.Newfail(500, "更新失败: " + ex.getMessage(), null); } } /** * 删除实体 * @param entity 要删除的实体 * @return 删除结果 */ public Information deleteEntity(EntityBasis entity) { if (entity == null) { return Information.NewFail("删除对象不能为空"); } try { String entityName = entity.getClass().getSimpleName().toLowerCase(); JpaRepository<Object, Serializable> repo = (JpaRepository<Object, Serializable>) repositoryService.getJpaRepository(entityName); // 先加载完整实体(关键步骤) Object fullEntity = repo.findById(entity.getId()) .orElseThrow(() -> new EntityNotFoundException("实体不存在")); if(entity instanceof Bancai) { if(((Bancai)fullEntity).getKucun().getShuliang()>0) { return Information.NewFail("库存不为零,无法删除"); } } // 检查依赖关系 if (dependencyService.hasDependencies(entity.getClass(), entity.getId())) { return Information.NewFail("该记录已被引用,无法删除"); } // 使用实体对象删除(触发级联操作) repo.delete(fullEntity); return Information.NewSuccess("删除成功"); } catch (Exception e) { return Information.NewFail("删除错误: " + e.getMessage()); } } /** * 动态查询实体 * @param entity 包含查询条件的实体对象 * @return 查询结果 */ public <T> Information queryEntity(T entity) { if (entity == null) { return Information.NewFail("查询参数不能为空"); } try { JpaRepository<T, ?> repo = (JpaRepository<T, ?>) repositoryService.getJpaRepository(entity.getClass()); Example<T> example = Example.of(entity, ExampleMatcher.matching() .withIgnoreNullValues() .withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING)); return Information.NewSuccess(repo.findAll(example)); } catch (Exception e) { return Information.NewFail("查询失败: " + e.getMessage()); } } // ====================== 私有辅助方法 ====================== /** * 为板材初始化库存 * @param bancai 板材对象 */ private void initializeKucunForBancai(Bancai bancai) throws Exception { Kucun kucun = new Kucun(null, bancai, 0); bancai.setKucun(kucunRepository.save(kucun)); } /** * 处理实体关联关系 * @param entity 要处理的实体 */ private void handleAssociations(Object entity) throws Exception { for (Field field : entity.getClass().getDeclaredFields()) { field.setAccessible(true); Object value = field.get(entity); if (value == null) continue; // 处理 JPA 实体关联 if (value instanceof EntityBasis) { EntityBasis associated = (EntityBasis) value; JpaRepository<Object, Serializable> repo = (JpaRepository<Object, Serializable>) repositoryService.getJpaRepository(associated.getClass().getSimpleName().toLowerCase()); // 只处理已存在实体(不创建新关联) if (associated.getId() != null) { Object managedEntity = repo.findById(associated.getId()) .orElseThrow(() -> new RuntimeException("关联实体不存在")); field.set(entity, managedEntity); } } // 处理集合关联 else if (value instanceof Collection) { List<Object> managedEntities = new ArrayList<>(); for (Object item : (Collection<?>) value) { if (item instanceof EntityBasis) { EntityBasis eb = (EntityBasis) item; JpaRepository<Object, Serializable> repo = (JpaRepository<Object, Serializable>) repositoryService.getJpaRepository(eb.getClass().getSimpleName().toLowerCase()); if (eb.getId() != null) { managedEntities.add(repo.findById(eb.getId()) .orElseThrow(() -> new RuntimeException("关联实体不存在"))); } } } if (!managedEntities.isEmpty()) { field.set(entity, managedEntities); } } } } /** * 复制非空属性 * @param source 源对象 * @param target 目标对象 */ private void copyNonNullProperties(Object source, Object target) throws IllegalAccessException { for (Field field : source.getClass().getDeclaredFields()) { field.setAccessible(true); Object value = field.get(source); // 跳过关联字段和ID字段 if (value != null && !(value instanceof EntityBasis) && !(value instanceof Collection) && !field.getName().equals("id")) { try { Field targetField = target.getClass().getDeclaredField(field.getName()); targetField.setAccessible(true); targetField.set(target, value); } catch (NoSuchFieldException ignored) {} } } } }package com.kucun.controller; import com.fasterxml.jackson.databind.ObjectMapper; import com.kucun.Service.AppService; import com.kucun.data.entity.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.Collections; import java.util.HashMap; import java.util.Map; @RestController @RequestMapping("/app") public class AppController { // Java 8 兼容的实体类型映射 private static final Map<String, Class<?>> ENTITY_MAP; static { Map<String, Class<?>> map = new HashMap<>(); map.put("user", User.class); map.put("bancai", Bancai.class); map.put("caizi", Caizhi.class); map.put("mupi", Mupi.class); map.put("dingdan", Dingdan.class); map.put("zujian", Zujian.class); map.put("chanpin", Chanpin.class); map.put("dingdan_chanpin", Dingdan_chanpin.class); map.put("dingdan_chanpin_zujian", Dingdan_chanpin_zujian.class); map.put("chanpin_zujian", Chanpin_zujian.class); map.put("jinhuo", Jinhuo.class); map.put("kucun", Kucun.class); ENTITY_MAP = Collections.unmodifiableMap(map); } @Autowired private AppService appService; @Autowired private ObjectMapper objectMapper; // ====================== 数据查询 ====================== @GetMapping("/all") public Information getAllData() { return appService.getAllData(); } @GetMapping("/all/{entityType}") public Information getEntityData(@PathVariable String entityType) { return appService.getEntityData(entityType.toLowerCase()); } // ====================== CRUD操作 ====================== @PostMapping("/add/{entityType}") public Information addEntity( @PathVariable String entityType, @RequestBody Map<String, Object> requestBody ) { return handleEntityOperation(entityType, requestBody, "add"); } @PostMapping("/select/{entityType}") public Information queryEntity( @PathVariable String entityType, @RequestBody Map<String, Object> requestBody ) { return handleEntityOperation(entityType, requestBody, "select"); } @PostMapping("/delete/{entityType}") public Information deleteEntity( @PathVariable String entityType, @RequestBody Map<String, Object> requestBody ) { return handleEntityOperation(entityType, requestBody, "delete"); } @PostMapping("/update/{entityType}") public Information updateEntity( @PathVariable String entityType, @RequestBody Map<String, Object> requestBody ) { return handleEntityOperation(entityType, requestBody, "update"); } // ====================== 核心辅助方法 ====================== private Information handleEntityOperation( String entityType, Map<String, Object> requestBody, String operation ) { String normalizedType = entityType.toLowerCase(); Class<?> entityClass = ENTITY_MAP.get(normalizedType); if (entityClass == null) { return Information.NewFail("不支持的实体类型: " + entityType); } try { Object entity = objectMapper.convertValue(requestBody, entityClass); switch (operation) { case "add": return appService.addEntity(entity); case "select": return appService.queryEntity(entity); case "delete": // 确保实体实现了EntityBasis接口 if (entity instanceof EntityBasis) { return appService.deleteEntity((EntityBasis) entity); } else { return Information.NewFail("删除操作需要实体实现EntityBasis接口"); } case "update": return appService.updateEntity(entity); default: return Information.NewFail("无效的操作类型"); } } catch (Exception e) { return Information.NewFail(operation + "操作失败: " + e.getMessage()); } } }package com.kucun.Service; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.metamodel.*; import org.springframework.stereotype.Service; import java.io.Serializable; import java.util.*; import java.util.stream.Collectors; @Service public class EntityDependencyService { @PersistenceContext private EntityManager entityManager; private final Map<Class<?>, Set<Attribute<?, ?>>> associationCache = new HashMap<>(); /** * 检查实体是否被其他实体引用 * @param entityClass 实体类 * @param entityId 实体ID * @return 存在引用返回true */ public boolean hasDependencies(Class<?> entityClass, Serializable entityId) { Metamodel metamodel = entityManager.getMetamodel(); Set<EntityType<?>> entities = metamodel.getEntities(); for (EntityType<?> entityType : entities) { // 跳过自身(避免自引用检查) if (entityType.getJavaType().equals(entityClass)) continue; Set<Attribute<?, ?>> associations = getAssociationAttributes(entityType); for (Attribute<?, ?> attr : associations) { if (attr.getJavaType().isAssignableFrom(entityClass)) { if (isReferenced(entityType, attr, entityId)) { return true; } } } } return false; } /** * 获取实体所有关联属性(带缓存) */ private Set<Attribute<?, ?>> getAssociationAttributes(EntityType<?> entityType) { return associationCache.computeIfAbsent(entityType.getJavaType(), k -> entityType.getAttributes().stream() .filter(Attribute::isAssociation) .collect(Collectors.toSet()) ); } /** * 检查特定关联是否引用目标实体 */ private boolean isReferenced(EntityType<?> entityType, Attribute<?, ?> attribute, Serializable targetId) { String jpql = buildReferenceQuery(entityType, attribute); Long count = (Long) entityManager.createQuery(jpql) .setParameter("targetId", targetId) .getSingleResult(); return count > 0; } /** * 构建引用检查查询 */ private String buildReferenceQuery(EntityType<?> entityType, Attribute<?, ?> attribute) { String entityName = entityType.getName(); String attrName = attribute.getName(); if (attribute instanceof PluralAttribute) { return String.format("SELECT COUNT(e) FROM %s e WHERE :targetId MEMBER OF e.%s", entityName, attrName); } else { return String.format("SELECT COUNT(e) FROM %s e WHERE e.%s.id = :targetId", entityName, attrName); } } }package com.kucun.Service; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.HashMap; import java.util.Map; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Service; @Service public class DynamicRepositoryService { private final Map<Class<?>, JpaRepository<?, ?>> repositoryMap = new HashMap<>(); private final Map<String, JpaRepository<?, ?>> repositoryNameMap = new HashMap<>(); public Map<String, JpaRepository<?, ?>> getRepositoryNameMap() { return repositoryNameMap; } public Map<Class<?>, JpaRepository<?, ?>> getRepositoryMap() { return repositoryMap; } public JpaRepository<?,?> getJpaRepository(String e){ // System.out.println("String:"+e+mapToString(repositoryNameMap)); return repositoryNameMap.get(e); } public JpaRepository<?,?> getJpaRepository(Class<?> e){ // System.out.println("Class:"+e+mapToString(repositoryMap)); // System.out.println("Class:"+e); return repositoryMap.get(e); } // 自动注入所有JpaRepository实例 @Autowired public void initRepositories(Map<String, JpaRepository<?, ?>> repositories) { repositories.forEach((name, repo) -> { // 获取代理类实现的接口 Class<?>[] interfaces = repo.getClass().getInterfaces(); for (Class<?> iface : interfaces) { // 扫描接口上的泛型信息 Type[] genericInterfaces = iface.getGenericInterfaces(); for (Type type : genericInterfaces) { if (type instanceof ParameterizedType) { ParameterizedType pt = (ParameterizedType) type; // 检查原始类型是否是JpaRepository if (pt.getRawType().equals(JpaRepository.class)) { Type[] args = pt.getActualTypeArguments(); if (args.length >= 2 && args[0] instanceof Class) { Class<?> entityClass = (Class<?>) args[0]; repositoryMap.put(entityClass, repo); String key = entityClass.getSimpleName().toLowerCase(); repositoryNameMap.put(key, repo); // 调试日志 System.out.printf("Mapped %s -> %s [key: %s]%n", entityClass.getName(), repo.getClass().getName(), key); } } } } // 检查接口本身的泛型签名(如果继承的是参数化接口) Type genericSuper = iface.getGenericSuperclass(); if (genericSuper instanceof ParameterizedType) { ParameterizedType pt = (ParameterizedType) genericSuper; if (pt.getRawType().equals(JpaRepository.class)) { Type[] args = pt.getActualTypeArguments(); if (args.length >= 2 && args[0] instanceof Class) { Class<?> entityClass = (Class<?>) args[0]; repositoryMap.put(entityClass, repo); String key = entityClass.getSimpleName().toLowerCase(); repositoryNameMap.put(key, repo); // 调试日志 System.out.printf("Mapped %s -> %s [key: %s]%n", entityClass.getName(), repo.getClass().getName(), key); } } } } }); } // 通用保存方法 public <T> T saveEntity(T entity) { JpaRepository<T, ?> repo = (JpaRepository<T, ?>) repositoryMap.get(entity.getClass()); if (repo != null) { return repo.save(entity); } throw new IllegalArgumentException("Repository not found for " + entity.getClass()); } public static String mapToString(Map<?, ?> map) { if (map == null || map.isEmpty()) { return ""; } StringBuilder sb = new StringBuilder(); for (Map.Entry<?, ?> entry : map.entrySet()) { sb.append(entry.getKey()).append("=").append(entry.getValue()).append(","); } // 删除最后一个多余的逗号 if (sb.length() > 0) { sb.setLength(sb.length() - 1); } return sb.toString(); } }package com.kucun.data.entity; import java.lang.annotation.Annotation; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.OneToOne; import javax.persistence.Table; import com.kucun.data.entity.DTO.*; import com.fasterxml.jackson.annotation.JsonBackReference; import com.fasterxml.jackson.annotation.JsonManagedReference; import com.fasterxml.jackson.databind.annotation.JsonSerialize; /** * 板材 * @author Administrator * */ @Entity @Table(name="bancai") @JsonSerialize(using = FullEntitySerializer.class) public class Bancai implements EntityBasis { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) @JoinColumn(name = "caizhi_id") // private Caizhi caizhi; @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) @JoinColumn(name = "mupi1_id") private Mupi mupi1; @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) @JoinColumn(name = "mupi2_id") private Mupi mupi2; private Double houdu; @OneToOne( cascade = CascadeType.ALL, orphanRemoval = true, // 添加此配置 fetch = FetchType.LAZY ) @JoinColumn(name = "kucun_id", referencedColumnName = "id") private Kucun kucun; public Kucun getKucun() { return kucun; } public void setKucun(Kucun kucun) { this.kucun = kucun; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public Caizhi getCaizhi() { return caizhi; } public void setCaizhi(Caizhi caizhi) { this.caizhi = caizhi; } public Mupi getMupi1() { return mupi1; } public void setMupi1(Mupi mupi1) { this.mupi1 = mupi1; } public Mupi getMupi2() { return mupi2; } public void setMupi2(Mupi mupi2) { this.mupi2 = mupi2; } public Double getHoudu() { return houdu; } public void setHoudu(Double houdu) { this.houdu = houdu; } public Bancai(int id, Caizhi caizhi, Mupi mupi1, Mupi mupi2, Double houdu) { super(); this.id = id; this.caizhi = caizhi; this.mupi1 = mupi1; this.mupi2 = mupi2; this.houdu = houdu; } public Bancai() { super(); } } package com.kucun.data.entity; import java.util.List; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.OneToMany; import javax.persistence.Table; import javax.persistence.UniqueConstraint; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.kucun.data.entity.DTO.FullEntitySerializer; /** * 板材材质 * @author Administrator * */ @Entity @Table(name="caizhi", uniqueConstraints = { @UniqueConstraint(columnNames = "name") }) @JsonSerialize(using = FullEntitySerializer.class) public class Caizhi extends SimpleEntity implements EntityBasis{ @OneToMany(mappedBy="caizhi") private List<Bancai> bancais; public Caizhi() { super(); } // 添加反向关联维护方法 public void addBancai(Bancai bancai) { bancais.add(bancai); bancai.setCaizhi(this); } // 添加移除方法 public void removeBancai(Bancai bancai) { bancais.remove(bancai); bancai.setCaizhi(null); } public List<Bancai> getBancais() { return bancais; } public void setBancais(List<Bancai> bancais) { this.bancais = bancais; } } package com.kucun.data.entity; import java.util.List; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.ManyToMany; import javax.persistence.OneToMany; import javax.persistence.Table; import javax.persistence.UniqueConstraint; import org.hibernate.annotations.Type; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.kucun.data.entity.DTO.FullEntitySerializer; /** * 木皮 * @author Administrator * */ @Entity @Table(name="mupi", uniqueConstraints = { @UniqueConstraint(columnNames = "name") }) @JsonSerialize(using = FullEntitySerializer.class) public class Mupi extends SimpleEntity implements EntityBasis{ /** * 是否有油漆 */ @Column(name="you") @Type(type = "org.hibernate.type.BooleanType") private Boolean you; // 添加 OneToMany 映射 @OneToMany(mappedBy = "mupi1") // 指向 Bancai 中的 mupi1 字段 private List<Bancai> bancaisForMupi1; @OneToMany(mappedBy = "mupi2") // 指向 Bancai 中的 mupi2 字段 private List<Bancai> bancaisForMupi2; public List<Bancai> getBancaisForMupi1() { return bancaisForMupi1; } public void setBancaisForMupi1(List<Bancai> bancaisForMupi1) { this.bancaisForMupi1 = bancaisForMupi1; } public List<Bancai> getBancaisForMupi2() { return bancaisForMupi2; } public void setBancaisForMupi2(List<Bancai> bancaisForMupi2) { this.bancaisForMupi2 = bancaisForMupi2; } public Mupi() { super(); } public Boolean getYou() { return you; } public void setYou(Boolean you) { this.you = you; } } 2025-06-17 13:50:08.559 DEBUG 18340 --- [nio-8080-exec-7] o.s.web.servlet.DispatcherServlet : POST "/KuCun2/app/delete/bancai", parameters={} 2025-06-17 13:50:08.561 DEBUG 18340 --- [nio-8080-exec-7] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.kucun.controller.AppController#deleteEntity(String, Map) 2025-06-17 13:50:08.567 DEBUG 18340 --- [nio-8080-exec-7] m.m.a.RequestResponseBodyMethodProcessor : Read "application/json;charset=UTF-8" to [{id=14}] 2025-06-17 13:50:08.585 DEBUG 18340 --- [nio-8080-exec-7] org.hibernate.SQL : select bancai0_.id as id1_0_0_, bancai0_.caizhi_id as caizhi_i3_0_0_, bancai0_.houdu as houdu2_0_0_, bancai0_.kucun_id as kucun_id4_0_0_, bancai0_.mupi1_id as mupi5_0_0_, bancai0_.mupi2_id as mupi6_0_0_ from bancai bancai0_ where bancai0_.id=? Hibernate: select bancai0_.id as id1_0_0_, bancai0_.caizhi_id as caizhi_i3_0_0_, bancai0_.houdu as houdu2_0_0_, bancai0_.kucun_id as kucun_id4_0_0_, bancai0_.mupi1_id as mupi5_0_0_, bancai0_.mupi2_id as mupi6_0_0_ from bancai bancai0_ where bancai0_.id=? 2025-06-17 13:50:08.590 DEBUG 18340 --- [nio-8080-exec-7] m.m.a.RequestResponseBodyMethodProcessor : Using 'application/json', given [*/*] and supported [application/json, application/*+json, application/json, application/*+json] 2025-06-17 13:50:08.591 DEBUG 18340 --- [nio-8080-exec-7] m.m.a.RequestResponseBodyMethodProcessor : Writing [com.kucun.data.entity.Information@3dee3314] 2025-06-17 13:50:08.591 DEBUG 18340 --- [nio-8080-exec-7] o.s.web.servlet.DispatcherServlet : Completed 200 OK 删除失败:null
06-18
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值