250x250
Notice
Recent Posts
Recent Comments
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- Docker
- Source
- oracle
- PostgreSQL
- JavaScript
- Tomcat
- Thymeleaf
- Exception
- MySQL
- ubuntu
- git
- IntelliJ
- Python
- spring
- maven
- 문서
- Core Java
- error
- SpringBoot
- 오픈소스
- Eclipse
- AJAX
- myBatis
- jpa
- JDBC
- Spring Boot
- 설정
- Open Source
- MSSQL
- STS
Archives
- Today
- Total
헤르메스 LIFE
[ModelMapper] Object to Object 매핑 본문
728x90
Object 변환에는 여러 가지 툴이 있습니다.
ObjectMapper 나 BeanUtil 등입니다.
https://hermeslog.tistory.com/483
ModelMapper 라는 Utility를 테스트 했습니다.
http://modelmapper.org/getting-started/
package test.mapper;
import org.modelmapper.ModelMapper;
import org.modelmapper.convention.MatchingStrategies;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
public class ModelMapperSample {
public static void main(String... args) {
ModelMapper modelMapper = new ModelMapper();
Order order = new Order(
new Customer(new Name("홍", "길동")),
new Address("용산구", "서울"));
OrderDTO orderDTO = modelMapper.map(order, OrderDTO.class);
// [실행 결과]
System.out.println("orderDTO :: " + orderDTO);
// 정확히 일치하는 필드만 매핑
modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
orderDTO = modelMapper.map(order, OrderDTO.class);
// [실행 결과]
System.out.println("STRICT orderDTO :: " + orderDTO);
// 지능적인 매핑
modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STANDARD);
orderDTO = modelMapper.map(order, OrderDTO.class);
// [실행 결과]
System.out.println("STANDARD orderDTO :: " + orderDTO);
// 느슨한 매핑
modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.LOOSE);
orderDTO = modelMapper.map(order, OrderDTO.class);
// [실행 결과]
System.out.println("LOOSE orderDTO :: " + orderDTO);
}
}
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ToString
class OrderDTO {
private String customerFirstName;
private String customerLastName;
private String billingStreet;
private String billingCity;
}
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ToString
class Order {
private Customer customer;
private Address billingAddress;
}
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ToString
class Customer {
private Name name;
}
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ToString
class Name {
private String firstName;
private String lastName;
}
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ToString
class Address {
private String street;
private String city;
}
[실행결과]
orderDTO :: OrderDTO(customerFirstName=홍, customerLastName=길동, billingStreet=용산구, billingCity=서울)
STRICT orderDTO :: OrderDTO(customerFirstName=홍, customerLastName=길동, billingStreet=용산구, billingCity=서울)
STANDARD orderDTO :: OrderDTO(customerFirstName=홍, customerLastName=길동, billingStreet=용산구, billingCity=서울)
LOOSE orderDTO :: OrderDTO(customerFirstName=홍, customerLastName=길동, billingStreet=용산구, billingCity=서울)
https://modelmapper.org/user-manual/configuration/
사용 가능한 규약
ModelMapper에는 다양한 속성 일치 요구사항을 처리하기 위해 사전 정의된 규약이 포함되어 있습니다.
규약 설명
규약 | 설명 |
NamingConventions.NONE | 모든 속성 이름에 적용되는 명명 규칙 없음 |
NamingConventions.JAVABEANS_ACCESSOR | JavaBeans 규칙에 따라 적합한 접근자를 찾습니다. |
NamingConventions.JAVABEANS_MUTATOR | JavaBeans 규칙에 따라 적합한 mutators(돌연변이) 를 찾습니다. |
NameTransformers.JAVABEANS_ACCESSOR | JavaBeans 규칙에 따라 접근자 이름 변환 |
NameTransformers.JAVABEANS_MUTATOR | JavaBeans 규칙에 따라 변환자 이름 변환 |
NameTokenizers.CAMEL_CASE | Camel Case 규칙에 따라 속성 토큰(Token) 및 클래스 이름 |
NameTokenizers.UNDERSCORE | underscores에 의한 속성 토큰(Token)과 클래스 이름 |
MatchingStrategies.STANDARD | 소스와 대상 속성을 지능적으로 일치 시킴 |
MatchingStrategies.LOOSE | 소스와 대상 속성을 느슨하게 일치 시킴 |
MatchingStrategies.STRICT | 소스와 대상 속성을 엄격하게 일치 시킴 |
매핑전략
- MatchingStrategies.STRICT : 정확히 일치하는 필드만 매핑
- MatchingStrategies.STANDARD : 지능적인 매핑
- MatchingStrategies.LOOSE : 느슨한 매핑
설정 예제
- Protected 필드 매핑
modelMapper.getConfiguration().setMethodAccessLevel(AccessLevel.PROTECTED); - Private 필드 매핑
modelMapper.getConfiguration().setFieldMatchingEnabled(true).setFieldAccessLevel(AccessLevel.PRIVATE);
Token (토큰) 이란?
Camel 표기법 : billingCity -> Billing 과 City 두개의 토큰
Underscore 표기법 : billing_city -> Billing 과 City 두개의 토큰
package test.mapper;
import org.modelmapper.ModelMapper;
import org.modelmapper.convention.MatchingStrategies;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
public class ModelMapperSample {
public static void main(String... args) {
ModelMapper modelMapper = new ModelMapper();
Item itemA = new Item("상품", 10, 1500, true);
Bill bill = modelMapper.map(itemA, Bill.class);
// [실행 결과]
System.out.println("bill :: " + bill);
// 정확히 일치하는 필드만 매핑
modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
bill = modelMapper.map(itemA, Bill.class);
// [실행 결과]
System.out.println("STRICT bill :: " + bill);
Bill2 bill2 = modelMapper.map(itemA, Bill2.class);
// [실행 결과]
System.out.println("STRICT bill2 :: " + bill2);
}
}
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ToString
class Item {
private String name;
private Integer stock;
private Integer price;
private Boolean discountYn;
}
@Getter
@Setter
@ToString
class Bill {
private Integer stock;
private Integer price;
private String name;
private Boolean discountYn;
}
@Getter
@Setter
@ToString
class Bill2 {
private String name1;
private Integer stock1;
private Integer price1;
private Boolean discountYn1;
}
[실행결과]
bill :: Bill(stock=10, price=1500, name=상품, discountYn=true)
STRICT bill :: Bill(stock=10, price=1500, name=상품, discountYn=true)
STRICT bill2 :: Bill2(name1=null, stock1=null, price1=null, discountYn1=null)
MatchingStrategies.STRICT
- 토큰들은 엄격한 순서로 일치해야 한다
- 모든 destination 속성 이름 토큰이 반드시 일치해야 한다
- 모든 source 속성 이름과 모든 토큰이 일치해야 한다
package test.mapper;
import org.modelmapper.ModelMapper;
import org.modelmapper.convention.MatchingStrategies;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
public class ModelMapperSample {
public static void main(String... args) {
ModelMapper modelMapper = new ModelMapper();
Item itemA = new Item("상품", 10, 1500, true);
Bill bill = modelMapper.map(itemA, Bill.class);
// [실행 결과]
System.out.println("bill :: " + bill);
// 정확히 일치하는 필드만 매핑
modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STANDARD);
bill = modelMapper.map(itemA, Bill.class);
// [실행 결과]
System.out.println("STANDARD bill :: " + bill);
Bill2 bill2 = modelMapper.map(itemA, Bill2.class);
// [실행 결과]
System.out.println("STANDARD bill2 :: " + bill2);
}
}
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ToString
class Item {
private String name;
private Integer stock;
private Integer price;
private Boolean discountYn;
}
@Getter
@Setter
@ToString
class Bill {
private Integer stock;
private Integer price;
private String name;
private Boolean discountYn;
}
@Getter
@Setter
@ToString
class Bill2 {
private Float stock;
private Integer price2;
private String name;
private Boolean discountYn;
}
[실행결과]
bill :: Bill(stock=10, price=1500, name=상품, discountYn=true)
STRICT bill :: Bill(stock=10, price=1500, name=상품, discountYn=true)
STRICT bill2 :: Bill2(stock=10.0, price2=null, name=상품, discountYn=true)
MatchingStrategies.STANDARD
- 토큰은 어떤 순서로든 일치될 수 있다.
- 모든 destination 속성 이름 토큰이 일치해야 한다.
- 모든 source 속성 이름은 일치하는 토큰이 하나 이상 있어야 한다.
package test.mapper;
import org.modelmapper.ModelMapper;
import org.modelmapper.convention.MatchingStrategies;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
public class ModelMapperSample {
public static void main(String... args) {
ModelMapper modelMapper = new ModelMapper();
Item itemA = new Item("상품", 10, 1500, true);
Bill bill = modelMapper.map(itemA, Bill.class);
// [실행 결과]
System.out.println("bill :: " + bill);
// 정확히 일치하는 필드만 매핑
modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.LOOSE);
bill = modelMapper.map(itemA, Bill.class);
// [실행 결과]
System.out.println("LOOSE bill :: " + bill);
Bill2 bill2 = modelMapper.map(itemA, Bill2.class);
// [실행 결과]
System.out.println("LOOSE bill2 :: " + bill2);
}
}
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ToString
class Item {
private String name;
private Integer stock;
private Integer price;
private Boolean discountYn;
}
@Getter
@Setter
@ToString
class Bill {
private Integer stock;
private Integer price;
private String name;
private Boolean discountYn;
}
@Getter
@Setter
@ToString
class Bill2 {
private String name;
private String name1;
private Integer stock1;
private Integer price;
private Integer price1;
private Boolean adiscountYn;
private Boolean discountYn;
}
[실행결과]
bill :: Bill(stock=10, price=1500, name=상품, discountYn=true)
STRICT bill :: Bill(stock=10, price=1500, name=상품, discountYn=true)
STRICT bill2 :: Bill2(name=상품, name1=상품, stock1=10, price=1500, price1=1500, adiscountYn=true, discountYn=true)
MatchingStrategies.LOOSE
- 토큰은 어떤 순서로든 일치될 수 있다
- 마지막 destination 속성 이름에는 모든 토큰이 일치해야 한다
- 마지막 source 속성 이름은 일치하는 토큰이 하나 이상 있어야 한다
그러면, 이런 경우는 어떻게 될까요..?
package test.mapper;
import org.modelmapper.ModelMapper;
import org.modelmapper.convention.MatchingStrategies;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
public class ModelMapperSample {
public static void main(String... args) {
ModelMapper modelMapper = new ModelMapper();
Item itemA = new Item("상품", 10, 1500, 3000, true, false);
Bill bill = modelMapper.map(itemA, Bill.class);
// [실행 결과]
System.out.println("bill :: " + bill);
// 정확히 일치하는 필드만 매핑
modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.LOOSE);
bill = modelMapper.map(itemA, Bill.class);
// [실행 결과]
System.out.println("LOOSE bill :: " + bill);
Bill2 bill2 = modelMapper.map(itemA, Bill2.class);
// [실행 결과]
System.out.println("LOOSE bill2 :: " + bill2);
}
}
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ToString
class Item {
private String name;
private Integer stock;
private Integer priceA;
private Integer priceB;
private Boolean discountYnA;
private Boolean discountYnB;
}
@Getter
@Setter
@ToString
class Bill {
private Integer stock;
private Integer priceB;
private Integer priceA;
private String name;
private Boolean discountYnB;
private Boolean discountYnA;
}
@Getter
@Setter
@ToString
class Bill2 {
private String name;
private String name1;
private Integer stock1;
private Integer price;
private Integer price1;
private Boolean adiscountYn;
private Boolean discountYn;
}
[실행결과]
bill :: Bill(stock=10, priceB=3000, priceA=1500, name=상품, discountYnB=false, discountYnA=true)
STRICT bill :: Bill(stock=10, priceB=3000, priceA=1500, name=상품, discountYnB=false, discountYnA=true)
Exception in thread "main" org.modelmapper.ConfigurationException: ModelMapper configuration errors:
1) The destination property test.mapper.Bill2.setPrice() matches multiple source property hierarchies:
test.mapper.Item.getPriceA()
test.mapper.Item.getPriceB()
2) The destination property test.mapper.Bill2.setPrice1() matches multiple source property hierarchies:
test.mapper.Item.getPriceA()
test.mapper.Item.getPriceB()
3) The destination property test.mapper.Bill2.setDiscountYn() matches multiple source property hierarchies:
test.mapper.Item.getDiscountYnB()
test.mapper.Item.getDiscountYnA()
4) The destination property test.mapper.Bill2.setAdiscountYn() matches multiple source property hierarchies:
test.mapper.Item.getDiscountYnB()
test.mapper.Item.getDiscountYnA()
4 errors
at org.modelmapper.internal.Errors.throwConfigurationExceptionIfErrorsExist(Errors.java:241)
at org.modelmapper.internal.ImplicitMappingBuilder.matchDestination(ImplicitMappingBuilder.java:159)
at org.modelmapper.internal.ImplicitMappingBuilder.build(ImplicitMappingBuilder.java:90)
at org.modelmapper.internal.ImplicitMappingBuilder.build(ImplicitMappingBuilder.java:75)
at org.modelmapper.internal.TypeMapStore.getOrCreate(TypeMapStore.java:131)
at org.modelmapper.internal.TypeMapStore.getOrCreate(TypeMapStore.java:106)
at org.modelmapper.internal.MappingEngineImpl.map(MappingEngineImpl.java:112)
at org.modelmapper.internal.MappingEngineImpl.map(MappingEngineImpl.java:71)
at org.modelmapper.ModelMapper.mapInternal(ModelMapper.java:573)
at org.modelmapper.ModelMapper.map(ModelMapper.java:406)
at test.mapper.ModelMapperSample.main(ModelMapperSample.java:30)
오류 나네요. ㅎㅎ
Mismatching 조작하기
package test.mapper;
import org.modelmapper.ModelMapper;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
public class ModelMapperSample {
public static void main(String... args) {
ModelMapper modelMapper = new ModelMapper();
Item item = new Item("상품", 10, 1500, true);
Bill bill = modelMapper.map(item, Bill.class);
// [실행 결과]
System.out.println("bill :: " + bill);
modelMapper.typeMap(Item.class, Bill.class).addMappings(mapper -> {
mapper.map(Item::getStock, Bill::setQty);
mapper.map(Item::getPrice, Bill::setSinglePrice);
});
Bill bill2 = modelMapper.map(item, Bill.class);
// [실행 결과]
System.out.println("bill2 :: " + bill2);
}
}
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ToString
class Item {
private String name;
private Integer stock;
private Integer price;
private Boolean sale;
}
@Getter
@Setter
@ToString
class Bill {
private String itemName;
private Integer qty;
private Integer singlePrice;
private Double discount;
}
[실행결과]
bill :: Bill(itemName=상품, qty=null, singlePrice=null, discount=null)
bill2 :: Bill(itemName=상품, qty=10, singlePrice=1500, discount=null)
Convert 타입 변환
package test.mapper;
import org.modelmapper.Converter;
import org.modelmapper.ModelMapper;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
public class ModelMapperSample {
public static void main(String... args) {
ModelMapper modelMapper = new ModelMapper();
Item item = new Item("상품", 10, 1500, true);
Bill bill = modelMapper.map(item, Bill.class);
// [실행 결과]
System.out.println("bill :: " + bill);
modelMapper.typeMap(Item.class, Bill.class).addMappings(mapper -> {
mapper.map(Item::getStock, Bill::setQty);
mapper.map(Item::getPrice, Bill::setSinglePrice);
mapper.using((Converter<Boolean, Double>) context -> context.getSource() ? 20.0 : 0.0)
.map(Item::getIsSale, Bill::setDiscount);
});
Bill bill2 = modelMapper.map(item, Bill.class);
// [실행 결과]
System.out.println("bill2 :: " + bill2);
}
}
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ToString
class Item {
private String name;
private Integer stock;
private Integer price;
private Boolean isSale;
}
@Getter
@Setter
@ToString
class Bill {
private String itemName;
private Integer qty;
private Integer singlePrice;
private Double discount;
}
[실행결과]
bill :: Bill(itemName=상품, qty=null, singlePrice=null, discount=null)
bill2 :: Bill(itemName=상품, qty=10, singlePrice=1500, discount=20.0)
전달하고 싶지 않은 필드 :: Skip
package test.mapper;
import org.modelmapper.Converter;
import org.modelmapper.ModelMapper;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
public class ModelMapperSample {
public static void main(String... args) {
ModelMapper modelMapper = new ModelMapper();
Item item = new Item("상품", 10, 1500, true);
Bill bill = modelMapper.map(item, Bill.class);
// [실행 결과]
System.out.println("bill :: " + bill);
modelMapper.typeMap(Item.class, Bill.class).addMappings(mapper -> {
mapper.map(Item::getStock, Bill::setQty);
mapper.map(Item::getPrice, Bill::setSinglePrice);
mapper.using((Converter<Boolean, Double>) context -> context.getSource() ? 20.0 : 0.0)
.map(Item::getIsSale, Bill::setDiscount);
mapper.skip(Bill::setItemName); // skip 추가
});
Bill bill2 = modelMapper.map(item, Bill.class);
// [실행 결과]
System.out.println("bill2 :: " + bill2);
}
}
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ToString
class Item {
private String name;
private Integer stock;
private Integer price;
private Boolean isSale;
}
@Getter
@Setter
@ToString
class Bill {
private String itemName;
private Integer qty;
private Integer singlePrice;
private Double discount;
}
[실행결과]
bill :: Bill(itemName=상품, qty=null, singlePrice=null, discount=null)
bill2 :: Bill(itemName=null, qty=10, singlePrice=1500, discount=20.0)
Camel Case 에서 Underscore 로 변환
package test.mapper;
import org.modelmapper.ModelMapper;
import org.modelmapper.convention.NameTokenizers;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
public class ModelMapperSample {
public static void main(String... args) {
ModelMapper modelMapper = new ModelMapper();
modelMapper.getConfiguration()
.setSourceNameTokenizer(NameTokenizers.CAMEL_CASE)
.setDestinationNameTokenizer(NameTokenizers.UNDERSCORE);
Item item = new Item("상품", 10, 1500, 2.0);
Bill bill = modelMapper.map(item, Bill.class);
// [실행 결과]
System.out.println("bill :: " + bill);
}
}
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ToString
class Item {
private String itemName;
private Integer itemQty;
private Integer singlePrice;
private Double itemDiscount;
}
@Getter
@Setter
@ToString
class Bill {
private String item_name;
private Integer item_qty;
private Integer single_price;
private Double item_discount;
}
https://hermeslog.tistory.com/483
728x90
'Core Java' 카테고리의 다른 글
[SDKMAN] JDK 버전을 관리하는 Tool 이라고 합니다. (0) | 2024.02.04 |
---|---|
[JAVA] OpenJDK 종류 (0) | 2023.05.11 |
[개발환경] Eclipse 에서 Gradle 기반의 Java 프로젝트 생성하기 (0) | 2023.04.26 |
[Excel] 대용량 Excel 파일 읽기 - 수정 (0) | 2023.02.02 |
[Java] 날짜 계산 Tip (0) | 2022.12.03 |