【问题标题】:How to clone an object and recursively set Id to Null?如何克隆一个对象并递归地将 Id 设置为 Null?
【发布时间】:2019-06-26 10:55:02
【问题描述】:

这是我的用例:

我有 4 个班级 A、B、C、D

  • A 类包含一个对象(B 类)和一个对象列表(C 类)
  • B 类包含一个对象(D 类)

我想克隆 A 类,并递归地将 id 设置为 null。

这里是一个例子:

public class ClassA {

    private Long id;
    private String name;
    private boolean ok;
    private ClassB classB;
    private List<ClassC> classCList;

}

public class ClassB {

    private Long id;
    private String name;
    private ClassD classD;

}

public class ClassC{

    private Long id;
    private String name;

}

public class ClassD{

    private Long id;
    private String name;

}

我开发了两个函数来实现它:
第一种方法:

public ClassA prepareClassA(ClassA detail) {

   Optional.ofNullable(detail).ifPresent( detail -> {
    detail.setId(null);
    Optional.ofNullable(detail).map(ClassA::getClassB)
            .ifPresent(objectB -> objectB.setId(null));

   Optional.ofNullable(detail).map(ClassA::getClassB).map(ClassB::getClassD)
            .ifPresent(objectB -> objectB.setId(null));

   Optional.ofNullable(detail).map(ClassA::getClassCList).
        .ifPresent(items -> items.stream().forEach(item -> {
            item.setId(null);
        }));

   }
}

第二种方法:(包括dozerMapper)

<dependency>
    <groupId>commons-beanutils</groupId>
    <artifactId>commons-beanutils</artifactId>
    <version>1.9.3</version>
</dependency>

<dependency>
    <groupId>net.sf.dozer</groupId>
    <artifactId>dozer</artifactId>
    <version>5.5.1</version>
</dependency>

我使用了 DozerBeanMapper 实现

public ClassA prepareClassA(ClassA detail) {

    ClassA objectA = new ClassA();

    DozerBeanMapper dozerBeanMapper = new DozerBeanMapper();

    BeanMappingBuilder bean = beanMappingBuilder(ClassA.class);

    dozerBeanMapper.addMapping(bean);
    Optional.ofNullable(detail).ifPresent(detail -> dozerBeanMapper.map(detail, objectA));
    return details;
}


public BeanMappingBuilder beanMappingBuilder(Class<?> source) {
    return new BeanMappingBuilder() {
        @Override
        protected void configure() {
            mapping(source, source,
                TypeMappingOptions .wildcard(true)
                //Here i have to do my work ?
                //TypeMappingOptions.mapNull(true)
            );
        }
    };
}

我想要这个结果:

ClassA testA = new ClassA();
//fill all the objects in objectA with id != null

ClassA testA_convert = prepareClassA(testA);

// testA_convert.getId() must be null
// testA_convert.getClassB().getId() must be null
// testA_convert.getClassB().getClassD().getId() must be null
// testA_convert.getClassCList().forEach( element -> element.getId()  must be null

问题:

  • 是否有任何现有的库可以解决我的问题?
  • DozerMapper 能做到吗?
  • 最好的方法是什么?

最好的问候

【问题讨论】:

  • 需求中已经有错误。
  • 你能解释一下吗?你在说什么要求?
  • 克隆这样一个对象图并将 ids 设置为null 的要求。除此之外,如果确实需要这样的操作,请向这些执行操作的类添加一个方法。
  • @Holger 看到:github rep - deep cloning

标签: java spring-boot java-8 java-stream dozer


【解决方案1】:

如果你已经克隆了你的对象并且想在没有 NPE 的情况下将 id 设置为 null,那么你可以为此创建一个帮助接口:

interface Nullify<T> {
    void apply(T obj);

    default <G> Nullify<T> andThen(Function<T, G> function, Nullify<G> nullify) {
        return (T t) -> {
            apply(t);
            G g = function.apply(t);
            if(g != null) {
                nullify.apply(g);
            }
        };
    }
}

及用法

Nullify<ClassB> bNull = b -> b.setId(null);
bNull = bNull.andThen(ClassB::getClassD, d -> d.setId(null));

Nullify<ClassA> aNull = a -> a.setId(null);
aNull.andThen(ClassA::getClassB, bNull)
     .andThen(ClassA::getClassCList, classCList -> classCList.forEach(c -> c.setId(null)))
     .apply(classAObject);

虽然最好将克隆/复制方法配置为忽略 id(例如 MapStruct)

【讨论】:

    【解决方案2】:

    是否有任何现有的库可以解决我的问题?

    是的,MapStruct 可以选择忽略您想要的属性。

    例如定义一个配置接口

    @Mapper(componentModel = "spring")
    public interface DomainDtoMapper {
    
       @Mapping(source = "id", target = "id", ignore = true)
       ClassA map(ClassA cla);
    
       @Mapping(source = "id", target = "id", ignore = true)
       ClassB map(ClassB clb);
    
       //...
    }
    

    然后只需自动连接DomainDtoMapper 并调用该方法,它将检查您的所有映射规则并相应地复制:

    @Autowire DomainDtoMapper mapper;
    
    //...
    
    public ClassA prepareClassA(ClassA detail) {
        return mapper.map(detail);
    }
    

    【讨论】:

      【解决方案3】:

      我认为我找到了一个简单的方法:

      实现 Gson 库:

      <dependency>
         <groupId>com.google.code.gson</groupId>
         <artifactId>gson</artifactId>
        <version>2.2.4</version>
      </dependency>
      

      并添加

      public class ClassA {
      
         private Long id;
      
         @Expose
         private String name;
      
         @Expose
         private boolean ok;
      
         @Expose
         private ClassB classB;
      
         @Expose
         private List<ClassC> classCList;
      
      }
      
      public class ClassB {
      
         private Long id;
      
         @Expose
         private String name;
      
         @Expose
         private ClassD classD;
      
      }
      

      我将@Expose 放到所有字段中!= id

      这里是我的主类中的实现

          import com.google.gson.annotations.Expose;
      
          public ClassA prepareClassA(ClassA detail) {
      
             Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
             ClassA object_A = gson.fromJson(gson.toJson(detail),ClassA.class);
             return  object_A;
      
          }
      

      而且它有效。

      但是,有没有办法只将@Expose 一次放到所有属性(不包括 Id)的类定义中

      例子:

      @Expose( exclude ="id" )
      public class ClassB {
      
         private Long id;
         private String name;
         private ClassD classD;
      
      }
      

      任何帮助?

      【讨论】:

        猜你喜欢
        • 2015-03-28
        • 2015-05-23
        • 2018-12-07
        • 1970-01-01
        • 2011-12-19
        • 1970-01-01
        • 2011-07-26
        • 1970-01-01
        • 2018-08-08
        相关资源
        最近更新 更多