[JAVA] Bean-Kopie mit MapStruct

1. Warum MapStruct?

Es gibt mehrere Bibliotheken, die häufig beim Kopieren von Beans mit Java verwendet werden.

Von diesen empfehle ich am meisten "mapstruct". Der Grund ist, dass mapstruct ** am schnellsten ** ist. In den folgenden Quellen wird die Zeit angegeben, die für jede Bean-Kopie unter Verwendung der oben genannten fünf Bibliotheken benötigt wird.

@Slf4j
public class CopyDemoTest {

    public UserMainBO bo;

    public static int count = 1000000;

    @Before
    public void init(){
        bo = new UserMainBO();
        bo.setId(1L);
    }

    @Test
    public void mapstruct() {
        UserMainVOMapping INSTANCE = Mappers.getMapper( UserMainVOMapping.class );
        log.info("star------------");
        for (int i = 1; i <=count; i++) {
            UserMainVO vo = INSTANCE.toVO(bo);
        }
        log.info("end------------");
    }

    @Test
    public void beanCopier() {
        log.info("star------------");
        BeanCopier copier = BeanCopier.create(UserMainBO.class, UserMainVO.class, false);
        for (int i = 1; i <=count; i++) {
            UserMainVO vo = new UserMainVO();
            copier.copy(bo, vo, null);
        }
        log.info("end------------");
    }

    @Test
    public void springBeanUtils(){
        log.info("star------------");
        for (int i = 1; i <=count; i++) {
            UserMainVO vo = new UserMainVO();
            BeanUtils.copyProperties(bo, vo);
        }
        log.info("end------------");
    }

    @Test
    public void apacheBeanUtils() throws InvocationTargetException, IllegalAccessException {
        for (int i = 1; i <=count; i++) {
            UserMainVO vo = new UserMainVO();
            org.apache.commons.beanutils.BeanUtils.copyProperties(bo, vo);
        }
        log.info("end------------");
    }

    @Test
    public void apachePropertyUtils() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        log.info("star------------");
        for (int i = 1; i <=count; i++) {
            UserMainVO vo = new UserMainVO();
            PropertyUtils.copyProperties(bo, vo);
        }
        log.info("end------------");
    }

}

Prüfergebnis:

1,000 mal 10,000 mal 100,000 mal 1,000,000 mal
apache.BeanUtils 550ms 1085ms 4287ms 32088ms
apache.PropertyUtils 232ms 330ms 2080ms 20681ms
cglib.BeanCopier 73ms 106ms 102ms 99ms
mapstruct 91ms 5ms 7ms 12ms
spring.BeanUtils 5ms 188ms 336ms 844ms

Aus den obigen Überprüfungsergebnissen kann bestätigt werden, dass mapstruct eine weitaus bessere Leistung als andere Bibliotheken aufweist.

2. MapStruct verwenden

Um MapStruct verwenden zu können, müssen Sie die MapStruct-Bibliothek in Ihr Projekt importieren. Das Folgende ist ein Beispiel für die Verwendung von Maven.

pom.xml


<properties>
    <org.mapstruct.version>1.3.1.Final</org.mapstruct.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>${org.projectlombok.version}</version>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.1</version>
            <configuration>
                <source>1.8</source> <!-- depending on your project -->
                <target>1.8</target> <!-- depending on your project -->
                <annotationProcessorPaths>
                    <path>
                        <groupId>org.mapstruct</groupId>
                        <artifactId>mapstruct-processor</artifactId>
                        <version>${org.mapstruct.version}</version>
                    </path>
                </annotationProcessorPaths>
            </configuration>
        </plugin>
    </plugins>
</build>

2.1 Kombinierte Verwendung mit Lombok

Bei Verwendung von MapStruct mit der Version 1.2.0.Final oder früher und lombok zusammen sind die folgenden Einstellungen erforderlich:

pom.xml


<properties>
    <org.mapstruct.version>1.1.0.Final</org.mapstruct.version>
    <org.projectlombok.version>1.18.12</org.projectlombok.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>${org.projectlombok.version}</version>
    </dependency>
    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct</artifactId>
        <version>${org.mapstruct.version}</version>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.1</version>
            <configuration>
                <source>1.8</source> <!-- depending on your project -->
                <target>1.8</target> <!-- depending on your project -->
                <annotationProcessorPaths>
                    <path>
                        <groupId>org.projectlombok</groupId>
                        <artifactId>lombok</artifactId>
                        <version>${org.projectlombok.version}</version>
                    </path>
                    <path>
                        <groupId>org.mapstruct</groupId>
                        <artifactId>mapstruct-processor</artifactId>
                        <version>${org.mapstruct.version}</version>
                    </path>
                </annotationProcessorPaths>
            </configuration>
        </plugin>
    </plugins>
