1

I have following two classes :

public class TodoDto {

    private Long id;
    private String description;
    private String title;
    private long version;

    //Getters & Setters
}

@Entity
@Table(name = "todos")
public class Todo {

    public static final int MAX_LENGTH_DESCRIPTION = 500;
    public static final int MAX_LENGTH_TITLE = 100;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column(name = "description", length = MAX_LENGTH_DESCRIPTION)
    private String description;

    @Column(name = "title", nullable = false, length = MAX_LENGTH_TITLE)
    private String title;

    @Version
    private long version;

    // Getters & Setters
}

I'm using the latest orika dependency :

<!-- Orika mapping -->
<dependency>
    <groupId>ma.glasnost.orika</groupId>
    <artifactId>orika-core</artifactId>
    <version>1.4.6</version>
</dependency>

I'm trying to map Optional<Todo> into Optional<TodoDto>.

Here is the main method :

@Override
public Optional<TodoDto> findById(final Long id) {
    final MapperFacade mapper = new ConfigurableMapper() {
        @Override
        protected void configure(final MapperFactory factory) {
            factory.registerMapper(new CustomMapper<Optional<Todo>, Optional<TodoDto>>() {
                @Override
                public void mapAtoB(final Optional<Todo> s, Optional<TodoDto> d, final MappingContext context) {
                    d = Optional.empty();
                    d.get().setId(s.get().getId());
                    // ...
                }
            });
        }
    };
    final Type<Optional<Todo>> optionalOfTodo = new TypeBuilder<Optional<Todo>>() {
    }.build();
    final Type<Optional<TodoDto>> optionalOfTodoDto = new TypeBuilder<Optional<TodoDto>>() {
    }.build();
    return mapper.map(todoRepository.findById(id), optionalOfTodo, optionalOfTodoDto);
}

My test method :

@Test
public void testFindByIdNoException() {
    final Optional<TodoDto> todoDto = todoManager.findById(1L);
    todoDto.orElseThrow(NoSuchElementException::new);
    assertThat(
            todoDto.get(),
            allOf(hasProperty("id", is(1L)), hasProperty("title", is("Foo")),
                    hasProperty("description", is("Lorem ipsum"))));
}

Here is the error :

ma.glasnost.orika.MappingException: While attempting the following mapping:
sourceClass = class java.util.Optional
sourceType = java.util.Optional<Todo>
destinationType = java.util.Optional<TodoDto>
resolvedStrategy = InstantiateAndUseCustomMapperStrategy<Optional<Todo>, Optional<TodoDto>> {customMapper: com.mycompany.core.manager.TodoManagerImpl$1$1@359b1f57, unenhancer: ma.glasnost.orika.unenhance.BaseUnenhancer@14be750c, objectFactory: ma.glasnost.orika.generated.Optional_Optional_ObjectFactory419300372496922419300467640277$1@3be80ece}
Error occurred: java.lang.IllegalArgumentException: java.util.Optional is an unsupported source class for constructing instances of java.util.Optional
-----begin dump of current state-----------------------------
Registered object factories: 1 (approximate size: 6 850,1 kB)
  [Optional<TodoDto>] : {Optional<Todo>=ma.glasnost.orika.generated.Optional_Optional_ObjectFactory419300372496922419300467640277$1@3be80ece}
-------------------------------------------------------------
Registered mappers: 1 (approximate size: 8 149,4 kB)
  [0] : com.mycompany.core.manager.TodoManagerImpl$1$1@359b1f57
-------------------------------------------------------------
Registered concrete types: 6 (approximate size: 225,4 kB)
  [interface java.util.Collection] : ArrayList<Object>
  [interface java.util.List] : ArrayList<Object>
  [interface java.util.Set] : LinkedHashSet<Object>
  [interface java.util.Map$Entry] : MapEntry<Object, Object>
  [class java.util.Optional] : Optional<Object>
  [interface java.util.Map] : LinkedHashMap<Object, Object>
-------------------------------------------------------------
Resolved strategies: 1 (approximate size: 8 149,7 kB)
{source: Optional, dest: Optional<TodoDto>, in-place:false}: InstantiateAndUseCustomMapperStrategy<Optional<Todo>, Optional<TodoDto>> {customMapper: com.mycompany.core.manager.TodoManagerImpl$1$1@359b1f57, unenhancer: ma.glasnost.orika.unenhance.BaseUnenhancer@14be750c, objectFactory: ma.glasnost.orika.generated.Optional_Optional_ObjectFactory419300372496922419300467640277$1@3be80ece}
-------------------------------------------------------------
Unenhance strategy: ma.glasnost.orika.unenhance.BaseUnenhancer@14be750c
-----end dump of current state-------------------------------
    at ma.glasnost.orika.impl.ExceptionUtility.newMappingException(ExceptionUtility.java:55)
    at ma.glasnost.orika.impl.MapperFacadeImpl.map(MapperFacadeImpl.java:280)
    at ma.glasnost.orika.impl.MapperFacadeImpl.map(MapperFacadeImpl.java:243)
    at ma.glasnost.orika.impl.MapperFacadeImpl.map(MapperFacadeImpl.java:133)
    at ma.glasnost.orika.impl.ConfigurableMapper.map(ConfigurableMapper.java:222)
    at com.mycompany.core.manager.TodoManagerImpl.findById(TodoManagerImpl.java:55)
    at com.mycompany.core.manager.TodoManagerTest.testFindByIdNoException(TodoManagerTest.java:48)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:73)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:82)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:73)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:224)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:83)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:68)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:163)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
Caused by: java.lang.IllegalArgumentException: java.util.Optional is an unsupported source class for constructing instances of java.util.Optional
    at ma.glasnost.orika.generated.Optional_Optional_ObjectFactory419300372496922419300467640277$1.create(Optional_Optional_ObjectFactory419300372496922419300467640277$1.java)
    at ma.glasnost.orika.impl.mapping.strategy.InstantiateAndUseCustomMapperStrategy.getInstance(InstantiateAndUseCustomMapperStrategy.java:55)
    at ma.glasnost.orika.impl.mapping.strategy.UseCustomMapperStrategy.map(UseCustomMapperStrategy.java:61)
    at ma.glasnost.orika.impl.MapperFacadeImpl.map(MapperFacadeImpl.java:269)
    ... 34 more

When i use the entity and dto object without java.util.Optional everything works fine.

Solution :

@Override
public Optional<TodoDto> findById(final Long id) {
    return todoRepository.findById(id).map(entity -> mapper.map(entity, TodoDto.class));
}
4

1 回答 1

2

You don't need to pass Optional to the Orika mapper.

Optional is a monad, a container type that allows you to perform operations inside the container. In other words, don't use Optional inside your findById method, but do put your findById code inside the Optional :)

E.g. If your Entity Repo method findBy(id) returns Optional

final Type<Todo> todoType = new TypeBuilder<Todo>() {}.build();
final Type<TodoDto> todoDtoType = new TypeBuilder<TodoDto>() {}.build();

return todoRepository.findById(id).
                  map(entity-> mapper.map(entity, todoType,todoDtoType)); 
于 2015-09-23T10:19:44.610 回答