Mapstruct对象插入数据库某个字段总是为空的bug详解

 更新时间:2022年07月25日 11:33:22   作者:女友在高考  
这篇文章主要为大家介绍了在一次需求开发Mapstruct中对象插入数据库某个字段总是为空的bug问题详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

前言

在一次需求的开发过程中,发现一个对象插入数据库时某个字段总是为空。

版本:lombok:1.18.24、mapstruct:1.5.2.Final

简化后的代码如下:

@Autowired
    private PersonService personService;
    public void test1(){
        Person person = personService.findById(1L);
        PersonDto personDto = PersonMapper.INSTANCE.personToPersonDto(person);
        personService.insert(personDto);
    }

这么简单的逻辑按理说不会出幺蛾子啊,我先排查了数据库里person id=1的记录发现值是有的啊,然后又排查了我的insert方法,也是没问题的。

经过一段时间的排查,才发现是

PersonDto personDto = PersonMapper.INSTANCE.personToPersonDto(person);

这行代码的问题。证据截图如下:

前面的时候addTeacherNum还有值,转化后怎么又没值了呢?

大家看到这里肯定猜测是不是我属性名不对,或者属性类型不对。我甚至还删除了之后用复制的方式来保证没有手敲敲错的情况。

完全是一模一样的属性啊。

我们知道mapstruct是编译时通过我们的PersonMapper接口来实现实现类,实现类里是setter、getter方法来实现的。于是我打开了PersonMapper的实现类准备一探究竟:

public class PersonMapperImpl implements PersonMapper {
    public PersonMapperImpl() {
    }
    public PersonDto personToPersonDto(Person person) {
        if (person == null) {
            return null;
        } else {
            PersonDtoBuilder personDto = PersonDto.builder();
            personDto.name(person.getName());
            return personDto.build();
        }
    }
}

竟然没有对我这个属性addTeacherNum进行赋值。这让我百思不得其解。只能去看看源码,试图找出原因。

如何调试Maven插件

前面我们提到mapstruct是在代码编译的时候就开始生成代码了,于是我们需要对maven编译期进行调试。方法如下:

  • maven debug命令
mvnDebug clean compile
  • idea远程debug

新建一个remote,然后修改端口为8000,然后在执行maven命令的同时,启动这个remote即可。

源码解析

断点应该打在哪里呢?

我们查看mapstruct的结构,一般先从配置的文件入手

找到了这个MappingProcessor类,我们可以看到这里面有个process方法,里面又调用了如下的这个方法:

private void processMapperTypeElement(ProcessorContext context, TypeElement mapperTypeElement) {
    Object model = null;
    for ( ModelElementProcessor<?, ?> processor : getProcessors() ) {
        try {
            model = process( context, processor, mapperTypeElement, model );
        }
        catch ( AnnotationProcessingException e ) {
           //省略
        }
    }
}

这段代码其实就是调用getProcessors()方法拿到多个processor,然后遍历调用。而这个getProcessors()就是从配置文件里通过SPI的方式加载对象。

这里面我们重点关注这个Processor:MapperCreationProcessor。它的process方法如下:

@Override
public Mapper process(ProcessorContext context, TypeElement mapperTypeElement, List<SourceMethod> sourceModel) {
    this.elementUtils = context.getElementUtils();
    this.typeUtils = context.getTypeUtils();
    this.messager =
        new MapperAnnotatedFormattingMessenger( context.getMessager(), mapperTypeElement, context.getTypeUtils() );
    this.options = context.getOptions();
    this.versionInformation = context.getVersionInformation();
    this.typeFactory = context.getTypeFactory();
    this.accessorNaming = context.getAccessorNaming();
    MapperOptions mapperOptions = MapperOptions.getInstanceOn( mapperTypeElement, context.getOptions() );
    List<MapperReference> mapperReferences = initReferencedMappers( mapperTypeElement, mapperOptions );
    MappingBuilderContext ctx = new MappingBuilderContext(
        typeFactory,
        elementUtils,
        typeUtils,
        messager,
        accessorNaming,
        context.getEnumMappingStrategy(),
        context.getEnumTransformationStrategies(),
        options,
        new MappingResolverImpl(
            messager,
            elementUtils,
            typeUtils,
            typeFactory,
            new ArrayList<>( sourceModel ),
            mapperReferences,
            options.isVerbose()
        ),
        mapperTypeElement,
        //sourceModel is passed only to fetch the after/before mapping methods in lifecycleCallbackFactory;
        //Consider removing those methods directly into MappingBuilderContext.
        Collections.unmodifiableList( sourceModel ),
        mapperReferences
    );
    this.mappingContext = ctx;
    return getMapper( mapperTypeElement, mapperOptions, sourceModel );
}