</build>

2.2 Kombinierte Verwendung mit Swagger-UI

Da das Innere von swagger-ui eine alte Version von MapSruct verwendet, muss die Abhängigkeit von MapSruct aus der swagger-ui-Bibliothek ausgeschlossen werden, wenn es zusammen mit swagger-ui verwendet wird.

pom.xml


<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>${springfox-swagger.version}</version>
    <exclusions>
        <exclusion>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>${springfox-swagger.version}</version>
    <exclusions>
        <exclusion>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct</artifactId>
        </exclusion>
    </exclusions>
</dependency>

3. Verwendung von MapStruct

3.1 Einfach zu bedienen

Es gibt zwei Bohnenklassen unten.

Student.java


import lombok.Data;
import lombok.ToString;

@Data
@ToString
public class Student {
    private String name;
    private String address;
    private String phone;
}

Person.java


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Person {
    private String name;
    private String address;
    private String telephone;
}

Von nun an Bean-Kopie vom Personenobjekt zum Schülerobjekt. Vor dem Kopieren der Bean muss die Mapper-Klasse im Voraus vorbereitet werden.

StudentMapper.java


import model.Person;
import model.Student;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;

@Mapper
public interface StudentMapper {
    StudentMapper INSTANCE = Mappers.getMapper(StudentMapper.class);

    Student personToStudent(Person person); //Der Methodenname ist beliebig
}

Kopieren wir die Bohne.

main_method


Person person = new Person("lisa", "Tokyo", "12345");
Student student = StudentMapper.INSTANCE.personToStudent(person);
System.out.println(student);

//Ausgabe
// Student(name=Lisa, address=Tokyo, phone=null)

Standardmäßig wird jedes Feld mit einer Namensinkongruenz automatisch ** ignoriert ** und kann nicht von "Telefon" auf "Telefon" kopiert werden.

3.2 Kopie des Namensinkongruenzfeldes

Wenn die Feldnamen von Quelle und Ziel nicht übereinstimmen, müssen Sie beim Kopieren "@ Mapping" angeben.

StudentMapper.java


import model.Person;
import model.Student;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;

@Mapper
public interface StudentMapper {
    StudentMapper INSTANCE = Mappers.getMapper(StudentMapper.class);

    @Mapping(source = "telephone", target = "phone")
    Student personToStudent(Person person);
}

Ausführungsergebnis:

Student(name=Lisa, address=Tokyo, phone=12345)

3.3 Felder ausschließen

Wenn Sie keine Felder kopieren möchten, können Sie sie über das Attribut "Ignorieren" von "@ Mapping" vom Kopieren ausschließen.

StudentMapper.java


import model.Person;
import model.Student;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;

@Mapper
public interface StudentMapper {
    StudentMapper INSTANCE = Mappers.getMapper(StudentMapper.class);

    @Mapping(source = "telephone", target = "phone")
    @Mapping(target = "address", ignore = true)
    Student personToStudent(Person person);
}

Ausführungsergebnis:

Student(name=Lisa, address=null, phone=12345)

3.4 Kopieren von mehreren Beans auf eine Bean

MapStruct kann Felder von mehreren Beans auf eine Bean kopieren.

LoginInfo.java


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class LoginInfo {
    private String id;
    private String password;
}

UserProfile.java


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserProfile {
    private String email;
    private String address;
}

UserInfo.java


@ToString
@Data
public class UserInfo {
    private String id;
    private String password;
    private String email;
    private String address;
}

Bereiten Sie Mapper vor.

UserInfoMapper.java


import model.LoginInfo;
import model.UserInfo;
import model.UserProfile;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;

@Mapper
public interface UserInfoMapper {
    UserInfoMapper INSTANCE = Mappers.getMapper(UserInfoMapper.class);

    @Mapping(target = "id", source = "loginInfo.id")
    @Mapping(target = "password", source = "loginInfo.password")
    @Mapping(target = "email", source = "userProfile.email")
    @Mapping(target = "address", source = "userProfile.address")
    UserInfo fromLoginInfoAndUserProfile(LoginInfo loginInfo, UserProfile userProfile);
}

eine Kopie machen.

main_method


LoginInfo loginInfo = new LoginInfo("12345", "54321");
UserProfile userProfile = new UserProfile("[email protected]", "Tokyo");

UserInfo userInfo = UserInfoMapper.INSTANCE.fromLoginInfoAndUserProfile(loginInfo, userProfile);
System.out.println(userInfo);

//Ausgabe
// UserInfo(id=12345, password=54321, [email protected], address=Tokyo)

3.5 Aktualisieren Sie die erstellte Bean

