Reputation: 6321
I'm new to Java Spring Boot. I decided to use MapStruct because it's similar to ASP.NET Core's AutoMapper.
Problem:
The image is pretty self explanatory. It doesn't map firstName and lastName. I thought it could be the underscore e.g. first_name, but then I added password to the DTO model and it's not being set too.
By the way, I tested it by removing the DTO model with the following code and it worked which means that the cause is that UserMapper.
@GetMapping
public ResponseEntity<List<User>> getAll() {
return ResponseEntity.ok(userService.getAll());
}
Log from ReportingPolicy.WARN
2019-10-27 15:58:44.024 DEBUG 24284 --- [nio-5000-exec-1] org.hibernate.SQL : select user0_.id as id1_2_, user0_.email as email2_2_, user0_.first_name as first_na3_2_, user0_.last_name as last_nam4_2_, user0_.password as password5_2_ from users user0_
2019-10-27 15:58:44.033 DEBUG 24284 --- [nio-5000-exec-1] org.hibernate.SQL : select roles0_.user_id as user_id1_1_0_, roles0_.role_id as role_id2_1_0_, role1_.id as id1_0_1_, role1_.description as descr
UserMapper.java
package com.holding.server.mappers;
import java.util.List;
import com.holding.server.dto.UserDTO;
import com.holding.server.entities.User;
import org.mapstruct.Mapper;
import org.mapstruct.ReportingPolicy;
@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.WARN)
public interface UserMapper {
UserDTO toUserDTO(User user);
List<UserDTO> toUserDTOs(List<User> users);
User toUser(UserDTO userDTO);
}
User.java
package com.holding.server.entities;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Entity(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true)
@NotNull
@Size(max = 40)
@Email
private String email;
@NotNull
private String password;
@NotNull
@Size(max = 40)
private String firstName;
@NotNull
@Size(max = 40)
private String lastName;
@ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinTable(name = "user_role", joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id"))
private Set<Role> roles;
}
UserDTO.java
package com.holding.server.dto;
import lombok.Data;
@Data
public class UserDTO {
private String email;
private String firstName;
private String lastName;
}
UserServiceImpl.java
package com.holding.server.services;
import java.util.List;
import java.util.Optional;
import com.holding.server.entities.User;
import com.holding.server.repositories.UserRepository;
import com.holding.server.services.UserService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import lombok.AllArgsConstructor;
@AllArgsConstructor
@Service
public class UserServiceImpl implements UserService {
private UserRepository userRepository;
private BCryptPasswordEncoder bCryptPasswordEncoder;
@Override
public List<User> getAll() {
return userRepository.findAll();
}
@Override
public Optional<User> get(Long id) {
return userRepository.findById(id);
}
@Override
public Optional<User> create(User user) {
if (userRepository.findByEmail(user.getEmail()).isPresent()) {
return Optional.empty();
}
user.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
return Optional.ofNullable(userRepository.save(user));
}
@Override
public Optional<User> update(User user) {
Optional<User> userToUpdate = get(user.getId());
if (userToUpdate.isPresent()) {
userToUpdate.get().setFirstName(user.getFirstName());
userToUpdate.get().setLastName(user.getLastName());
userToUpdate.get().setRoles(user.getRoles());
if (user.getPassword() != null) {
userToUpdate.get().setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
}
return Optional.ofNullable(userRepository.save(userToUpdate.get()));
}
return Optional.empty();
}
@Override
public void delete(Long id) {
Optional<User> user = get(id);
if (user.isPresent()) {
userRepository.delete(user.get());
}
}
}
UserController.java
package com.holding.server.controllers;
import java.util.List;
import java.util.Optional;
import com.holding.server.dto.UserDTO;
import com.holding.server.entities.User;
import com.holding.server.mappers.UserMapper;
import com.holding.server.services.UserService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import lombok.AllArgsConstructor;
@AllArgsConstructor
@RestController
@RequestMapping("/api/users")
public class UserController {
private UserService userService;
private UserMapper userMapper;
@GetMapping
public ResponseEntity<List<UserDTO>> getAll() {
return ResponseEntity.ok(userMapper.toUserDTOs(userService.getAll()));
}
@GetMapping("/{id}")
public ResponseEntity<UserDTO> get(@PathVariable("id") Long id) {
Optional<User> user = userService.get(id);
if (user.isPresent()) {
return ResponseEntity.ok(userMapper.toUserDTO(user.get()));
}
return ResponseEntity.notFound().build();
}
@PostMapping
public ResponseEntity<UserDTO> create(@RequestBody UserDTO userDTO) {
User user = userMapper.toUser(userDTO);
user.setId(null);
Optional<User> savedUser = userService.create(user);
if (savedUser.isPresent()) {
return ResponseEntity.ok(userMapper.toUserDTO(savedUser.get()));
}
return ResponseEntity.status(HttpStatus.CONFLICT).build();
}
@PutMapping("/{id}")
public ResponseEntity<UserDTO> update(@PathVariable(required = true) Long id, @RequestBody UserDTO userDTO) {
User user = userMapper.toUser(userDTO);
user.setId(id);
Optional<User> updatedUser = userService.update(user);
if (updatedUser.isPresent()) {
return ResponseEntity.ok(userMapper.toUserDTO(updatedUser.get()));
}
return ResponseEntity.badRequest().build();
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable(required = true) Long id) {
if (userService.get(id).isPresent()) {
userService.delete(id);
return ResponseEntity.ok().build();
}
return ResponseEntity.notFound().build();
}
}
pom.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.0.RELEASE</version>
<relativePath />
</parent>
<groupId>com.holding</groupId>
<artifactId>server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<org.mapstruct.version>1.3.1.Final</org.mapstruct.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.10.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
If you need something else, ask me.
Upvotes: 1
Views: 1449
Reputation: 699
MapStruct creates generated class at compile time. You might want to clean and rebuild then check if the generated Mapper contains the required mappings.
Upvotes: 4