Generic way to update pojos via getters and setters

后端 未结 2 1313
小鲜肉
小鲜肉 2021-02-15 16:42

Let\'s say I have POJO with getters and setters of different types. I want to write some generic algorithm for updating data from one to another based on just defining getters a

相关标签:
2条回答
  • 2021-02-15 17:21

    I know you already have an answer but for anyone needing something like this in the future: I have developed a small library around this context - datus.

    Here is an example which shows some of its features:

    class Person {
      //getters + setters omitted for brevity
      private String firstName;
      private String lastName;
    }
    
    class PersonDTO {
      //getters + setters + empty constructor omitted for brevity
      private String firstName;
      private String lastName;
    }
    
      //the mutable API defines a mapping process by multiple getter-setter steps
      Mapper<Person, PersonDTO> mapper = Datus.forTypes(Person.class, PersonDTO.class).mutable(PersonDTO::new)
          .from(Person::getFirstName).into(PersonDTO.setFirstName)
          .from(Person::getLastName)
          .given(Objects::nonNull, ln -> ln.toUpperCase()).orElse("fallback")
          .into(PersonDTO::setLastName)
          .from(/*...*/).into(/*...*/)
          .build();
    
      Person person = new Person();
    person.setFirstName("firstName");
        person.setLastName(null);
        PersonDTO personDto = mapper.convert(person);
    /*
        personDto = PersonDTO [
            firstName = "firstName",
            lastName = "fallback"
        ]
    */
        person.setLastName("lastName");
        personDto = mapper.convert(person);
    /*
        personDto = PersonDTO [
            firstName = "firstName",
            lastName = "LASTNAME"
        ]
    */
    
    0 讨论(0)
  • 2021-02-15 17:28

    Use something like

    private static final List<BiConsumer<Entity,Entity>> ACCESSORS =
        Collections.unmodifiableList(Array.asList(
            (src,dst) -> dst.setAreaCode(src.getAreaCode()),
            (src,dst) -> dst.setOtherProperty(src.getOtherProperty())
            /* etc */
    ));
    

    Then, you can loop over the list and apply each operation to two entities, like

    static final void copyAll(Entity src, Entity dst) {
        ACCESSORS.forEach(op -> op.accept(src, dst));
    }
    

    The key point is that the actual property value type is handled within each BiConsumer but is not part of the generic signature anymore and therefore doesn’t need to be declared for ACCESSORS. It’s even more efficient, as it can handle primitive data types without boxing overhead.

    The Map wasn’t an appropriate data structure for this task anyway, as for these functions, no meaningful lookup could be performed, so this is a data structure is only intended to be iterated over.

    You can integrate the “copy only when non-null” logic with a generic helper method:

    private static final List<BiConsumer<Entity,Entity>> ACCESSORS =
        Collections.unmodifiableList(Arrays.asList(
            copyWhenNonNull(Entity::getAreaCode, Entity::setAreaCode),
            copyWhenNonNull(Entity::getOtherProperty, Entity::setOtherProperty)
            /* etc */
    ));
    private static <E,V> BiConsumer<E,E> copyWhenNonNull(
        Function<? super E, ? extends V> getter, BiConsumer<? super E, ? super V> setter) {
        return (src,dst) -> {
            V value = getter.apply(src);
            if(value != null) setter.accept(dst, value);
        };
    }
    

    The copyAll method doesn’t change. This even allows mixing unconditional copying of properties which can never be null with conditional copying.

    0 讨论(0)
提交回复
热议问题