MapStruct kann nicht nur Beans kopieren und erstellen, sondern auch erstellte Beans aktualisieren.

Fügen Sie UserInfoMapper.java die folgende Quelle hinzu:

UserInfoMapper.java


@Mapping(target = "email", source = "email")
@Mapping(target = "address", source = "address")
void updateUserProfile(UserProfile userProfile, @MappingTarget UserInfo userInfo);

Lassen Sie uns aktualisieren:

main_method


LoginInfo loginInfo = new LoginInfo("12345", "54321");
UserProfile userProfile = new UserProfile("[email protected]", "Tokyo");

UserInfo userInfo = UserInfoMapper.INSTANCE.fromLoginInfoAndUserProfile(loginInfo, userProfile);
System.out.println(userInfo);
//Ausgabe
// UserInfo(id=12345, password=54321, [email protected], address=Tokyo)

userProfile = new UserProfile("[email protected]", "Fukuoka");
UserInfoMapper.INSTANCE.updateUserProfile(userProfile, userInfo);
System.out.println(userInfo);
//Ausgabe
// UserInfo(id=12345, password=54321, [email protected], address=Fukuoka)

3.6 Formatkonvertierung

In MapStruct können Sie beim Konvertieren von Datum / lokalem Datum in Zeichenfolge und beim Konvertieren von int in Zeichenfolge das Format angeben und konvertieren.

Person.java


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.time.LocalDate;

@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class Person {
    private String name;
    private LocalDate birthday;
    private int salary;
}

Employee.java


import lombok.Data;
import lombok.ToString;

@Data
@ToString
public class Employee {
    private String name;
    private String birthday;
    private String salary;
}

PersonMapper.java


import model.Employee;
import model.Person;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;

@Mapper
public interface PersonMapper {
    PersonMapper INSTANCE = Mappers.getMapper(PersonMapper.class);

    @Mapping(source = "birthday", target = "birthday", dateFormat = "yyyy/MM/dd")
    @Mapping(source = "salary", target = "salary", numberFormat = "#,###")
    Employee personToEmployee(Person person);
}

main_method


Person person = new Person("lisa", LocalDate.of(1990, 1, 20), 1234567);
Employee employee = PersonMapper.INSTANCE.personToEmployee(person);
System.out.println(employee);
//Ausgabe
// Employee(name=lisa, birthday=1990/01/20, salary=1,234,567)

3.7 expression

Wenn beim Kopieren einer Bean die Komplexität konvertiert werden muss, kann dies leicht mit expression realisiert werden.

Großschreiben Sie beispielsweise das Feld "Name", wenn Sie von einem Personenobjekt in ein Mitarbeiterobjekt kopieren.

PersonMapper.java


import model.Employee;
import model.Person;
import org.apache.commons.lang3.StringUtils;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;

@Mapper(imports = {StringUtils.class}) //StringUtils importieren
public interface PersonMapper {
    PersonMapper INSTANCE = Mappers.getMapper(PersonMapper.class);

    @Mapping(target = "name", expression = "java(StringUtils.upperCase(person.getName()))")
    Employee personToEmployee(Person person);
}

3.8 Standardmethode

Ab Java8 kann die Schnittstelle die Standardmethode unterstützen. MapStruct kann auch die Standardmethode verwenden, um eine komplexe Kopierlogik zu erstellen.

PersonMapper.java


import model.Employee;
import model.Person;
import org.apache.commons.lang3.StringUtils;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;

import java.time.format.DateTimeFormatter;

@Mapper(imports = {StringUtils.class})
public interface PersonMapper {
    PersonMapper INSTANCE = Mappers.getMapper(PersonMapper.class);

    default Employee personToEmployee(Person person, String dateFormat) {
        Employee employee = new Employee();
        //Großschreibung des Namens
        employee.setName(StringUtils.upperCase(person.getName()));
        //Konvertieren Sie den Geburtstag in das angegebene Format
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(dateFormat);
        employee.setBirthday(person.getBirthday().format(dateTimeFormatter));

        return employee;
    }
}

Führen Sie die Standardmethode aus.

main_method


Person person = new Person("lisa", LocalDate.of(1990, 1, 20), 1234567);
Employee employee = PersonMapper.INSTANCE.personToEmployee(person, "JJJJ Jahr MM Monat TT Tag");
System.out.println(employee);
//Ausgabe
// Employee(name=LISA, birthday=20. Januar 1990, salary=null)

Recommended Posts

Bean-Kopie mit MapStruct
Führen Sie das Bean-Mapping mit MapStruct mit hoher Geschwindigkeit durch
Bean-Mapping mit MapStruct Teil 1
Bean-Mapping mit MapStruct Teil 3
Bean-Mapping mit MapStruct Teil 2