mp实现多对多关系表--不单独定义实体类实现两个类的多表关系

本文介绍了如何在MP项目中,通过定义自定义注解`@MultiRequestBody`和使用Jackson处理JSON,解决在服务层处理多对多关系表时的参数映射问题,以及如何配置SpringMVC解析器以支持这种注解的解析。
摘要由CSDN通过智能技术生成

mp中实现多对多关系表

方法一:在类中定义list,mapper中添加对应collection
在这里插入图片描述
!!!然后将select方法封装到service层(先mapper,再service)

方法二:使用自定义注解@multiRequestBody
如果不想定义单独的实体类关联其余两个实体类来体现多表关系,可以选择使用自定义注解@multiRequestBody
缺点:
传递参数过多的话,代码冗余,难以维护。
在这里插入图片描述
代码如下:

  1. 定义注解:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


/**
* @author Anna
* @date 2023/6/9
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface MultiRequestBody {


    /**
     * 解析时用到的 JSON 中的 key
     */
    String value() default "";


    /**
     * 是否必传的参数
     */
    boolean required() default true;


    /**
     * 当 value 的值或者参数名不匹配时,是否允许解析最外层属性得到该对象
     */
    boolean parseAllFields() default true;


}
  1. 定义参数解析器
import com.cawei.website.anno.MultiRequestBody;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;




import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.core.MethodParameter;
import org.springframework.util.Assert;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;


import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;


/**
* MultiRequestBody解析器
* 解决的问题:
* 1、单个字符串等包装类型都要写一个对象才可以用@RequestBody接收;
* 2、多个对象需要封装到一个对象里才可以用@RequestBody接收。
* 主要优势:
* 1、支持通过注解的value指定JSON的key来解析对象。
* 2、支持通过注解无value,直接根据参数名来解析对象
* 3、支持基本类型的注入
* 4、支持GET和其他请求方式注入
* 5、支持通过注解无value且参数名不匹配JSON串key时,根据属性解析对象。
* 6、支持多余属性(不解析、不报错)、支持参数“共用”(不指定value时,参数名不为JSON串的key)
* 7、支持当value和属性名找不到匹配的key时,对象是否匹配所有属性。
* @author Anna
* @date 2023/6/9
*/
public class MultiRequestBodyResolver implements HandlerMethodArgumentResolver {




    private static final Set<Class> classSet = new HashSet<>(16);
    private static ObjectMapper objectMapper = new ObjectMapper();
    private static final String JSON_REQUEST_BODY = "JSON_REQUEST_BODY";


    static {
        classSet.add(Integer.class);
        classSet.add(Long.class);
        classSet.add(Short.class);
        classSet.add(Float.class);
        classSet.add(Double.class);
        classSet.add(Boolean.class);
        classSet.add(Byte.class);
        classSet.add(Character.class);
    }




    /**
     * 支持的方法参数类型
     * @param methodParameter
     * @return
     */
    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        return methodParameter.hasParameterAnnotation(MultiRequestBody.class);
    }


    /**
     * 参数解析
     * @param methodParameter
     * @param modelAndViewContainer
     * @param nativeWebRequest
     * @param webDataBinderFactory
     * @return
     * @throws Exception
     */
    @Override
    public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer,
                                  NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
        Object result;
        Object value;


        // 获取请求体
        String requestBody = getRequestBody(nativeWebRequest);
        // 允许使用不带引号的字段
        objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
        // 解析 JSON 串
        JsonNode rootNode = objectMapper.readTree(requestBody);
        // JSON 串为空抛出异常
        Assert.notNull(rootNode, String.format("param %s parsing failed", requestBody));


        // 获取注解
        MultiRequestBody multiRequestBody = methodParameter.getParameterAnnotation(MultiRequestBody.class);
        Assert.notNull(multiRequestBody, String.format("param %s parsing failed", requestBody));


        String key = multiRequestBody.value();
        // 根据注解 value 解析 JSON 串,如果没有根据参数的名字解析 JSON
        if (StringUtils.isNotBlank(key)) {
            value = rootNode.get(key);
            // 如果为参数必填但未根据 key 成功得到对应 value 抛出异常
            Assert.isTrue(multiRequestBody.required() && Objects.nonNull(value), String.format("required param %s is not present", key));
        } else {
            key = methodParameter.getParameterName();
            value = rootNode.get(key);
        }


        // 获取参数的类型
        Class<?> paramType = methodParameter.getParameterType();
        // 成功从 JSON 解析到对应 key 的 value
        if (Objects.nonNull(value)) {
            return objectMapper.readValue(value.toString(), paramType);
        }


        // 未从 JSON 解析到对应 key(可能是注解的 value 或者是参数名字) 的值,要么没传值,要么传的名字不对
        // 如果参数为基本数据类型,且为必传参数抛出异常
        Assert.isTrue(!(isBasicDataTypes(paramType) && multiRequestBody.required()), String.format("required param %s is not present", key));
        // 参数非基本数据类型,如果不允许解析外层属性,且为必传参数报错抛出异常
        Assert.isTrue(!(!multiRequestBody.parseAllFields() && multiRequestBody.required()), String.format("required param %s is not present", key));


        try {
            // 既然找不到对应参数,而且非基本类型,我们可以解析外层属性,将整个 JSON 作为参数进行解析。解析失败会抛出异常
            result = objectMapper.readValue(requestBody, paramType);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        // 必填参数的话,看解析出来的参数是否对应,非必填直接返回吧
        if (multiRequestBody.required()) {
            Field[] declaredFields = paramType.getDeclaredFields();
            for (Field field : declaredFields) {
                field.setAccessible(true);
                Assert.notNull(field.get(result), String.format("required param %s is not present", key));
            }
        }
        return result;
    }


    /**
     * 获取请求 JSON 字符串
     */
    private String getRequestBody(NativeWebRequest webRequest) {
        HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
        String jsonBody = (String) webRequest.getAttribute(JSON_REQUEST_BODY, NativeWebRequest.SCOPE_REQUEST);
        if (StringUtils.isEmpty(jsonBody)) {
            try (BufferedReader reader = servletRequest.getReader()) {
                jsonBody = IOUtils.toString(reader);
                webRequest.setAttribute(JSON_REQUEST_BODY, jsonBody, NativeWebRequest.SCOPE_REQUEST);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return jsonBody;
    }


    /**
     * 判断是否为基本数据类型包装类
     */
    private boolean isBasicDataTypes(Class clazz) {
        return classSet.contains(clazz);
    }


}

  1. 定义参数解析配置文件,覆盖原本解析器
import com.cawei.website.handler.MultiRequestBodyResolver;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;


import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;


/**
* @author Anna
* @date 2023/6/9
*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    /**
     * 参数解析器
     * @see MultiRequestBodyResolver
     */
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(new MultiRequestBodyResolver());
    }


    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        MappingJackson2HttpMessageConverter jacksonConver = new MappingJackson2HttpMessageConverter();
        ArrayList<MediaType> mediaTypes = new ArrayList<>();
        mediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
        jacksonConver.setSupportedMediaTypes(mediaTypes);
        jacksonConver.setDefaultCharset(Charset.forName("UTF-8"));
        converters.add(jacksonConver);
    }
}
  • 17
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值