MapStuct partial implementation - Java - java

My code goes like this,
#Mapper
public interface DtoMapper {
DtoMapper MAPPER = Mappers.getMapper(DtoMapper.class);
ExampleModel dtoToDdModel(ExampleDto exampleDto);
ExampleDto someOtherEntityToDto(OtherEntity otherEntity);
}
public class DtoMapperImpl implements DtoMapper {
#Override
public ExampleDto someOtherEntityToDto(OtherEntity otherEntity){
if ( OtherEntity == null ) {
return null;
}
// Conversion
}
// I don't want to define dtoToDdModel
}
Is it possible to only provide implementation of someOtherEntityToDto in DtoMapperImpl? While dtoToDdModel follows the default mapping provided by MapStruct?
Thank you in Advance!

Apply below annotation in DtoMapper interface
#InheritInverseConfiguration
ExampleModel dtoToDdModel(ExampleDto exampleDto)

MapStruct will only implement the abstract methods. Which means that if you provide a custom default methods then MapStruct will not implement it.
e.g.
#Mapper
public interface DtoMapper {
DtoMapper MAPPER = Mappers.getMapper(DtoMapper.class);
default ExampleModel dtoToDdModel(ExampleDto exampleDto) {
//TODO write implementation
}
ExampleDto someOtherEntityToDto(OtherEntity otherEntity);
}
Note: the #Mapper does not have to be an interface it can also be an abstract class.

Related

#Service Class Not Autowired in org.mapstruct.#Mapper Class

I am trying to add json serialization to my SpringBoot app using MapStruct. #Mapper class uses #Service to add some "aftermapping" logic. The problem is, that this #Service class is not autowired.
This is my Mapper class:
#Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE, componentModel = "spring")
public abstract class InstrumentMapper {
protected MarketDataService marketDataService; // is #Service
#Mapping(target = "marketCode",
expression = "java(instrument.getMarket().getCode())")
public abstract InstrumentDto fromInstrument(Instrument instrument);
public abstract List<InstrumentDto> fromInstruments(List<Instrument> instruments);
#Mapping(target = "market",
expression = "java(marketDataService.findMarketByCode(instrumentDto.getMarketCode()))")
public abstract Instrument toInstrument(InstrumentDto instrumentDto);
public abstract List<Instrument> toInstruments(List<InstrumentDto> instrumentDtos);
#Autowired
public void setMarketDataService(MarketDataService marketDataService) {
this.marketDataService = marketDataService;
}
}
When toInstrument method is called, application fails with NPE, trying to marketDataService.findMarketByCode(instrumentDto.getMarketCode()).
Hopefully, this information will be enough. Let me know if anything else is needed.
Thanks in advance!
Update:
MarketDataService class. It is added to the context through #Service annotation.
#Service
public class MarketDataService {
#Autowired
private InstrumentRepository instrumentRepository;
public Instrument findInstrumentByCode(String code) {
return instrumentRepository.findFirstByCode(code);
}
public List<InstrumentDto> getAllInstrumentDtos() {
List<Instrument> instruments = getAllInstruments();
List<InstrumentDto> dtos = Mappers.getMapper(InstrumentMapper.class).fromInstruments(instruments);
return dtos;
}
public void updateInstrument(InstrumentDto instrumentDto) {
Instrument instrument = findInstrumentByCode(instrumentDto.getCode());
if (instrument == null) {
throw new IllegalArgumentException("Market with given code not found!");
}
instrumentRepository.delete(instrument);
instrument = Mappers.getMapper(InstrumentMapper.class).toInstrument(instrumentDto);
instrumentRepository.save(instrument);
}
}
The algorithm is the following: #Controller class gets PUT request and calls MarketDataService.updateInstrument method with the body of the request (instrumentDto parameter). The latter one calls toInstrument method with the same parameter.
The reason why you have an NPE is because you are using the MapStruct Mappers factory for a non default component model.
The Mappers factory does not perform any dependency injections.
You have to inject your mapper in your MarketDataService. Be careful when doing that because you have a cyclic dependency.
In addition to that the patterns you are using in your Mapper are not really the right ones. You are using an expression when a simple source will do.
e.g.
#Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE, componentModel = "spring")
public abstract class InstrumentMapper {
protected InstrumentRepository instrumentRepository;
#Mapping(target = "marketCode", source = "market.code")
public abstract InstrumentDto fromInstrument(Instrument instrument);
public abstract List<InstrumentDto> fromInstruments(List<Instrument> instruments);
#Mapping(target = "market", source = "marketCode")
public abstract Instrument toInstrument(InstrumentDto instrumentDto);
public abstract List<Instrument> toInstruments(List<InstrumentDto> instrumentDtos);
protected Instrument findInstrumentByCode(String code) {
return instrumentRepository.findFirstByCode(code);
}
#Autowired
public void setMarketDataService(MarketDataService marketDataService) {
this.marketDataService = marketDataService;
}
}

MapStruct Mapper as Spring Framework Converter - idiomatic use possible?

