2016-02-02 2 views
0

Вдохновленный this answer, я написал собственный конвектор (вы можете найти весь рабочий пример в Github repo). для бульдозеров, чтобы конвертировать между:Как собирать коллекции неизменяемых объектов в бульдозер

public class MyEntity { 
    private List<ObjectId> attachmentIds; 

    public List<ObjectId> getAttachmentIds() { return attachmentIds; } 

    public void setAttachmentIds(List<ObjectId> attachmentIds) { 
     this.attachmentIds = attachmentIds; 
    } 
} 

И его DTO:

public class MyEntityDto { 
    private List<FileDataDto> attachments; 

    public List<FileDataDto> getAttachments() { return attachments; } 

    public void setAttachments(List<FileDataDto> attachments) { 
     this.attachments = attachments; 
    } 
} 

MyEntity содержит только идентификаторы для файлов, хранящихся в Монго Databse. Его DTO, который отправляется интерфейсу в JSON, должен содержать как идентификатор, так и имя файла (который является содержимым класса FileDataDto). Мой конвертер:

public class FileIdToFileDataConverter extends DozerConverter<ObjectId, FileDataDto> { 
    public FileIdToFileDataConverter() {super(ObjectId.class, FileDataDto.class); } 

    @Override 
    public FileDataDto convertTo(ObjectId source, FileDataDto destination) { 
     if (source == null) { 
      return null; 
     } 
     FileDataDto fileData = destination == null ? new FileDataDto() : destination; 
     fileData.setId(source.toString()); 
     // fetch the file from repository and update the name from db 
     fileData.setFilename("myfile.txt"); 
     return fileData; 
    } 

    @Override 
    public ObjectId convertFrom(FileDataDto source, ObjectId destination) { 
     return source == null ? null : new ObjectId(source.getId()); 
    } 
} 

Конверсия работает, как ожидалось в MyEntity направлении ->MyEntityDto. Однако это не соответствует обратному. Он использует ObjectId, созданный Dozer (передан как аргумент destination) вместо того, который возвращается преобразователем. Этот тест

@Test 
public void dtoToMyEntity() { 
    MyEntityDto dto = new MyEntityDto(); 
    FileDataDto fileData = new FileDataDto(); 
    fileData.setFilename("file.txt"); 
    fileData.setId(new ObjectId().toString()); 
    dto.setAttachments(Arrays.asList(fileData)); 
    MyEntity myEntity = mapper.map(dto, MyEntity.class); 
    assertEquals(fileData.getId(), myEntity.getAttachmentIds().get(0).toString()); 
} 

завершается с сообщением, например:

org.junit.ComparisonFailure: 
    Expected :56b0a9d110a937fc32a6db18 
    Actual :56b0a9d110a937fc32a6db19 

Вы можете найти весь тест и конфигурацию я использую в Github repo.

Как сделать преобразователь работать в обоих направлениях?

ответ

1

Это связано с ошибкой в ​​бульдозер, который вызывает, что пользовательские преобразователи не используются при отображении через API: https://github.com/DozerMapper/dozer/issues/242

Таким образом, вы можете предоставить либо отображение через XML:

<?xml version="1.0" encoding="UTF-8"?> 
<mappings xmlns="http://dozer.sourceforge.net" 
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
      xsi:schemaLocation="http://dozer.sourceforge.net 
      http://dozer.sourceforge.net/schema/beanmapping.xsd"> 
    <configuration> 
     <custom-converters> 
      <converter type="com.example.mapping.FileIdToFileDataConverter"> 
       <class-a>org.bson.types.ObjectId</class-a> 
       <class-b>com.example.mapping.entity.FileDataDto</class-b> 
      </converter> 
     </custom-converters> 
    </configuration> 
    <mapping> 
     <class-a>com.example.mapping.entity.MyEntity</class-a> 
     <class-b>com.example.mapping.entity.MyEntityDto</class-b> 
     <field> 
      <a>attachmentIds</a> 
      <b>attachments</b> 
      <a-hint>org.bson.types.ObjectId</a-hint> 
      <b-hint>com.example.mapping.entity.FileDataDto</b-hint> 
     </field> 
    </mapping> 
</mappings> 

А потом

mapper.setMappingFiles(Arrays.asList("dozerconfig.xml")); 

Или, если вы не хотите использовать xml, yo и может создать обходной путь для использования собственного ObjectIdFactory:

mapping(type(ObjectId.class).beanFactory(ObjectIdFactory.class), FileDataDto.class) 
    .fields(this_(), this_(), customConverter(FileIdToFileDataConverter.class)); 

и класс фабрики

public class ObjectIdFactory implements BeanFactory { 
    @Override 
    public Object createBean(Object source, Class<?> sourceClass, String targetBeanId) { 
     if (source == null) { 
      return null; 
     } 
     if (source instanceof ObjectId) { 
      return source; // we can return source, because it's immutable 
     } 
     if (source instanceof String) { 
      return new ObjectId((String) source); 
     } 
     if (source instanceof FileDataDto) { 
      return new ObjectId(((FileDataDto) source).getId()); 
     } 
     throw new MappingException("ObjectId should be of type ObjectId, String or FileDataDto"); 
    } 
} 

Причина, почему это workaroud работает и почему идентификаторы не совпадают:

бульдозер по умолчанию использует конструктор no-args класса для создания нулевых значений. ObjectId - неизменяемый класс, а конструктор no-args создает новый экземпляр, основанный на отметке времени.

Смежные вопросы