Mapping abstract class with mapStruct - spring

I have a project where I have a Library entity which contains a field media of type Media. Media is an abstract class which is implemented by a class Image and a class Video.
I also have a dto LibraryDto with a field media of type MediaDto. MediaDto contains a field whose value can be either "image" or "video". My question is : how can I tell mapstruct to instantiate either a Image class or a Video class based on the value of the type field of MediaDto?
Thanks for your response.

Sorry, first time I misunderstood your question.
You can create object factory method which instantiate the Media based on the type field like this:
#Mapper
public interface LibraryMapper {
Library libraryDtoToLibrary(LibraryDto libraryDto);
Media mediaDtoToMedia(MediaDto mediaDto);
#ObjectFactory
default Media createMedia(MediaDto mediaDto) {
if ("image".equals(mediaDto.getType())) {
return new Image();
} else if ("video".equals(mediaDto.getType())) {
return new Video();
}
throw new IllegalArgumentException("Unknown media type.");
}
}

Related

Is it considered a good practice using classes that extend an abstract class as #RequestBody?

I´m working on a legacy spring boot project that makes a strong reuse of a DTO class in a generic controller and in multiple services:
#PostMapping
controller.input(#RequestBody MyTypeDto type) { ... }
service.resolve(MyTypeDto type) { ... }
processor.send(MyTypeDto type) { ... }
I want to start decoupling it by creating another endpoint and making MyTypeDto an abstract class.
My biggest concern under all is the compatility with jackson.
public abstract class MyTypeDto { ... }
public class AnotherTypeDto extends MyTypeDto { ... }
public class AndAnotherTypeDto extends MyTypeDto { ... }
Is it considered a good practice?
As it is implied on your question, you controller endpoint is generic, it takes the input, creates the type, pass it to service based on subtype. Otherwise, you will end up many endpoints which all doing is creating the subtype and pass it to service.
If Jackson is your concern, Jackson has mechanism for subtypes. Please note you have to send one additional field which act as the discriminator (in this example, it is called type to decide which sub type to create.
#JsonTypeInfo(use = Id.NAME, include = As.PROPERTY, property = "type")
#JsonSubTypes({#JsonSubTypes.Type(value = FirstSubDto.class, name = "First"),
#JsonSubTypes.Type(value = SecondSubDto.class, name = "Second")})
public abstract class MyTypeDto {
..
}

Spring's couchbase JPA repository with abstract class fails to find entity

We are developing a project in Springboot that uses a Couchbase, I have following classes:
public abstract class Content {
...
}
public class Film extends Content {
...
}
public class Serie extends Content {
...
}
Then I have following JPA repository:
public interface ContentJpaRepository extends ReactiveCouchbaseSortingRepository<Content> {
}
Then, when I save a content (film or serie) the content is successfully saved, however, the _class field gets the simple class name (instead of the full package name).
Then, when doing:
repository.findById(id);
The repository fails as it can't deserialize the json document to the expected entity. How could I achieve that?
Thank you very much
Using a generic repository is currently not supported for Couchbase Spring Data, as the _class attribute will refer to the abstract class instead of its implementations.

Passing of list of type Baseclass between Webservices involving generics and conversion of polymorphic types

I have two REST services using Spring Boot running on two different servers. I am using REST Template for this communication.
There are some models that are shared by these two services. All these models are of type 'IDataToTransferred' .
'IDataToTransferred' is a marker Interface implemented by various Model Beans.
I need to write a common logic for passing a list of these models between these REST services.
Hence I wrote a logic which uses parameters
List<? extends IDataToTransferred> from Sender service to Receiver Service.
Update: With Some Code
IDataToTransferred.java is a marker Interface
DataToBeSent.java
DataToBeSent Implements IDataToTransferred{
//Simple Pojo
}
SenderService.java
sendData(List<? extends IDataToTransferred> uploadDataObjectList){
//Some Code with REST Template
//restTemplate.postForEntity
}
IDataToTransferred Interface is shared between communicating webservices.
DataToBeReceived.java
DataToBeReceived Implements IDataToTransferred{
//Simple Pojo
}
ReceiverService.java
receiveData(List<? extends IDataToTransferred> uploadDataObjectList){
//Some Code to convert uploadDataObjectList to DataToBeReceived
}
Note In REST service I was always getting 415 error. Unsupported Media type. when I use the same List<? extends IDataToTransferred> on Receiver.
When I changed this to List<? super IDataToTransferred> on Receiver side, now it works, I am guessing because of Producer extends Consumer super rules.
But the problem is that now I can't typecast to the IDataToTransferred type on Receiver Side. Inside the list I am getting all linkedHashmap, the json got converted to linked HashMap between these services.
How can I get DataToBeReceived class object in ReceiverService?
For simplicity sake I have removed Controllers. Assume that they have the same signature as the services.
If I had known better terms to search, I would have found answer before Posting. But alas.
In any case I found the answer in stackoverflow page here together with a this blog ofcourse.
The examples are with abstract classes. I have used with interfaces.
As mentioned in the link. I Introduced below annotation in the marker interface IDataToTransferred:
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type")
#JsonSubTypes({
#Type(value = DataToBeSent.class, name = "datatransfer")})
The property type is introduced in the bean DataToBeSent as a property. This type param is used as information for conversion into implementing type from interface type. One can use a different variable than one named "type". In JsonSubTypes annotation , we mention the classes that are implementing this interface.
DataToBeSent Implements IDataToTransferred{
//Simple Pojo
// Some Properties with getter and setter
String type = "datatransfer";
//with getter and setter
}
The same exercise needs to be implemented on the Receiver Side also. Hence, we will have annotation as below:
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type")
#JsonSubTypes({
#Type(value = DataToBeReceived.class, name = "datatransfer")})
Here, we have DataToBeReceived class as implementing the IDataToTransferred interface. Ofcourse you need to add type as property to DataToBeReceived class also as below:
DataToBeReceived Implements IDataToTransferred{
//Simple Pojo
// Some Properties with getter and setter
String type = "datatransfer";
//with getter and setter
}
Hope this helps.

Mapping persisted entities to mongo using spring

In spring documentation talking about Customizing type mapping
Reference
Using #TypeAlias and TypeInformationMapper .. but I cannot find any practical example
can someone please reference?
When defining MappingMongoConverter it is related the entities persistence?
Thanks
When you persist an entity using spring-data then mongo document created will have an _class attribute which stores the fully qualified name of the class. The #TypeAlias is to customize the value saved in the _class attribute.
This example from the spring reference shows how the _class attribute is added to the mongo document. If you attach a #TypeAlias("sample) then the _class attribute will have the value "sample" instead of the fully qualified name.
public class Sample {
Contact value;
}
public abstract class Contact { … }
public class Person extends Contact { … }
Sample sample = new Sample();
sample.value = new Person();
mongoTemplate.save(sample);
{ "_class" : "com.acme.Sample",
"value" : { "_class" : "com.acme.Person" }
}

Cannot convert type System.Data.Entity.DbSet to System.Collections.Generic.ICollection

I am using Entity Framework 4.1 code first in an MVC 3 app.
I have the following repository:
public class BankRepository : IBankRepository
{
HefContext db = new HefContext();
public ICollection<Bank> GetAll()
{
return db.Banks;
}
}
I get an error when returning db.Banks. I'm not sure what it means, can someone please help clarify and how to change it so that the error goes away? The error is:
Cannot implicitly convert type 'System.Data.Entity.DbSet<MyProject.Core.DomainObjects.Bank>' to 'System.Collections.Generic.ICollection<MyProject.Core.DomainObjects.Bank>'. An explicit conversion exists (are you missing a cast?)
What is returned by db.Banks? An IEnumerable?
db.Banks is of type DbSet. This class does not implement ICollection interface. Change the return type of the method to IQueryable<Bank> or IEnumerable<Bank>.
public class BankRepository : IBankRepository
{
HefContext db = new HefContext();
public IQueryable<Bank> GetAll()
{
return db.Banks;
}
}
ICollection is used only as the backing property to support LazyLoading, not as the result of a method. Check here ;)

Resources