I'd like to combine MapStruct mappers with Spring's Conversion model. So I declare every Mapper interface as an extension of Spring's Converter:
#Mapper
public interface CarMapper extends Converter<Car, CarDto> {
#Override
CarDto convert(Car car);
}
I can then use the mapper beans by injecting the standard ConversionService:
class CarWarehouse {
#Autowired
private ConversionService conversionService;
...
public CarDto getCarInformation(Car car) {
return conversionService.convert(car, CarDto.class);
}
}
This works nicely, but I'm wondering whether there's a way to avoid injecting some Mappers into others directly via the uses attribute. What I'd like to do is tell a Mapper to use the ConversionService for employing another mapper. However, since the ConversionService's convert method doesn't match MapStruct's standard pattern for a mapping method, the code generation plugin doesn't recognise that it can use the service when looking for a submapping. Basically, what I want to do is write
#Mapper(uses=ConversionService.class)
public interface ParentMapper extends Converter<Parent, ParentDto>
instead of
#Mapper(uses={ChildMapper1.class, ChildMapper2.class, ChildMapper3.class})
public interface ParentMapper extends Converter<Parent, ParentDto>
Is there a way to achieve this?
Edit
Since it's been asked, let's say I've got a CarMapper defined as above, with the types Car and CarDto having an attribute wheel of type Wheel and WheelDto, respectively. Then I'd like to be able to define another Mapper like this:
#Mapper
public interface WheelMapper extends Converter<Wheel, WheelDto> {
#Override
WheelDto convert(Wheel wheel);
}
Right now, I'd have to add this Mapper explicitly:
#Mapper(uses = WheelMapper.class)
public interface CarMapper extends Converter<Car, CarDto>
Which would then give the generated CarMapperImpl an #Autowired member of type WheelMapper which would be called in order to map the attribute wheel.
However, what I'd like is that the generated code would look somewhat like this:
#Component
public class CarMapperImpl implements CarMapper {
#Autowired
private ConversionService conversionService;
#Override
public CarDto convert(Car car) {
CarDto carDto = new CarDto();
carDto.setWheel(conversionService.convert(car.getWheel(), WheelDto.class);
return carDto;
}
}
It's been more than a year since I asked this question, but now we've come up with an answer inside the MapStruct project itself - the MapStruct Spring Extensions project.
A CarMapper example is provided as an example within the project.
You can just skip passing a WheelMapper entirely, when you just have a CarMapper the generated CarMapperImpl will contain a logic to map Wheel <-> WheelDto as well. No need to pass anything to uses, making your issue obsolete.
carDto.setWheel( wheelToWheelDto( car.getWheel() ) );
with a method like;
protected WheelDto wheelToWheelDto(Wheel wheel) {
if ( wheel == null ) {
return null;
}
WheelDto wheelDto = new WheelDto();
wheelDto.setName( wheel.getName() );
return wheelDto;
}
I did try to achieve an intelligent injection of ConversionService through MapStruct, but it is not possible I think. You'd need support from MapStruct to achieve such a feat. It does not even consider injecting ConversionService. Maybe a custom generic mapper that is already implemented and uses ConversionService might work, but I was unable to do that! Though I don't see any reason for it since MapStruct is already creating all necessary smaller mappers from the parent mapper...
Frankly, I doubt you can achieve automatic wiring of ConversionService into generated mappers by MapStruct. The way that you described in the question (that wires individual mappers through uses annotation attribute), probably, the best that MapStruct can give out of the box.
However, there is workaround, if you absolutely need to use ConversionService to perform conversion for some DTOs (e.g. if you have some legacy converters, that you don't want to refactor to mappers). Basically, you can use combination of Mappers.getMapper static factory to get instance of ConversionService and default method in the mapper interface, to use ConversionService instance:
#Mapper(componentModel = "spring")
public interface CarMapper extends Converter<Car, CarDto> {
ConversionService CONVERSION_SERVICE = Mappers.getMapper(ConversionService.class);
#Override
default CarDto convert(Car car) {
if (car == null) {
return null;
}
CarDto carDto = new CarDto();
carDto.setEngine(CONVERSION_SERVICE.convert(car.getEngine(), EngineDto.class));
carDto.setWheel(CONVERSION_SERVICE.convert(car.getWheel(), WheelDto.class));
return carDto;
}
}
Note: as you can see, workaround requires to write CarMapper code. So, in my opinion, the solution with uses annotation attribute is cleaner approach. For example, you get almost the same result, by defining following interface:
#Mapper(componentModel = "spring",
uses = {EngineMapper.class, WheelMapper.class},
injectionStrategy = InjectionStrategy.CONSTRUCTOR)
public interface CarMapper extends Converter<Car, CarDto> {
#Override
CarDto convert(Car car);
Generated mapper:
#Component
public class CarMapperImpl implements CarMapper {
private final EngineMapper engineMapper;
private final WheelMapper wheelMapper;
#Autowired
public CarMapperImpl(EngineMapper engineMapper, WheelMapper wheelMapper) {
this.engineMapper = engineMapper;
this.wheelMapper = wheelMapper;
}
#Override
public CarDto convert(Car car) {
if (car == null) {
return null;
}
CarDto carDto = new CarDto();
carDto.setEngine(engineMapper.convert(car.getEngine()));
carDto.setWheel(wheelMapper.convert(car.getWheel()));
return carDto;
}
}

Autowire Spring Bean into interface for default method

I need to add a default method to an interface some classes implement, but my IDE complains (bean may not have been initialized).
Code would be something like this:
public interface IValidator {
MyValidationBean beanToBeAutowired;
...
default Boolean doSomeNewValidations(){
return beanToBeAutowired.doSomeNewValidations();
}
}
Is it just that autowiring into interfaces is not allowed or there's something wrong with the code?
Using #Component on the interface doesn't make any difference.
I'd rather keep this design instead of using an abstract class.
Adding a Variable into interface is not possible in Java. It will be by default a public static final constant. So you have to do either the following:
MyValidationBean beanToBeAutowired = new MyValidationBeanImpl();
or the following:
MyValidationBean beanToBeAutowired();
default Boolean doSomeNewValidations(){
return beanToBeAutowired().doSomeNewValidations();
}
And you can override the beanToBeAutowired method in the implementation class.
i can think of solution as below -
public interface IValidator {
public Service getBeanToBeAutowired();
default Boolean doSomeNewValidations(){
return getBeanToBeAutowired().doSomeNewValidations();
}
}
public class ValidatorClass implements IValidator {
#Autowire private Service service;
#Override
public Service getBeanToBeAutowired() {
return service;
}
}
Just an idea, send validation bean to interface as parameter;
public interface IValidator {
default Boolean doSomeNewValidations(MyValidationBean beanToBeAutowired){
return beanToBeAutowired.doSomeNewValidations();
}
}
Your callerClass;
public class CallerClass implements IValidator{
#Autowired
MyValidationBean beanToBeAutowired;
...
doSomeNewValidations(beanToBeAutowired);
}

What is the best approach to get injected beans with same interface in factory using Spring?

I created one factory to decide what best implementation should be returned, based in some conditional check.
// Factory
#Component
public class StoreServiceFactory {
#Autowired
private List<StoreService> storeServices;
public StoreService getService(){
if(isActiveSale){
return storeServices.get("PublicStoreService")
}
return storeServices.get("PrivateStoreService")
}
}
//Service Implementations
#Service
#Qualifier("PublicStoreService")
public class PublicStoreService implements StoreService {
public getStoreBalanceScore(){
Do Stuff....
}
}
#Service
#Qualifier("PrivateStoreService")
public class PrivateStoreService implements StoreService {
public getStoreBalanceScore(){
Do Stuff....
}
}
// Controller
#Autowired
StoreServiceFactory storeServiceFactory;
#Override
public StoreData getStoreBalance(String storeId) {
StoreService storeService = storeServiceFactory.getService();
return simulationService.simulate(sellerId, simulation);
}
Is this approach good? If yes, how can i get my service from an elegant way?
I would like to use only annotations, without configurations.
You should use a map instead of a List and pass a string parameter to the getService method.
public class StoreServiceFactory {
#Autowired
private Map<String,StoreService> storeServices = new HashMap<>();
public StoreService getService(String serviceName){
if(some condition...){
// want to return specific implementation on storeServices map, but using #Qualifier os something else
storeServices.get(serviceName)
}
}
}
You can prepopulate the map with supported implementations. You can then get an appropriate service instance as follows :
// Controller
#Autowired
StoreServiceFactory storeServiceFactory;
#Override
public StoreData getStoreBalance(String storeId) {
StoreService storeService = storeServiceFactory.getService("private");//not sure but you could pass storeId as a parameter to getService
return simulationService.simulate(sellerId, simulation);
}
If you don't like using Strings, you can define an enum for the supported implementations and use that as the key for your map.
You don't need to create a list or map on your code. You can retrieve it directly from Spring context using GenericBeanFactoryAccessor. This has various method to retrieve a specific bean like based on name, annotation etc. You can take a look at javadoc here. This avoids unnecessary complexity.
http://docs.spring.io/spring-framework/docs/2.5.6/api/org/springframework/beans/factory/generic/GenericBeanFactoryAccessor.html

Use an annotation as a typed method parameter

In Java is it possible to use a class annotation as a typed method parameter.
For example - this is your annotation
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
public #interface Entity {
}
then
#Entity
public class Car {
...
}
and then do
interface Persister {
void persist(Entity entity);
}
You can do
public #interface Entity {
String name();
}
public class Car implements Entity{
public String name(){ return "car"; }
}
but that's just odd. Entity should be an ordinary interface instead.
---
It is possible though that through annotation processing, we can require that an argument to a method must have a static type that contains certain annotation. Not sure if someone has done that.
You can do this but it won't do what you expect. This persist(Entity) method can only take your Entity annotation, not an instance of a class you want to use.
Instead what you can do is
interface Entity { }
interface Car extends Entity {
interface Persister {
void persist(Entity entity);
}
This will work as expected and you can pass an instance of a Car to the persist method.

Categories

Resources