getMapper里面有一段这个方法引起我的注意:

List<MappingMethod> mappingMethods = getMappingMethods( mapperOptions, methods );

猜测这段就是获取要写入的set、get方法。 于是一路跟踪:

发现mapstruct里面把方法分为了下面四类,而我的addTeacherNum属性通过lombok生成的方法methodType被分到了ADDER里面。

而在生成我的Mapper实现类的时候它会只过滤setter方法。

List<Accessor> candidates = new ArrayList<>( getSetters() );

至此真相大白。

以上就是Mapstruct中对象插入数据库某个字段总是为空的bug详解的详细内容,更多关于Mapstruct插入数据字段为空的资料请关注脚本之家其它相关文章!

相关文章

  • 解析web.xml中在Servlet中获取context-param和init-param内的参数

    解析web.xml中在Servlet中获取context-param和init-param内的参数

    本篇文章是对web.xml中在Servlet中获取context-param和init-param内的参数进行了详细的分析介绍,需要的朋友参考下
    2013-07-07
  • java实现转圈打印矩阵算法

    java实现转圈打印矩阵算法

    这篇文章主要为大家详细介绍了java实现转圈打印矩阵算法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-03-03
  • 浅析RxJava处理复杂表单验证问题的方法

    浅析RxJava处理复杂表单验证问题的方法

    这篇文章主要介绍了RxJava处理复杂表单验证问题的相关资料,非常不错具有参考借鉴价值,需要的朋友可以参考下
    2016-06-06
  • IntelliJ Idea 2020.1 正式发布,官方支持中文(必看)

    IntelliJ Idea 2020.1 正式发布,官方支持中文(必看)

    这篇文章主要介绍了IntelliJ Idea 2020.1 正式发布,官方支持中文了,本文通过截图的形式给大家展示,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-04-04
  • spring mvc中@RequestBody注解的作用说明

    spring mvc中@RequestBody注解的作用说明

    这篇文章主要介绍了spring mvc中@RequestBody注解的作用说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-08-08
  • Fastjson反序列化随机性失败示例详解

    Fastjson反序列化随机性失败示例详解

    这篇文章主要为大家介绍了Fastjson反序列化随机性失败示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-08-08
  • 详解JAVA中接口的定义和接口的实现

    详解JAVA中接口的定义和接口的实现

    这篇文章主要介绍了JAVA中接口的定义和接口的实现,文中讲解非常细致,配合代码更好的帮大家学习参考,感兴趣的朋友可以了解下
    2020-06-06
  • java实现猜拳小游戏

    java实现猜拳小游戏

    这篇文章主要为大家详细介绍了java实现猜拳小游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-01-01
  • MyBatis-Plus详解(环境搭建、关联操作)

    MyBatis-Plus详解(环境搭建、关联操作)

    MyBatis-Plus 是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生,今天通过本文给大家介绍MyBatis-Plus环境搭建及关联操作,需要的朋友参考下吧
    2022-09-09
  • Java中BigInteger用法小结

    Java中BigInteger用法小结

    这篇文章主要介绍了Java中BigInteger用法的详解,在这里,我们详细描述下BigInteger的用法,在使用之前,我们需要导入java.math.*包,本文通过实例代码相结合给大家详细讲解,需要的朋友可以参考下
    2023-03-03

最新评论