Mapstruct circular reference

MapStruct Circular Reference

Circular reference is a situation in which two or more objects refer to each other, creating an infinite loop. MapStruct is a Java library used for object-to-object mapping, and it can also encounter circular reference scenarios. In this case, circular reference means that two or more objects have references to each other, which can lead to a stack overflow error or incorrect mappings.

Let’s take an example to understand this scenario:

    
      // Source class
      public class Source {
        private Long id;
        private List<Target> targets;
      
        // constructors, getters, setters
      }
      
      // Target class
      public class Target {
        private Long id;
        private Source source;
      
        // constructors, getters, setters
      }
    
  

In the above example, we have two classes, Source and Target, where Source has a list of Target objects, and each Target object has a reference to the Source object.

Now, let’s assume you want to map a Source object to a Target object using MapStruct:

    
      @Mapper
      public interface SourceTargetMapper {
      
        Target sourceToTarget(Source source);
      
        Source targetToSource(Target target);
      
      }
    
  

If you directly use the above mapper, it will lead to a circular reference error. MapStruct doesn’t support circular references by default, and it needs some additional configuration to handle them properly.

To handle circular references in MapStruct, you can use the `@Context` annotation to identify the context in which the mapping is being performed. By passing this context between mappings, MapStruct can break the circular reference and avoid infinite recursion.

Here’s an updated version of the mapper with the `@Context` annotation:

    
      @Mapper
      public interface SourceTargetMapper {
      
        Target sourceToTarget(Source source, @Context CycleAvoidingMappingContext context);
      
        Source targetToSource(Target target, @Context CycleAvoidingMappingContext context);
      
      }
    
  

In the above example, we have added the `@Context CycleAvoidingMappingContext` as a parameter in both mapping methods. The `CycleAvoidingMappingContext` is a custom context object that you need to implement.

Here’s an example implementation of the `CycleAvoidingMappingContext`:

    
      public class CycleAvoidingMappingContext {
        private Map<Long, Object> knownInstances = new HashMap<>();
      
        public Object getMappedInstance(Long id) {
          return knownInstances.get(id);
        }
      
        public void storeMappedInstance(Long id, Object instance) {
          knownInstances.put(id, instance);
        }
      }
    
  

The `CycleAvoidingMappingContext` class keeps track of already mapped instances using a `knownInstances` map.

Finally, you need to register the `CycleAvoidingMappingContext` in your Spring configuration or any DI framework you are using:

    
      @Configuration
      public class MapStructConfig {
      
        @Bean
        public CycleAvoidingMappingContext cycleAvoidingMappingContext() {
          return new CycleAvoidingMappingContext();
        }
      
        // Other configurations
      
      }
    
  

By following the above steps, you can handle circular references in MapStruct mappings. The `CycleAvoidingMappingContext` ensures that already mapped instances are reused instead of creating new instances, breaking the circular reference.

Hope this helps in understanding how to handle circular references with MapStruct using a custom context object.

Related Post

Leave a comment