I'm developing a spring boot application for now. Now I have a problem with mapping with org.mapstruct.Mapper.
For short I have an entity:
package ru.innopolis.entity;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "addresses")
public class Address {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String locality;
private String street;
private String house;
private String porch;
private String floor;
private String apartment;
@ManyToOne
@JoinColumn(name = "user_id", nullable = false)
private User user;
}
@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "first_name", nullable = false)
private String firstName;
@Column(name = "last_name", nullable = false)
private String lastName;
@Column(name = "phone_number", nullable = false)
private String phoneNumber;
@OneToMany(mappedBy = "user")
private List<Order> orders = new ArrayList<>();
}
There is a controller which creates an address. For that a DTO was created with :
package ru.innopolis.dto;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class CreateAddressDTO {
public String locality;
public String street;
public String house;
public String porch;
public String floor;
public String apartment;
public Long userId;
}
To display CreateAddressDTO.userId in swagger as long I specified it as a long.
To map DTO to entity mapper was created:
package ru.innopolis.mappers;
import org.mapstruct.*;
import ru.innopolis.dto.CreateAddressDTO;
import ru.innopolis.dto.RetrieveAddressDTO;
import ru.innopolis.entity.Address;
import java.util.List;
@Mapper(
componentModel = MappingConstants.ComponentModel.SPRING,
unmappedTargetPolicy = ReportingPolicy.IGNORE,
nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE
)
public interface AddressMapper {
@Mapping(target = "user", ignore = true)
Address map(CreateAddressDTO data);
@Mapping(source = "user.id", target = "userId")
RetrieveAddressDTO map(Address address);
}
user
is not being passed from frontend so it is ignored.
Then at last in service:
package ru.innopolis.service;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import ru.innopolis.dto.CreateAddressDTO;
import ru.innopolis.entity.Address;
import ru.innopolis.mappers.AddressMapper;
import ru.innopolis.repository.AddressRepository;
import ru.innopolis.repository.UserRepository;
import java.util.List;
@Service
@RequiredArgsConstructor
public class AddressServiceImpl implements AddressService {
private final AddressRepository addressRepository;
private final UserRepository userRepository;
private final AddressMapper addressMapper;
@Override
public Address createAddress(CreateAddressDTO data) {
var address = addressMapper.map(data);
var user = userRepository.findById(data.getUserId()).orElse(null);
address.setUser(user);
addressRepository.save(address);
return address;
}
}
userId
is passed as a long. It is being processed on its own. First get via UserRepository and then attached to address
.
I send data via endpoint:
{
"locality": "string",
"street": "string",
"house": "string",
"porch": "string",
"floor": "string",
"apartment": "string",
"userId": 1
}
So my question is: is that the way foreign keys handled in cases like mine?
Through using MapStruct, you can define fetchUser method as default one to handle with toEntity process
@Mapper(componentModel = "spring",
uses = UserRepository.class)
public interface AddressMapper {
@Mapping(target = "user",
expression = "java(fetchUser(dto.getUserId(), userRepository))")
Address toEntity(CreateAddressDTO dto, @Context UserRepository userRepository);
default User fetchUser(Long id, UserRepository repo) {
return repo.findById(id)
.orElseThrow(() -> new EntityNotFoundException("User not found: " + id));
}
}
Next, you can add these code block shown below to relevant service.
Address address = addressMapper.toEntity(dto, userRepository);
addressRepo.save(address);
I hope you can help you fix your issue.