From d73e3c6d9d5b74940b54dfb7beb8cf2ecc78405d Mon Sep 17 00:00:00 2001 From: Sjaak Derksen Date: Thu, 31 Jan 2019 20:15:03 +0100 Subject: [PATCH] adding deep cloner (#49) --- README.md | 1 + mapstruct-clone/pom.xml | 78 ++++++++++++++++++ .../mapstruct/example/dto/CustomerDto.java | 66 +++++++++++++++ .../mapstruct/example/dto/OrderItemDto.java | 26 ++++++ .../example/dto/OrderItemKeyDto.java | 17 ++++ .../org/mapstruct/example/mapper/Cloner.java | 51 ++++++++++++ .../org/mapstruct/example/ClonerTest.java | 80 +++++++++++++++++++ pom.xml | 1 + 8 files changed, 320 insertions(+) create mode 100644 mapstruct-clone/pom.xml create mode 100644 mapstruct-clone/src/main/java/org/mapstruct/example/dto/CustomerDto.java create mode 100644 mapstruct-clone/src/main/java/org/mapstruct/example/dto/OrderItemDto.java create mode 100644 mapstruct-clone/src/main/java/org/mapstruct/example/dto/OrderItemKeyDto.java create mode 100644 mapstruct-clone/src/main/java/org/mapstruct/example/mapper/Cloner.java create mode 100644 mapstruct-clone/src/test/java/org/mapstruct/example/ClonerTest.java diff --git a/README.md b/README.md index 5b123f5..d1c0cd9 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ Currently, the following examples exist: * _mapstruct-jpa-parent-child_: Example on how to use @Context in relation to parent / child relations in JPA) * _mapstruct-suppress-unmapped_: Shows how mapping to target properties can be ignored without warning by default in a mixed scenario. However bean property mappings that have the same name will still be applied. * _mapstruct-lookup-entity-with-composed-key_: Shows how an object with composite key can be read from the database in a mapping method. +* _mapstruct-clone_: Shows how an object can be deeply cloned by defining all mapping methods. * _mapstruct-metadata-annotations_: Demonstrates how to read annotations and use them as mapping instruction. ## License diff --git a/mapstruct-clone/pom.xml b/mapstruct-clone/pom.xml new file mode 100644 index 0000000..8ca60b4 --- /dev/null +++ b/mapstruct-clone/pom.xml @@ -0,0 +1,78 @@ + + + + 4.0.0 + + org.mapstruct.examples.clone + mapstruct-deep-clone + 1.0.0 + + + 1.3.0.Beta2 + + + + + org.mapstruct + mapstruct-jdk8 + ${org.mapstruct.version} + + + + junit + junit + 4.12 + test + + + + org.assertj + assertj-core + 3.6.2 + test + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.6.2 + + 1.8 + 1.8 + + + org.mapstruct + mapstruct-processor + ${org.mapstruct.version} + + + + + + + + diff --git a/mapstruct-clone/src/main/java/org/mapstruct/example/dto/CustomerDto.java b/mapstruct-clone/src/main/java/org/mapstruct/example/dto/CustomerDto.java new file mode 100644 index 0000000..bf650bc --- /dev/null +++ b/mapstruct-clone/src/main/java/org/mapstruct/example/dto/CustomerDto.java @@ -0,0 +1,66 @@ +/** + * Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/) + * and/or other contributors as indicated by the @authors tag. See the + * copyright.txt file in the distribution for a full listing of all + * contributors. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.mapstruct.example.dto; + +import java.util.List; +import java.util.Map; + +/** + * @author Sjaak Derksen + */ +public class CustomerDto { + + private Long id; + private String customerName; + private List orders; + private Map stock; + + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getCustomerName() { + return customerName; + } + + public void setCustomerName(String customerName) { + this.customerName = customerName; + } + + public List getOrders() { + return orders; + } + + public void setOrders(List orders) { + this.orders = orders; + } + + public Map getStock() { + return stock; + } + + public void setStock(Map stock) { + this.stock = stock; + } +} diff --git a/mapstruct-clone/src/main/java/org/mapstruct/example/dto/OrderItemDto.java b/mapstruct-clone/src/main/java/org/mapstruct/example/dto/OrderItemDto.java new file mode 100644 index 0000000..758c79f --- /dev/null +++ b/mapstruct-clone/src/main/java/org/mapstruct/example/dto/OrderItemDto.java @@ -0,0 +1,26 @@ +package org.mapstruct.example.dto; + +/** + * @author Sjaak Derksen + */ +public class OrderItemDto { + + private String name; + private Long quantity; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Long getQuantity() { + return quantity; + } + + public void setQuantity(Long quantity) { + this.quantity = quantity; + } +} diff --git a/mapstruct-clone/src/main/java/org/mapstruct/example/dto/OrderItemKeyDto.java b/mapstruct-clone/src/main/java/org/mapstruct/example/dto/OrderItemKeyDto.java new file mode 100644 index 0000000..1fd800a --- /dev/null +++ b/mapstruct-clone/src/main/java/org/mapstruct/example/dto/OrderItemKeyDto.java @@ -0,0 +1,17 @@ +package org.mapstruct.example.dto; + +/** + * @author Sjaak Derksen + */ +public class OrderItemKeyDto { + + private long stockNumber; + + public long getStockNumber() { + return stockNumber; + } + + public void setStockNumber(long stockNumber) { + this.stockNumber = stockNumber; + } +} diff --git a/mapstruct-clone/src/main/java/org/mapstruct/example/mapper/Cloner.java b/mapstruct-clone/src/main/java/org/mapstruct/example/mapper/Cloner.java new file mode 100644 index 0000000..ed41778 --- /dev/null +++ b/mapstruct-clone/src/main/java/org/mapstruct/example/mapper/Cloner.java @@ -0,0 +1,51 @@ +/** + * Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/) + * and/or other contributors as indicated by the @authors tag. See the + * copyright.txt file in the distribution for a full listing of all + * contributors. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.mapstruct.example.mapper; + +import java.util.List; +import java.util.Map; + +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.example.dto.CustomerDto; +import org.mapstruct.example.dto.OrderItemDto; +import org.mapstruct.example.dto.OrderItemKeyDto; +import org.mapstruct.factory.Mappers; + +/** + * @author Sjaak Derksen + * + * By defining all methods, we force MapStruct to generate new objects for each mapper in stead of + * taking shortcuts by mapping an object directly. + */ +@Mapper +public interface Cloner { + + Cloner MAPPER = Mappers.getMapper( Cloner.class ); + + CustomerDto clone(CustomerDto customerDto); + + List clone(List orders); + + OrderItemDto clone(OrderItemDto order); + + Map clone(Map stock); + + OrderItemKeyDto clone( OrderItemKeyDto orderItemKeyDto ); +} diff --git a/mapstruct-clone/src/test/java/org/mapstruct/example/ClonerTest.java b/mapstruct-clone/src/test/java/org/mapstruct/example/ClonerTest.java new file mode 100644 index 0000000..136929f --- /dev/null +++ b/mapstruct-clone/src/test/java/org/mapstruct/example/ClonerTest.java @@ -0,0 +1,80 @@ +/** + * Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/) + * and/or other contributors as indicated by the @authors tag. See the + * copyright.txt file in the distribution for a full listing of all + * contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.mapstruct.example; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.junit.Test; +import org.mapstruct.example.dto.CustomerDto; +import org.mapstruct.example.dto.OrderItemDto; +import org.mapstruct.example.dto.OrderItemKeyDto; +import org.mapstruct.example.mapper.Cloner; + +/** + * + * @author Sjaak Derksen + */ +public class ClonerTest { + + @Test + public void testMapDtoToEntity() { + + CustomerDto customerDto = new CustomerDto(); + customerDto.setId( 10L ); + customerDto.setCustomerName("Jaques" ); + OrderItemDto order1 = new OrderItemDto(); + order1.setName ("Table" ); + order1.setQuantity( 2L ); + customerDto.setOrders( new ArrayList<>( Collections.singleton( order1 ) ) ); + OrderItemKeyDto key = new OrderItemKeyDto(); + key.setStockNumber( 5 ); + Map stock = new HashMap( ); + stock.put( key, order1 ); + customerDto.setStock( stock ); + + CustomerDto customer = Cloner.MAPPER.clone( customerDto ); + + // check if cloned + assertThat( customer.getId() ).isEqualTo( 10 ); + assertThat( customer.getCustomerName() ).isEqualTo( "Jaques" ); + assertThat( customer.getOrders() ) + .extracting( "name", "quantity" ) + .containsExactly( tuple( "Table", 2L ) ); + assertThat( customer.getStock() ).isNotNull(); + assertThat( customer.getStock() ).hasSize( 1 ); + + Map.Entry entry = customer.getStock().entrySet().iterator().next(); + assertThat( entry.getKey().getStockNumber() ).isEqualTo( 5 ); + assertThat( entry.getValue().getName() ).isEqualTo( "Table" ); + assertThat( entry.getValue().getQuantity() ).isEqualTo( 2L ); + + // check mapper really created new objects + assertThat( customer ).isNotSameAs( customerDto ); + assertThat( customer.getOrders().get( 0 ) ).isNotEqualTo( order1 ); + assertThat( entry.getKey() ).isNotEqualTo( key ); + assertThat( entry.getValue() ).isNotEqualTo( order1 ); + assertThat( entry.getValue() ).isNotEqualTo( customer.getOrders().get( 0 ) ); + } +} diff --git a/pom.xml b/pom.xml index 88d7390..16227f9 100644 --- a/pom.xml +++ b/pom.xml @@ -43,5 +43,6 @@ mapstruct-lookup-entity-with-id mapstruct-metadata-with-annotations mapstruct-suppress-unmapped + mapstruct-clone