Skip to content

org.immutables example with mapstruct #80

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Currently, the following examples exist:

* _mapstruct-on-ant_: Shows how to use MapStruct in Ant-based projects; to build this example, run `ant build` on the command line
* _mapstruct-on-gradle_: Shows how to use MapStruct in Gradle-based projects; to build the example project, run `./gradlew clean build` on the command line
* _mapstruct-immutables_: Shows how to use MapStruct together with [Immutables](https://immutables.github.io/).
* _mapstruct-lombok_: Shows how to use MapStruct together with Lombok (with both a Maven `pom.xml` and a Gradle `build.gradle`); to build the example project, run either `mvn clean install` or `./gradlew clean build` on the command line
* _mapstruct-iterable-non-iterable_: Shows how by means of a mapper util class conversions can be made from an iterable- to its non-iterable element
* _mapstruct-mapping-from-map_: Shows how by means of a mapper util class and qualifiers extracting value can be carried out on Maps. Watch [mapstruct/mapstruct#1075](https://github.com/mapstruct/mapstruct/issues/1075) for native support.
Expand Down
125 changes: 125 additions & 0 deletions mapstruct-immutables/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--

Copyright 2020 MapStruct developers
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.

-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>org.mapstruct.examples.immutables</groupId>
<artifactId>mapstruct-examples-immutables</artifactId>
<version>1.0-SNAPSHOT</version>

<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<org.mapstruct.version>1.3.1.Final</org.mapstruct.version>
<org.immutables.version>2.8.2</org.immutables.version>
<com.fasterxml.jackson.version>2.10.3</com.fasterxml.jackson.version>
</properties>

<dependencies>

<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
<scope>provided</scope>
</dependency>

<!-- immutables dependencies should not end up on classpath -->
<dependency>
<groupId>org.immutables</groupId>
<artifactId>value</artifactId>
<version>${org.immutables.version}</version>
<scope>provided</scope>
</dependency>

<!-- IntelliJ pre 2018.1.1 requires the mapstruct processor to be present as provided dependency -->
<!-- <dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
<scope>provided</scope>
</dependency>-->


<!-- Demostrates immutables are serializable with jackson -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${com.fasterxml.jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${com.fasterxml.jackson.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.15.0</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<!-- See https://maven.apache.org/plugins/maven-compiler-plugin/compile-mojo.html -->
<!-- Classpath elements to supply as annotation processor path. If specified, the compiler -->
<!-- will detect annotation processors only in those classpath elements. If omitted, the -->
<!-- default classpath is used to detect annotation processors. The detection itself depends -->
<!-- on the configuration of annotationProcessors. -->
<!-- -->
<!-- According to this documentation, the provided dependency processor is not considered! -->
<annotationProcessorPaths>
<path>
<groupId>org.immutables</groupId>
<artifactId>value</artifactId>
<version>${org.immutables.version}</version>
</path>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* Copyright 2020 MapStruct developers
* 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.
* <p>
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.immutables;

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import org.immutables.value.Value;


@Value.Immutable
@JsonDeserialize(builder = ApiPerson.Builder.class)
@ExampleStyle
public interface ApiPerson {
String getId();
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wish we didn't need getters here on the immutables, and we could have stayed with fluent getters, but mapstruct requires POJOs with getters I believe.

The builders however supports the fluent setters.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Immutables is able to support this. This is from Immutables documentation:
"No annotations are required on abstract accessor methods in order for them to become attributes."

https://immutables.github.io/immutable.html#overview

Can we modify the ImmutablesAccessorNamingStrategy to support abstract accessors to qualify as getters.


String getFirstName();

String getFullName();

String getMapperDoingFullName();

// Required for MapStruct to find the builder
static Builder builder() {
return new Builder();
}

// And Builder resides in generated Immutable implementation, which is private protected.
// See https://immutables.github.io/json.html#jackson
class Builder extends ImmutableApiPerson.Builder {
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* Copyright 2020 MapStruct developers
* 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.
* <p>
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.immutables;

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import org.immutables.value.Value;

@Value.Immutable
@JsonDeserialize(builder = DtoPerson.Builder.class)
@ExampleStyle
public interface DtoPerson {
String getUuid();

String getFirstName();

String getLastName();

default String getFullName() {
return getFirstName() + " " + getLastName();
}

// Required for MapStruct to find the builder
static Builder builder() {
return new Builder();
}

// And Builder resides in generated Immutable implementation, which is private protected.
// See https://immutables.github.io/json.html#jackson
class Builder extends ImmutableDtoPerson.Builder {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* Copyright 2020 MapStruct developers
* 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.
* <p>
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.immutables;


import org.immutables.value.Value;

import static org.immutables.value.Value.Style.ImplementationVisibility.PACKAGE;

/**
* Our ExampleStyle to make it easier to reuse annotations on our org.immutables interfaces.
* <p>
* Details: https://immutables.github.io/style.html
* <pre>
* builder = "new" is required for serializing with jackson
* visibility to package is nice to hide implementation details from users using our builders
* overshadowImplementation sets return types on builders to be the interface instead of the generated immutable class. ( https://immutables.github.io/immutable.html#builder )
*/
@Value.Style(builder = "new", visibility = PACKAGE, overshadowImplementation = true)
public @interface ExampleStyle {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* Copyright 2020 MapStruct developers
* 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.
* <p>
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.immutables;

import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;

@Mapper
public interface ResponseMapper {

ResponseMapper INSTANCE = Mappers.getMapper(ResponseMapper.class);

@Mapping(source = "uuid", target = "id")
@Mapping(expression = "java(person.getFirstName() + \" \" + person.getLastName())", target = "mapperDoingFullName")
ApiPerson mapFrom(DtoPerson person);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* Copyright 2020 MapStruct developers
* 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.
* <p>
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.immutables;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Test;

import static org.assertj.core.api.Assertions.assertThat;

public class ResponseMapperTest {

@Test
public void testResponseMapper() {
DtoPerson.Builder barBuilder = new DtoPerson.Builder()
.uuid("foo")
.firstName("John")
.lastName("Snow");


assertThat(ResponseMapper.INSTANCE.mapFrom(barBuilder.build()))
.isEqualTo(new ApiPerson.Builder()
.id("foo")
.firstName("John")
.fullName("John Snow")
.mapperDoingFullName("John Snow")
.build());

}

@Test
public void testResponseMapperWithSerializingPersonToJson() throws JsonProcessingException {
DtoPerson.Builder personFromDatabase = new DtoPerson.Builder()
.uuid("foo")
.firstName("John")
.lastName("Snow");

ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(ResponseMapper.INSTANCE.mapFrom(personFromDatabase.build()));
assertThat(json).isEqualTo("{\"id\":\"foo\",\"firstName\":\"John\",\"fullName\":\"John Snow\",\"mapperDoingFullName\":\"John Snow\"}");
}

}
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
<module>mapstruct-field-mapping</module>
<module>mapstruct-iterable-to-non-iterable</module>
<module>mapstruct-lombok</module>
<module>mapstruct-immutables</module>
<module>mapstruct-mapping-from-map</module>
<module>mapstruct-mapping-with-cycles</module>
<module>mapstruct-nested-bean-mappings</module>
Expand Down