Uso de Mapeo Generado
Esta página cubrirá cómo generar el mapping código y luego, posteriormente, cómo utilizar esos mappers.
Generando el mapping informe
A veces nos enfrentamos a la abrumadora tarea de configurar el mapping entre dos clases similares, grandes y profundas estructuras (como dos versiones diferentes de ISO 20022), por lo que consideramos utilizar implicit mapping pero le gusta la idea de mantener el control. En tal caso, sería bueno saber qué es lo actual mappings sería aplicado. Afortunadamente, el marco incluye un Maven plugin hacer precisamente eso:
-
pom.xml
<plugin>
<groupId>com.iconsolutions.ipf.core.mapper</groupId>
<artifactId>orika-transformation-report-plugin</artifactId>
<version>${icon-mapper.version}</version>
<dependencies>
<!-- Do not forget to list your domain class libraries here -->
<dependency>
<groupId>com.example</groupId>
<artifactId>my-iso20022-domain</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
</plugin>
Este complemento se ejecutará en la línea de comandos:
$ mvn orika-transformation-report:mapping-report \
-Dsource=eba.scti.iso.std.iso._20022.tech.xsd.pacs_002_001_003.FIToFIPaymentStatusReportV03 \
-Ddestination=iso.std.iso._20022.tech.xsd.pacs_002_001_007.FIToFIPaymentStatusReportV07
produciendo una salida compuesta de dos partes. La primera parte explicará qué es un implicit mapping resultaría en:
Mapping report for eba.scti.iso.std.iso._20022.tech.xsd.pacs_002_001_003.FIToFIPaymentStatusReportV03 to iso.std.iso._20022.tech.xsd.pacs_002_001_007.FIToFIPaymentStatusReportV07
Mapped from source
grpHdr (SCTInstGroupHeader5) to grpHdr (GroupHeader53) (1)
msgId (String) to msgId (String) (2)
creDtTm (XMLGregorianCalendar) to creDtTm (XMLGregorianCalendar)
instgAgt (SCTInstBranchAndFinancialInstitutionIdentification3) to instgAgt (BranchAndFinancialInstitutionIdentification5)
finInstnId (SCTInstFinancialInstitutionIdentification5Choice) to finInstnId (FinancialInstitutionIdentification8)
bic not mapped (3)
bicfi in target but not mapped from source
clrSysMmbId in target but not mapped from source (4)
nm in target but not mapped from source
pstlAdr in target but not mapped from source
othr in target but not mapped from source
brnchId in target but not mapped from source
...
-
Coincidencia de diferentes clases por nombre
-
Válido mapping
-
bicdebe ser mapeado abicfibelow -
Campos que no tienen una correspondencia directa en la clase de origen
La segunda parte del informe contendrá un mapping sugerencia, para comenzar con:
source-class: eba.scti.iso.std.iso._20022.tech.xsd.pacs_002_001_003.FIToFIPaymentStatusReportV03
destination-class: iso.std.iso._20022.tech.xsd.pacs_002_001_007.FIToFIPaymentStatusReportV07
implicit-mapping: true
target-class-name: ADD YOUR DESIRED CLASS NAME HERE # (1)
mappings: [
{source: NO SOURCE DETECTED, destination: splmtryDatas, type: List} # (2)
{source: NO SOURCE DETECTED, destination: grpHdr.instgAgt.brnchId, type: BranchData2}
{source: NO SOURCE DETECTED, destination: grpHdr.instgAgt.finInstnId.bicfi, type: String}
{source: NO SOURCE DETECTED, destination: grpHdr.instgAgt.finInstnId.clrSysMmbId, type: ClearingSystemMemberIdentification2}
{source: NO SOURCE DETECTED, destination: grpHdr.instgAgt.finInstnId.nm, type: String}
{source: NO SOURCE DETECTED, destination: grpHdr.instgAgt.finInstnId.pstlAdr, type: PostalAddress6}
{source: NO SOURCE DETECTED, destination: grpHdr.instgAgt.finInstnId.othr, type: GenericFinancialIdentification1}
{source: grpHdr.instgAgt.finInstnId.bic, destination: NO DESTINATION MATCHED, type: String}
// ...
]
-
El nombre de la clase objetivo es necesario. También, un
target-packagedebe ser añadido. -
Esta es la lista de faltantes mappings de acuerdo con Orika. Cambie según sea necesario.
Generando el Java Código
Para utilizar esta biblioteca, añada la siguiente dependencia:
-
pom.xml
<dependency>
<groupId>com.iconsolutions.ipf.core.mapper</groupId>
<artifactId>orika-transformation-impl</artifactId>
</dependency>
Para generar el Java código, el proyecto del usuario deberá utilizar Maven y configure lo siguiente Maven plugin:
-
pom.xml
<plugin>
<groupId>com.iconsolutions.ipf.core.mapper</groupId>
<artifactId>orika-transformation-generation-plugin</artifactId>
<version>${icon-mapper.version}</version>
<executions>
<execution>
<id>make-mappers</id>
<goals>
<goal>generate-code</goal>
</goals>
<configuration>
<!-- location of the HOCON files -->
<mapperPath>${project.basedir}/src/main/mappers</mapperPath>
<!-- where to place generated sources -->
<generatedMappersPath>${project.build.directory}/generated-sources/java</generatedMappersPath>
<!-- where to place generated test-sources -->
<generatedMappersTestPath>${project.build.directory}/generated-test-sources/java </generatedMappersTestPath>
<!-- the Java package they will belong to -->
<targetPackage>com.ipf.example.mapping</targetPackage>
<!-- Name the generated class that will instantiate our universal mapping service -->
<transformationServiceFactoryName>ExampleTransformationFactory</transformationServiceFactoryName>
<!-- You can code your own Orika mappings if you wish, just place them in the target package and list them below -->
<additionalCustomisers>
<additionalCustomiser>MySpecialCustomiser</additionalCustomiser>
<additionalCustomiser>MyPreciousCustomiser</additionalCustomiser>
</additionalCustomisers>
</configuration>
</execution>
</executions>
<dependencies>
<!--
The plugin needs to depend on your domain classes to generate the code.
List all domain model libraries as dependencies here.
-->
<dependency>
<groupId>com.iconsolutions.ipf.core.mapper</groupId>
<artifactId>example-model</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
</plugin>
Cuando se ejecuta la compilación y se lleva a cabo el objetivo del complemento, un número de
Las clases customiser se producen para ambos mappings y enriquecimientos.
Estas clases generadas interactúan con el Orika mapping API implementar
el actual mappings. Se pueden escribir personalizados utilizando el Orika API
y luego se añadió a la <additionalCustomisers /> above.
Saltar Java Prueba Code Generation
Para deshabilitar la prueba code generation conjunto <skipTestsGeneration>true<skipTestsGeneration/>.
Por defecto,skipTestsGeneration es falso, lo que requiere generatedMappersTestPath tener un valor.
Ejemplo de configuración mostrado a continuación:
-
pom.xml
<configuration>
<!-- location of the HOCON files -->
<mapperPath>${project.basedir}/src/main/mappers</mapperPath>
<!-- where to place generated sources -->
<generatedMappersPath>${project.build.directory}/generated-sources/java</generatedMappersPath>
<!-- When true the test code is not generated - default is set to false -->
<skipTestsGeneration>true</skipTestsGeneration>
<!-- Not required when skipping test code generation -->
<!-- <generatedMappersTestPath>${project.build.directory}/generated-test-sources/java </generatedMappersTestPath> -->
<!-- the Java package they will belong to -->
<targetPackage>com.ipf.example.mapping</targetPackage>
<!-- Name the generated class that will instantiate our universal mapping service -->
<transformationServiceFactoryName>ExampleTransformationFactory</transformationServiceFactoryName>
<!-- You can code your own Orika mappings if you wish, just place them in the target package and list them below -->
<additionalCustomisers>
<additionalCustomiser>MySpecialCustomiser</additionalCustomiser>
<additionalCustomiser>MyPreciousCustomiser</additionalCustomiser>
</additionalCustomisers>
</configuration>
Escritura de Personalizadores Adicionales
Desde Orika’s documentation:
Orika uses a declarative Java-based configuration of mappings from one class to another, whereby you define which fields from one type need to be matched up with which fields from another using a fluent-style API.
Esto significa que podemos aprovechar la flexibilidad total de la Orika marco para abordar casos extremos, tales como:
class TestA {
private String foo;
private String bar;
// ...
}
class TestB {
public TestB(String arg1, String arg2) {
// ...
}
}
class MyCustomiser implements OrikaCustomiser {
public void customise(MapperFactory mapperFactory) {
mapperFactory.classMap(TestA.class, TestB.class)
.constructorB("foo","bar") // TestA's foo and bar used to instantiate TestB
//...
.register();
}
}
class TestA {
private Map<String, String> address;
// ...
}
class TestB {
private String firstLine;
private String secondLine;
// ...
}
class MyCustomiser implements OrikaCustomiser {
public void customise(MapperFactory mapperFactory) {
mapperFactory.classMap(TestA.class, TestB.class)
.field("address['line1']", "firstLine")
.field("address['town']", "secondLine")
.register();
}
}
Vea el Orika guía de Configuración de Mapeo Declarativo utilizando el estilo fluido ClassMapBuilder API' para más detalles y casos de uso.
Uso del Código Generado
En el previous section, mostramos cómo configurar el code generation. Parte de la tarea fue elegir un nombre para un Java clase que instanciaría nuestro mapping servicio:
<transformationServiceFactoryName>ExampleTransformationFactory</transformationServiceFactoryName>
Nuestro generado ExampleTransformationFactory la clase se utilizará para crear un TransformationService,
que es efectivamente nuestro _universal_mapping servicio:
.Instantiating a TransformationService
TransformationService transformationService = new ExampleTransformationFactory(
DefaultEnrichmentContext.builder().build()
).transformationService();
Este único, genérico mapper solo cubrirá todo el mapping y configuración de enriquecimiento. Dado que es sin estado, compartir una única instancia o ejecutar múltiples es una cuestión de elección de diseño.
Como podemos ver en el ejemplo anterior, la fábrica de servicios necesita un contexto de enriquecimiento
y el marco nos proporciona una implementación predeterminada llamada DefaultEnrichmentContext, donde:
-
se utiliza un reloj del sistema UTC
-
La configuración HOCON se lee de archivos llamados " ipf.conf "
-
los tokens generados aleatoriamente tienen 32 caracteres de longitud
Estos tres aspectos pueden configurarse como se observa en el ejemplo a continuación:
.Configuring the default enrichment context
DefaultEnrichmentContext.builder()
.withClock(Clock.system(ZoneId.of("GMT+2"))) (1)
.withConfigProvider(new SimpleConfigProvider(
ConfigFactory.load("my-application.conf"))) (2)
.withRandomAlphaNumericGenerator(new RandomAlphanumericGenerator(16)); (3)
| 1 | cambie la zona horaria |
| 2 | proporcione una fuente diferente de configuración |
| 3 | cambie la longitud del token |
Ahora que tenemos un TransformationService, veamos lo que podemos hacer con ello:
.Mapping an object to a different class
TestA testA = new TestA();
testA.setFoo("something magic");
TestB testB = transformationService.map(testA, TestB.class);
assertThat(testB.getBar(), is("something magic"));
.Enriching an object
TestB testB = transformationService.enrich(new TestB());
// testB has been applied any configured enrichment
.Mapping to a target class then enrich
TestA testA = new TestA();
testA.setFoo("something magic");
TestB testB = transformationService.mapThenEnrichWithDefault(testA, TestB.class);
// testB has been both mapped from testA and applied any configured enrichment
Configuración de Mapeo Automático
Orikagenera automáticamente mappings para tipos que carecen de explícito mappers, una característica que puede desactivarse para eliminar la sobrecarga de la resolución de tipos dinámicos.
Está habilitado por defecto, pero puede deshabilitarse a través de la siguiente propiedad:
ipf.orika.transformations.use-auto-mapping = false
| Con auto mapping deshabilitado, explícito mapping se deberán definir configuraciones para todos los tipos que requieran mapping, incluidos los tipos anidados. |