Home > database >  How to find all 'missing' and 'extra' fields in dto by their entity using Reflec
How to find all 'missing' and 'extra' fields in dto by their entity using Reflec

Time:01-12

I need to find all 'missing' and extra fields for every dto's by their entities using reflection. For example.

I have

public class TestDto {
  long id;
  String name;
  int age;
  long personId;
  String phone;
}

And Entity

public class TestEntity {
  long id;
  String name;
  int age;
  Person person;
  String address;
}

person = personId(mapping). We don't need to print it like 'missing' field and 'extra' field.

Output: Missing fields for dto. Please add!: address; Extra fields for dto. Please remove! : phone;

i wrote

private final Map<String, String> fieldMappings = new HashMap<>();

      fieldMappings.put("person", "personId"); 

      Field[] dtoFields = auditDtoClass.getDeclaredFields();
      Field[] entityFields = entityClass.getDeclaredFields();

      List<String> missingFields = Arrays.stream(entityFields)
          .filter(field -> !fieldMappings.containsKey(field.getName()) && Stream.of(dtoFields)
              .noneMatch(dtoField -> dtoField.getName().equals(field.getName())))
          .map(Field::getName)
          .filter(field -> Arrays.stream(dtoFields)
              .noneMatch(f -> f.getName().equals(field)))
          .toList();

      List<String> extraFields = Arrays.stream(dtoFields)
          .filter(field -> !fieldMappings.containsValue(field.getName()) &&
              !fieldMappings.containsKey(field.getName()) && Stream.of(entityFields)
              .noneMatch(entityField -> entityField.getName().equals(field.getName())))
          .map(Field::getName)
          .filter(field -> Arrays.stream(entityFields)
              .noneMatch(f -> f.getName().equals(field)))
          .toList();

It's wrong.

Because programmer can add (private Person person field) in other entity without adding to dto and it didn't print it in missing fields.

I also think that we can link those fields fieldMappings.put("person", "personId"); to the entity/dto classes but now I don't understand how. I'd love to hear ideas on how to do this.

CodePudding user response:

This should do the trick. Please optimize the code to your needs.

{
    Map<String, Class<?>> testDtoDeclarationMap = getFieldDeclarationMap(new TestDto());
    Map<String, Class<?>> testEntityDeclarationMap = getFieldDeclarationMap(new TestEntity());

    testDtoDeclarationMap.forEach(((k, v) -> {
        if (!(testEntityDeclarationMap.containsKey(k) && testDtoDeclarationMap.get(k) == testEntityDeclarationMap.get(k))) {
            System.out.println("Extra fields for dto. Please remove!: '"   k   "' with the datatype: "   v);
        }
    }));

    testEntityDeclarationMap.forEach(((k, v) -> {
        if (!(testDtoDeclarationMap.containsKey(k) && testEntityDeclarationMap.get(k) == testDtoDeclarationMap.get(k))) {
            System.out.println("Missing fields for dto. Please add!: '"   k   "' with the datatype: "   v);
        }
    }));
}

Map<String, Class<?>> getFieldDeclarationMap(Object obj) {
    Map<String, Class<?>> map = new HashMap<>();
    Field[] declaredFields = obj.getClass().getDeclaredFields();
    for (int x=0; x<declaredFields.length; x  ) {
        Field field = declaredFields[x];
        String name = field.getName();
        Class<?> type = field.getType();
        map.put(name, type);
    }
    return map;
}

CodePudding user response:

I decided to create 'config' for mappings. For each entity.

private final Map<String, Map<String, String>> entityFieldMappings = new HashMap<>();

Then add some 'mappings'

 entityFieldMappings.put("TestEntity", Map.of("person", "personId"));

 entityFieldMappings.put("SampleEntity", Map.of(
        "customer", "customerId",
        "location", "locationId",
        "storageCondition", "storageConditionId"
    ));

Then created method for comparing entity and dto fields

private void compareFields(Class<?> entity, Class<?> dto, String entityName, Map<String, Map<String, String>> fieldMappings) {
    final Set<String> entityFieldNames = Arrays.stream(entity.getDeclaredFields()).map(Field::getName).collect(Collectors.toSet());
    final Set<String> dtoFieldNames = Arrays.stream(dto.getDeclaredFields()).map(Field::getName).collect(Collectors.toSet());

       Map<String, String> entityMappings = fieldMappings.get(entityName);
    
        Set<String> missingFields = entityFieldNames.stream()
            .filter(fieldName -> !dtoFieldNames.contains(fieldName) && !entityMappings.containsKey(fieldName))
            .collect(Collectors.toSet());
    
        Set<String> extraFields = dtoFieldNames.stream()
            .filter(fieldName -> !entityFieldNames.contains(fieldName) && !entityMappings.containsValue(fieldName))
            .collect(Collectors.toSet());
    
        if (!missingFields.isEmpty()) {
          log.error("Missing fields in Dto: "   dto.getName()   " for entity: "   entity.getName()   " are: "   missingFields);
        }
    
        if (!extraFields.isEmpty()) {
          log.error("Extra fields in Dto: "   dto.getName()   " for entity: "   entity.getName()   " are: "   extraFields);
        }
      }
  • Related