Dozer轻松实现对象间属性复制

对外接口,一般都使用特定的DTO对象,而不会使用领域模型,以避免两者的变动互相影响。其他框架适配等情形,也可能需要DTO对象。

 

如果手工一个个属性的在两个对象间复制有点累人,如果对象里面还有对象,就更累了。所以希望有一个工具类,可以一行代码就把对象A中的属性全部Copy到对象B中。 普通的反射框架不适合做这个事情,看看Dozer 所支持的特性就知道了:

 

支持两个对象间的同一属性的类型是异构的对象,比如CarDTOengine属性是EngineDTO Carengine属性是Engine

 

支持String <-> 基础类型的转换,比如CarDTOprice属性是String, Carprice属性是Double.

 

支持Collection类型间的转换,比如String[] <-> List

 

支持双向依赖,比如Product有个属性是List parts, 而每个Part也有一个Product属性,此时ProductPart双向依赖了。

 

属性名实在不一致时,可以用@Mapping定义,而且只在其中一边定义就可以了。

 

Dozer 也有个缺点,必须基于getter/setter,不能直接访问public field,卡住了我让Entity/DTO都取消掉getter/setter的计划。

 

Dozer已比较成熟,所以更新很慢。另一个类似但更新较勤快的项目叫Orika

 

 

In SpringSide

core中封装了一个BeanMapper,实现如下功能:

 

持有Dozer单例避免重复创建DozerMapper消耗资源.

 

自动泛型类型转换.

 

批量转换Collection中的所有对象.

 

区分创建新的B对象与将对象A值赋值到已存在的B对象的函数.

 

showcase中有一个DozerDemo,详细演示了Dozer上述的各种特性。

 

 

 

