lombok + Orika + Builder Pattern

Reference


Introduction

  1. Builder Pattern is suggested in Effective Java and used to generate immutable object.
  2. Builder Pattern introduces many codes, lombok project makes it easier
  3. We often use Orika to map data when transfer object from UI entity to domain entity or data persistence object to domain entity.
  4. It's hard to map Java Bean to Builder Pattern object because Builder Pattern doesn't follows Java Bean convention. It causes we can't leverage lombok and Orika at the same time

How to fix

  1. Copy and paste the following code: BuilderPropertyResolver
  2. /**
     * This class is used to map from an object to a builder who follows Builder Pattern.
     * 
     * <pre>
     * MapperFactory factory = new DefaultMapperFactory.Builder()
     *  .propertyResolverStrategy(new BuilderPropertyResolver())
     *  .build();
     *  factory.registerObjectFactory((Object o, MappingContext mappingContext) 
     *  -&gt; new MyBuilder(), TypeFactory.valueOf(MyBuilder.class));
     *  </pre>
     */
    public class BuilderPropertyResolver extends IntrospectorPropertyResolver {
    
     private static final Logger logger = LoggerFactory.getLogger(BuilderPropertyResolver.class);
     
     @Override
     protected void collectProperties(Class<?> type, Type<?> referenceType, Map<String, Property> properties) {
      super.collectProperties(type, referenceType, properties);
      if (StringUtils.endsWith(type.getName(), "Builder")) {
       Set<String> fieldNames = Arrays.stream(type.getDeclaredFields()).map(Field::getName).collect(Collectors.toSet());
       Arrays.stream(type.getDeclaredMethods())
         .filter(method -> fieldNames.contains(method.getName()))
         .filter(method -> isWriteMethodInBuilder(type, method))
         .forEach(method -> {
          Property.Builder builder = new Property.Builder();
          builder.expression(method.getName());
          builder.name(method.getName());
          builder.setter(method.getName() + "(%s)");
          Class<?> fieldType = method.getParameterTypes()[0];
          builder.type(this.resolvePropertyType(null, fieldType, type, referenceType));
          Property property = builder.build(this);
          properties.put(method.getName(), property);
         });
      }
     }
    
     private boolean isWriteMethodInBuilder(Class<?> type, Method method) {
      try {
       Class<?> returnType = method.getReturnType();
       Class<?> fieldType = type.getDeclaredField(method.getName()).getType();
       boolean onlyOneParameter = method.getParameterTypes().length == 1;
       boolean methodReturnBuilderType = returnType.equals(type);
       Class<?> methodParamType = method.getParameterTypes()[0];
       boolean isFieldSetter = fieldType.equals(methodParamType);
       return methodReturnBuilderType && onlyOneParameter && isFieldSetter;
      } catch (NoSuchFieldException e) {
       e.printStackTrace();
       return false;
      }
     }
    
    }
    
  3. Leverage this object
    public class Mapper {
     
     public static void main(String[] params) {
      MapperFactory factory = new DefaultMapperFactory.Builder()
        .propertyResolverStrategy(new BuilderPropertyResolver())
        .build();
      factory.registerObjectFactory((Object o, MappingContext mappingContext) 
        -> Person.builder(), TypeFactory.valueOf(Person.PersonBuilder.class));
      
      
      PersonBO bo = new PersonBO();
      bo.setName("TEST");
      factory.classMap(PersonBO.class, Person.class);
      System.out.println(factory.getMapperFacade().map(bo, Person.PersonBuilder.class).build());
      
      Person.PersonBuilder builder = Person.builder().list(Arrays.asList("A"));
      factory.getMapperFacade().map(bo, builder);
      System.out.println(builder);
     }
    
     @Data
     public static class PersonBO {
      private String name;
      private List<String> list;
     }
     
     @ToString
     @Builder
     public static class Person {
      private String name;
      private List<String> list;
     }
     
    }
    

Lessons Learned While Using Claude Code Skills

Recently, I started experimenting with Claude Code Skills . I first saw this YouTube video: https://www.youtube.com/watch?v=CEvIs9y1uog ...