Java – modelmapper: ensure that the parameter of the method is zero and does not return void

I have the following configuration for the model mapper to convert an instance of the user class to an instance of extendedgetuserdto

public ExtendedGetUserDto convertToExtendedDto(User user) {
        PropertyMap<User,ExtendedGetUserDto> userMap = new PropertyMap<User,ExtendedGetUserDto>() {
            protected void configure() {
                map().setDescription(source.getDescription());
                map().setId(source.getId());
//              map().setReceivedExpenses(
//                      source.getReceivedExpenses()
//                              .stream()
//                              .map(expense -> expenseDtoConverter.convertToDto(expense))
//                              .collect(Collectors.toSet())
//                      );
                Set<GetInvitationDto> result = new HashSet<GetInvitationDto>();
                for (Invitation inv: source.getReceivedInvitations()) {
                    System.out.println("HELLO");
                    //result.add(null);
                }
                //map().setReceivedInvitations(result);
            }
        };
        modelmapper.addMappings(userMap);
        return modelmapper.map(user,ExtendedGetUserDto.class);
    }

I received this error before commenting out setreceivedexpense:

org.modelmapper.ConfigurationException: modelmapper configuration errors:

1) Invalid source method java.util.stream.Stream.map(). Ensure that method has zero parameters and does not return void.

2) Invalid source method java.util.stream.Stream.collect(). Ensure that method has zero parameters and does not return void.

2 errors

After spending some time without finding the root cause, I tried to delete all suspicious circular dependencies in dto (I referenced getuserdto in getexpensedto and used the return result of feedtoconverter). I still received the same error. I commented out map() Setreceivedexpenses (as you can see in the code) and replace it with a simple for loop

I received the following error:

1) Invalid source method java.io.PrintStream.println(). Ensure that method has zero parameters and does not return void.

Why do I receive these errors?

Edit 1

User. java

@Entity
@Table(name="User")
public class User implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name="id")
    private long id;

    @Column(name = "name")
    private String name;

    @Size(min=15,max=15)
    @Column(name="image_id")
    private String imageId;

    @Size(max=100)
    @Column(name="description")
    private String description;

    @OneToMany(mappedBy="admin")
    private Set<Group> ownedGroups;

    @ManyToMany(mappedBy="members")
    private Set<Group> memberGroups;

    @OneToMany(cascade = CascadeType.ALL,fetch=FetchType.EAGER,mappedBy="owner")
    private Set<Expense> ownedExpenses;

    @ManyToMany(cascade = CascadeType.REFRESH,fetch=FetchType.EAGER)
    private Set<Expense> receivedExpenses;

    @OneToMany(cascade=CascadeType.ALL)
    private Set<Invitation> ownedInvitations;

    @OneToMany(cascade=CascadeType.ALL)
    private Set<Invitation> receivedInvitations;
    //setters and getters for attributes
}

ExtendedGetUserDto. java

public class ExtendedGetUserDto extends GetUserDto {

    private static final long serialVersionUID = 1L;

    private Set<GetInvitationDto> receivedInvitations;
    private Set<GetExpenseDto> receivedExpenses;
    private Set<GetExpenseDto> ownedExpenses;
    private Set<GetGroupDto> ownedGroups;
    private Set<GetGroupDto> memberGroups;
    //setters and getters for attributes
}

Solution

You receive these errors because the propertymap limits what you can do in configure()

In Javadoc:

Technically, it involves bytecode analysis, manipulation and proxy, and it requires Java method calls suitable for this edsl This clever trick allows modelmapper to record your mapping instructions and replay them at will

To understand the library source code: the error you get is invalidsourcemethod, throw here in explicitmapping visitor, where objectmapper accesses and uses ASM library to detect the code of the configuration method

The following example is a stand - alone operational example that should help clarify I invite you to the modelmapertest Copy it in Java and actually run it, then switch annotations in configure() to reproduce the error:

