ModelMapper integration with Jooq Record

2.4k views Asked by At

===== POJO =====

// Employee POJO
     @JsonInclude(JsonInclude.Include.NON_NULL)
     @JsonNaming(PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy.class)
     public class Employee implements Serializable {
       private Integer id;
       private String name;
       private Integer companyId;
       // assume getters ,setters and serializable implementations.
     }
// Company POJO
     @JsonInclude(JsonInclude.Include.NON_NULL)
     @JsonNaming(PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy.class)
     public class Company implements Serializable {
       private Integer id;
       private String name;
       // assume getters ,setters and serializable implementations.
     }

// EmployeeVO POJO
     @JsonInclude(JsonInclude.Include.NON_NULL)
     @JsonNaming(PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy.class)
     public class EmployeeVO implements Serializable {
       private Employee employee;
       private Company company;

       // assume getters ,setters and serializable implementations.
     }

===== My DAO layer class =====

public List<EmployeeVO> getEmployees(){     
    // configuring model mapper.
       ModelMapper modelMapper = new ModelMapper();
       modelMapper.getConfiguration()
                  .addValueReader(new RecordValueReader())
              .setSourceNameTokenizer(NameTokenizers.UNDERSCORE);

    //property map configuration.
      PropertyMap<Record, EmployeeVO> employeeVOMap = new PropertyMap<Record, EmployeeVO>() {
       protected void configure() {
          map().getEmployee().setName(this.<String>source("name"));
          map().getEmployee()..setId(this.<Integer>source("id"));
          map().getCompany().setName(this.<String>source("comp_name"));
          map().getCompany().setId(this.<String>source("comp_id"));
        }
     };
    // TypeMap config
    modelMapper.createTypeMap(Record.class, EmployeeVO.class);
    // adding employeeVOMap .
    modelMapper.addMappings(employeeVOMap);         

// JOOQ query  
    List<Field<?>> fields = Lists.newArrayList();  
    //  fields includes, id, name, comp_name, comp_id

    SelectJoinStep query = select(dslContext, fields).from(EMPLOYEE)
                                       .join(COMPANY)
                                       .on(COMPANY.ID.equal(EMPLOYEE.COMPANY_ID));    
        Result<Record> records = query.fetch();

    Record record = null;
        Iterator<Record> it = records.iterator();
        List<EmployeeVO> employeeList=  Lists.newArrayList();
        while (it.hasNext()) {
            record = it.next();

            EmployeeVO employeeVOObj =
                    modelMapper.map(record, EmployeeVO.class);
            employeeList.add(employeeVOObj);
        }
    return employeeList;    
}

===== Error log =====

1) Error mapping org.jooq.impl.RecordImpl to com.myportal.bingo.db.model.EmployeeVO 1 error] with root cause java.lang.ArrayIndexOutOfBoundsException: -1

Note: ModelMapper throws the above exception when it reaches below method.

private void matchSource(TypeInfo<?> sourceTypeInfo, Mutator destinationMutator) in ImplicitMappingBuilder.java

sourceTypeInfo.getAccessors() is null.

Any help?

1

There are 1 answers

6
YOY On

Had the same problem, or at least which looked the same. (You can move directly to my solution in the last paragraph.) Lots of debugging have shown the following:

if accessors on that line (mentioned in your question) are null, then accessors = PropertyInfoSetResolver.resolveAccessors(source, type, configuration) line in TypeInfoImpl class is executed, and the reason of exception in my case was this call:

valueReader.get(source, memberName) at the following piece of code at 'resolveAccessors' method in the PropertyInfoSetResolver class:

if (valueReader == null)
  resolveProperties(type, true, configuration, accessors);
else {
  NameTransformer nameTransformer = configuration.getSourceNameTransformer();
  for (String memberName : valueReader.memberNames(source))
    accessors.put(nameTransformer.transform(memberName, NameableType.GENERIC),
        new ValueReaderPropertyInfo(valueReader, valueReader.get(source, memberName),
            memberName));

which ends up in source.getValue(memberName.toUpperCase()), where source is JOOQ's Record; InvoiceRecord in my case. And - tada - for some reason invoice.getValue("INVOICE_ID") ends up in the exception (no such field and therefore indexOf returns -1 which causes the ArrayIndexOutOfBoundsException), while invoice.getValue("invoice_id") is totally fine.

So else condition (the same piece of code above) wasn't the right way to execute the code, and if case turned out to be ok.

So that's what helped me in my particular case: removing of the row modelMapper.getConfiguration().addValueReader(new RecordValueReader()). Hope this will help you too.