所需jar

 

 1<dependency>

         <groupId>net.sf.dozer</groupId>

         <artifactId>dozer</artifactId>

         <version>5.3.1</version>

      </dependency>

 2<dependency>

         <groupId>org.springside</groupId>

         <artifactId>springside-core</artifactId>

      </dependency>

  1  package org.springside.examples.showcase.utilities.dozer;
  2 
  3  import  static org.junit.Assert.*;
  4 
  5  import java.util.List;
  6 
  7  import org.dozer.Mapping;
  8  import org.junit.Test;
  9  import org.springside.modules.mapper.BeanMapper;
 10 
 11  /**
 12   * 演示Dozer如何只要属性名相同,可以罔顾属性类型是基础类型<->String的差别,Array转为List,甚至完全另一种DTO的差别。
 13   * 并且能完美解决循环依赖问题。
 14   * 使用@Mapping能解决属性名不匹配的情况.
 15    */
 16  public  class DozerDemo {
 17 
 18      /**
 19       * 从一个ProductDTO实例,创建出一个新的Product实例。
 20        */
 21     @Test
 22      public  void map() {
 23         ProductDTO productDTO =  new ProductDTO();
 24         productDTO.setName("car");
 25         productDTO.setPrice("200");
 26 
 27         PartDTO partDTO =  new PartDTO();
 28         partDTO.setName("door");
 29         partDTO.setProduct(productDTO);
 30 
 31         productDTO.setParts( new PartDTO[] { partDTO });
 32 
 33          // ProductDTO->Product
 34          Product product = BeanMapper.map(productDTO, Product. class);
 35 
 36         assertEquals("car", product.getProductName());
 37          // 原来的字符串被Map成Double。
 38          assertEquals( new Double(200), product.getPrice());
 39          // 原来的PartDTO同样被Map成Part ,Array被Map成List
 40          assertEquals("door", product.getParts().get(0).getName());
 41          // Part中循环依赖的Product同样被赋值。
 42          assertEquals("car", product.getParts().get(0).getProduct().getProductName());
 43 
 44          // 再反向从Product->ProductDTO
 45          ProductDTO productDTO2 = BeanMapper.map(product, ProductDTO. class);
 46         assertEquals("car", productDTO2.getName());
 47         assertEquals("200.0", productDTO2.getPrice());
 48         assertEquals("door", productDTO2.getParts()[0].getName());
 49     }
 50 
 51      /**
 52       * 演示将一个ProductDTO实例的内容,Copy到另一个已存在的Product实例.
 53        */
 54     @Test
 55      public  void copy() {
 56         ProductDTO productDTO =  new ProductDTO();
 57         productDTO.setName("car");
 58         productDTO.setPrice("200");
 59 
 60         PartDTO partDTO =  new PartDTO();
 61         partDTO.setName("door");
 62         partDTO.setProduct(productDTO);
 63 
 64         productDTO.setParts( new PartDTO[] { partDTO });
 65 
 66          // 已存在的Product实例
 67          Product product =  new Product();
 68         product.setProductName("horse");
 69         product.setWeight( new Double(20));
 70 
 71         BeanMapper.copy(productDTO, product);
 72 
 73          // 原来的horse,被替换成car
 74          assertEquals("car", product.getProductName());
 75          // 原来的20的属性被覆盖成200,同样被从字符串被专为Double。
 76          assertEquals( new Double(200), product.getPrice());
 77          // DTO中没有的属性值,在Product中被保留
 78          assertEquals( new Double(20), product.getWeight());
 79          // Part中循环依赖的Product同样被赋值。
 80          assertEquals("car", product.getParts().get(0).getProduct().getProductName());
 81     }
 82 
 83      public  static  class Product {
 84          private String productName;
 85          private Double price;
 86          private List<Part> parts;
 87          // DTO中没有的属性
 88           private Double weight;
 89 
 90          public String getProductName() {
 91              return productName;
 92         }
 93 
 94          public  void setProductName(String productName) {
 95              this.productName = productName;
 96         }
 97 
 98          public Double getPrice() {
 99              return price;
100         }
101 
102          public  void setPrice(Double price) {
103              this.price = price;
104         }
105 
106          public List<Part> getParts() {
107              return parts;
108         }
109 
110          public  void setParts(List<Part> parts) {
111              this.parts = parts;
112         }
113 
114          public Double getWeight() {
115              return weight;
116         }
117 
118          public  void setWeight(Double weight) {
119              this.weight = weight;
120         }
121 
122     }
123 
124      public  static  class Part {
125          // 反向依赖Product
126           private Product product;
127 
128          private String name;
129 
130          public String getName() {
131              return name;
132         }
133 
134          public  void setName(String name) {
135              this.name = name;
136         }
137 
138          public Product getProduct() {
139              return product;
140         }
141 
142          public  void setProduct(Product product) {
143              this.product = product;
144         }
145     }
146 
147      public  static  class ProductDTO {
148          // 定义到Product中的productName,只要在一边定义,双向转换都可以使用.
149          @Mapping("productName")
150          private String name;
151          // 类型为String 而非 Double
152           private String price;
153          // 类型为Array而非List, PartDTO而非Part
154           private PartDTO[] parts;
155 
156          public String getName() {
157              return name;
158         }
159 
160          public  void setName(String name) {
161              this.name = name;
162         }
163 
164          public String getPrice() {
165              return price;
166         }
167 
168          public  void setPrice(String price) {
169              this.price = price;
170         }
171 
172          public PartDTO[] getParts() {
173              return parts;
174         }
175 
176          public  void setParts(PartDTO[] parts) {
177              this.parts = parts;
178         }
179     }
180 
181      public  static  class PartDTO {
182          // 反向依赖ProductDTO
183           private ProductDTO product;
184 
185          private String name;
186 
187          public String getName() {
188              return name;
189         }
190 
191          public  void setName(String name) {
192              this.name = name;
193         }
194 
195          public ProductDTO getProduct() {
196              return product;
197         }
198 
199          public  void setProduct(ProductDTO product) {
200              this.product = product;
201         }
202 
203     }
204 
205 }
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值