import org.modelmapper.modelmapper;
import org.modelmapper.PropertyMap;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class modelmapperTest {

    public static void main(String[] args) {
        PropertyMap<Foo,FooDTO> propertyMap = new PropertyMap<Foo,FooDTO>() {
            protected void configure() {
                /* This is executed exactly ONCE,to "record" the mapping instructions.
                 * The bytecode of this configure() method is analyzed to produce new mapping code,* a new dynamically-generated class with a method that will basically contain the same instructions
                 * that will be "replayed" each time you actually map an object later.
                 * But this can only work if the instructions are simple enough (ie follow the DSL).
                 * If you add non-compliant code here,it will break before "configure" is invoked.
                 * Non-compliant code is supposedly anything that does not follow the DSL.
                 * In practice,the framework only tracks what happens to "map()" and "source",so
                 * as long as print instructions do not access the source or target data (like below),* the framework will ignore them,and they are safe to leave for debug. */
                System.out.println("Entering configure()");
                // This works
                List<String> things = source.getThings();
                map().setThingsCSVFromList(things);
                // This would fail (not because of Java 8 code,but because of non-DSL code that accesses the data)
                // String csv = things.stream().collect(Collectors.joining(","));
                // map().setThingsCSV(csv);
                System.out.println("Exiting configure()");
            }
        };
        modelmapper modelmapper = new modelmapper();
        modelmapper.addMappings(propertyMap);
        for (int i=0; i<5; i++) {
            Foo foo = new Foo();
            foo.setThings(Arrays.asList("a"+i,"b"+i,"c"+i));
            FooDTO dto = new FooDTO();
            modelmapper.map(foo,dto); // The configure method is not re-executed,but the dynamically generated mapper method is.
            System.out.println(dto.getThingsCSV());
        }
    }

    public static class Foo {

        List<String> things;

        public List<String> getThings() {
            return things;
        }

        public void setThings(List<String> things) {
            this.things = things;
        }

    }

    public static class FooDTO {

        String thingsCSV;

        public String getThingsCSV() {
            return thingsCSV;
        }

        public void setThingsCSV(String thingsCSV) {
            this.thingsCSV = thingsCSV;
        }

        public void setThingsCSVFromList(List<String> things) {
            setThingsCSV(things.stream().collect(Collectors.joining(",")));
        }

    }

}

If you execute it as is, you will get:

Entering configure()
Exiting configure()
a0,b0,c0
a1,b1,c1
a2,b2,c2
a3,b3,c3
a4,b4,c4

Therefore, configure () executes only once to record the mapping instruction, and then the generated mapping code (not configure () itself) is replayed five times, once for each object mapping

If you comment out map () in configure () Setthingscsvfromlist (things), and then uncomment the 2 lines below "this would failed", you will get:

Exception in thread "main" org.modelmapper.ConfigurationException: modelmapper configuration errors:

1) Invalid source method java.util.stream.Stream.collect(). Ensure that method has zero parameters and does not return void.

In short, you can't directly in propertymap Configure () executes complex custom logic, but you can call methods that do so This is because the framework only needs to detect and process the bytecode part of pure mapping logic (i.e. DSL), and it doesn't care what happens in these methods

solution

(a – legacy, for Java 6 / 7) strictly limit the configuration content required by DSL For example, move your "special needs" (records, collection logic, etc.) to a dedicated method of dto itself

In your case, moving this logic elsewhere may have more work, but the idea is there

Note that doc implies propertymap Configure, its DSL is mainly used for Java 6 / 7, but Java 8 and Lambdas now allow elegant solutions that do not require bytecode manipulation magic

(B – Java 8) check other options, such as converter

This is another example (use the same data class as above and the entire type of converter, because it is more suitable for my example, but you can execute by attribute):

Converter<Foo,FooDTO> converter = context -> {
        FooDTO dto = new FooDTO();
        dto.setThingsCSV(
                context.getSource().getThings().stream()
                        .collect(Collectors.joining(",")));
        return dto;
    };
    modelmapper modelmapper = new modelmapper();
    modelmapper.createTypeMap(Foo.class,FooDTO.class)
            .setConverter(converter);
    Foo foo = new Foo();
    foo.setThings(Arrays.asList("a","b","c"));
    FooDTO dto = modelmapper.map(foo,FooDTO.class);
    System.out.println(dto.getThingsCSV()); // a,b,c
The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>