Can't mapping the abstract class using model mapper - spring

I have an issue while mapping the DTO to Entity the CVV and expiration date not working but the number of the card is working fine, and mapping the did I miss any configuration ??
I have class A inherit from PaymentRequestDTO and class B inherit PaymentRequest
and mapping using this.modelmapper().map(classA, classB);
Parent Class for A
public abstract class PaymentRequestDTO {
....
CardRequestDTO card;
...
}
CardDTO
public class CardRequestDTO {
#JsonProperty("name")
public String name;
#JsonProperty("number")
public String number;
#JsonProperty("expiryMonth")
public String expiryMonth;
#JsonProperty("expiryYear")
public String expiryYear;
#JsonProperty("cvv")
public String cvv;
}
Parent Class for B
#Data
#JsonInclude(JsonInclude.Include.NON_NULL)
public abstract class PaymentRequest{
#JsonProperty("card_number")
public String cardNumber;
#JsonProperty("expiry_date")
public String expiryDate;
#JsonProperty("card_security_code")
public String cardSecurityCode;
}
PropertyMap class
public class PaymentRequestConverter extends PropertyMap<PaymentRequestDTO, PaymentRequest> {
#Override
protected void configure() {
map().setCardNumber(source.getCard().getNumber());
map().setCardSecurityCode(source.getCard().getCvv());
map().setExpiryDate(FortHelper.expiryDate(source.getCard().getExpiryYear(), source.getCard().getExpiryMonth()));
}
}
Here is my model mapper
#Bean
public ModelMapper modelMapper() {
ModelMapper modelMapper = new ModelMapper();
modelMapper.createTypeMap(PaymentRequestDTO.class, PaymentRequest.class).addMappings(new PaymentRequestConverter());
return modelMapper;
}
Model mapper version 3.1.0

Related

Retrive a object of parent class into child class in spring boot application

I am building REST API using spring boot application. I have connected application to Mongodb database. I have created a database named "Employee" and collection as "Employee" itself. Now i want to create a document. I have three class. Class A, Class B and class C.
Class A is the parent Class having property (id,name,password). Class B is child class and extends Class A with property(address,phoneNumber) and class C is child class which also extends class A with property (fatherName,MotherName).
Now i want to add the data to database as object of B or object of C and also want to retrive the data from database as object of B or Object of C.
here is code of Class A:
package com.example.webproject;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
#Document(collection="Employee")
public class A {
#Id
private String id;
private String passwd;
private String username;
public String getId() {
return id;
}
public void setIp(String string) {
this.ip = string;
}
public String getPasswd() {
return passwd;
}
public void setPasswd(String passwd) {
this.passwd = passwd;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
class B:
package com.example.webproject;
public class B extends A {
private String address;
private String phoneNumber;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber= phoneNumber;
}
}
Class C :
package com.example.webproject;
public class C extends A {
private String fatherName;
private String motherName;
public String getFatherName() {
return fatherName;
}
public void setFatherName(String fatherName) {
this.fatherName = fatherName;
}
public String getMotherName() {
return motherName;
}
public void setMotherName(String motherName) {
this.motherName = motherName;
}
}
EmployeeRepository.java
package com.example.webproject;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface EmployeeRepository extends MongoRepository<A,String> {}
EmployeeController.java
#RestController
public class EmployeeController {
#Autowired
private EmployeeRepository repo;
#PostMapping("/addByB")
public String addDataByB(#RequestBody B res) {
repo.save(res);
return "added";
}
#PostMapping("/addByC")
public String addDataByC(#RequestBody C res) {
repo.save(res);
return "added";
}
#GetMapping("/getByB")
public List<B> getDataByB(){
List<B> b= repo.findAll(); #Here it throws error because repo.findAll return object of A.
return b;
}
When i try to add data as B object or C object using swagger , the data is getting stored in database. Now i want to retrieve the data as B object or C object, how to achieve this?
Because you just create Repository of class A and call it, you nedd to creat two another repo of class B and C then call them like you call " EmployeeRepository " so you can use them and get the data.

Why does MongoRepository save return an empty json and save empty value when a variable is not empty?

I have a simple document:
#Document
#NoArgsConstructor
#AllArgsConstructor
#Builder
#ToString
public class ProductUnit {
#Id
String id;
private String name;
private Integer price;
private LocalDateTime localDateTime;
}
Simple MongoRepository :
public interface productRepo extends MongoRepository<ProductUnit,String> {
ProductUnit deleteByName(String name);
List<ProductUnit> findByPrice(Integer price);
}
and Service :
#Service
public class productServiseImpl implements productServise {
#Autowired
productRepo repository;
#Override
public ProductUnit saveOrUpdate(ProductUnit productUnit) {
System.out.println("inside save or update");
return repository.save(productUnit);
}
#Override
public List<ProductUnit> findAll() {
return repository.findAll();
}
#Override
public ProductUnit deleteUnitByPrice(String name) {
return repository.deleteByName(name);
}
#Override
public List<ProductUnit> findByPrice(Integer price) {
return repository.findByPrice(price);
}
}
Now , inside RestController , I pass id through a post request and use a random class to generate a random value of the price and name .At this stage everything is fine, i.e. all values were initialized correctly, but when it comes to service.saveOrUpdate(forSave) It stores the value incorrectly, i.e. the request returns an empty json and the findAll method returns a list of empty json.Can you tell me what the error is? thanks
#RestController
public class productUnitRestController {
#Autowired
productServise service;
#Autowired
Supplier<MetaInfGenerator> generatorSupplier;
#GetMapping(path = "/all")
public List<ProductUnit> getAllProoduct(){
return service.findAll();
}
#PostMapping(path = "/products")
public ProductUnit createProoduct(#RequestParam("id") Optional<String> newId){
System.out.println("***** iside PostMapping ******");
MetaInfGenerator generator = generatorSupplier.get();
System.out.println("***** supplier PostMapping ******");
ProductUnit forSave = ProductUnit.builder()
.id(newId.get())
.name(generator.getRandomString())
.price(generator.getRandomInteger())
.localDateTime(LocalDateTime.now()).build();
System.out.println(forSave);
return service.saveOrUpdate(forSave);
}
}

java.lang.ClassCastException: Entity A incompatible with Entity B

I'm trying to get proficient in generics in Java. I have some 100 entities that use the same findBy method in JPA interface. Almost all of them require a call to AwrSnapDetails so instead of adding
#ManyToOne private AwrSnapDetails awrSnapDetails; to each Entity, I've created a HelperEntity class and using #Embedded annotation. Now I have gotten to the point in coding where I can't figure out what I am doing wrong and how to go about resolving this error.
Entity
#Entity
public class AwrMemStats {
String description;
double begin_;
double end_;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
Long id;
#Embedded
private HelperEntity helperEntity;
public AwrMemStats() {
}
public AwrMemStats(String description, double begin_, double end_, AwrSnapDetails awrSnapDetails) {
this.description = description;
this.begin_ = begin_;
this.end_ = end_;
HelperEntity h = new HelperEntity(awrSnapDetails);
}
// getters/setters removed for clarity
}
Embedded Entity
#Embeddable
public class HelperEntity implements Serializable{
private static final long serialVersionUID = 1L;
#ManyToOne
AwrSnapDetails awrSnapDetails;
public HelperEntity() {
}
public HelperEntity(AwrSnapDetails awrSnapDetails) {
super();
this.awrSnapDetails = awrSnapDetails;
}
public AwrSnapDetails getAwrSnapDetails() {
return awrSnapDetails;
}
public AwrSnapDetails setAwrSnapDetails(AwrSnapDetails awrSnapDetails) {
return this.awrSnapDetails = awrSnapDetails;
}
}
Service Class
#Service
public class HelperService<T> {
#Autowired
private HelperRepository<T> repository;
public void add(T entity) {
repository.save(entity);
}
public void add(List<T> entities) {
repository.saveAll(entities);
}
public T get(T entity) {
T t = repository.findByHelperEntityAwrSnapDetailsStartSnapIdAndHelperEntityAwrSnapDetailsInstanceDetailDbNameAndHelperEntityAwrSnapDetailsInstanceDetailDbId(
((HelperEntity) entity).getAwrSnapDetails().getStartSnapId(),
((HelperEntity) entity).getAwrSnapDetails().getInstanceDetail().getDbName(),
((HelperEntity) entity).getAwrSnapDetails().getInstanceDetail().getDbId());
//((AwrMemStats) entity).getHelperEntity().getAwrSnapDetails().getStartSnapId(),
//((AwrMemStats) entity).getHelperEntity().getAwrSnapDetails().getInstanceDetail().getDbName(),
//((AwrMemStats) entity).getHelperEntity().getAwrSnapDetails().getInstanceDetail().getDbId());
if (t!= null) {
return t;
}
return null;
}
}
Controller
#RestController
public class HelperController<T> {
#Autowired
private HelperService<T> service;
public void add(T entity) {
service.add(entity);
}
public void add(List<T> entities) {
service.add(entities);
}
public T get(T entity) {
return service.get(entity);
}
}
Execution
getAwrSnapDetails() initilized in HelperLoader
#Component
public class LoadAwrMemStats extends HelperLoader{
#Autowired
private HelperController<AwrMemStats> controller;
public void doThis() {
AwrMemStats profile = new AwrMemStats("a",1.0,1.0,getAwrSnapDetails());
AwrMemStats s = controller.get(profile);
ANd finally the ERROR message
Exception in thread "restartedMain" java.lang.reflect.InvocationTargetException
...
...
Caused by: java.lang.ClassCastException: net.mharoon.perfmon.awr.entities.AwrMemStats incompatible with net.mharoon.perfmon.awr.entities.HelperEntity
at net.mharoon.perfmon.awr.service.HelperService.get(HelperService.java:27)
at net.mharoon.perfmon.awr.controller.HelperController.get(HelperController.java:24)
...
...
Update this code works but only for given class AwrMemStats.
public List<T> get(T entity) {
List<T> ts = repository.findByHelperEntityAwrSnapDetailsStartSnapIdAndHelperEntityAwrSnapDetailsInstanceDetailDbIdAndHelperEntityAwrSnapDetailsInstanceDetailDbName(
//((HelperEntity) entity).getAwrSnapDetails().getStartSnapId(),
//((HelperEntity) entity).getAwrSnapDetails().getInstanceDetail().getDbName(),
//((HelperEntity) entity).getAwrSnapDetails().getInstanceDetail().getDbId());
((AwrMemStats) entity).getHelperEntity().getAwrSnapDetails().getStartSnapId(),
((AwrMemStats) entity).getHelperEntity().getAwrSnapDetails().getInstanceDetail().getDbId(),
((AwrMemStats) entity).getHelperEntity().getAwrSnapDetails().getInstanceDetail().getDbName());
if (!ts.isEmpty()) {
return ts;
}
return null;
}
The reason is because you are returning an Object that is not AwrMemStats and assigning it to AwrMemStats.
A simple work around is to replace
public T get(T entity)
with
public <T extends AwrMemStats> T get(T entity)
EDIT : Another solution (which is more generic) is..
replace
public class AwrMemStats
with
public class AwrMemStats extends HelperEntity
then replace
AwrMemStats s = controller.get(profile);
with
AwrMemStats s = (AwrMemStats) controller.get(profile);

Adding content property to serialization

Whenever I use a custom serializer in spring data rest, it adds a "content" property that wrapps the object returned, like:
{
"content":{
object properties...
},
_links: {
}
}
EDIT: Add configuration class
#Configuration
public class JacksonCustomizations {
#Bean
public Module rateModule() {
return new RateModule();
}
#SuppressWarnings("serial")
static class RateModule extends SimpleModule {
public RateModule() {
setMixInAnnotation(Package.class, RateModule.PackageMixin.class);
setMixInAnnotation(Section.class, RateModule.SectionMixin.class);
setMixInAnnotation(MainPart.class, RateModule.MainPartMixin.class);
setMixInAnnotation(SubPart.class, RateModule.SubPartMixin.class);
addSerializer(MaintenanceTask.class, new MaintenanceTaskSerializer());
addDeserializer(Package.class, new PackageDeserializer());
addDeserializer(Section.class, new SectionDeserializer());
addDeserializer(MainPart.class, new MainPartDeserializer());
addDeserializer(MaintenanceTask.class, new MaintenanceTaskDeserializer());
}
#JsonAutoDetect(fieldVisibility=Visibility.NONE, getterVisibility=Visibility.NONE, isGetterVisibility=Visibility.NONE)
static abstract class PackageMixin {
#JsonProperty("name") public abstract String getName();
#JsonProperty("sections") public abstract List<Section> getSections();
}
#JsonAutoDetect(fieldVisibility=Visibility.NONE, getterVisibility=Visibility.NONE, isGetterVisibility=Visibility.NONE)
static abstract class SectionMixin {
#JsonProperty("id") public abstract Long getId();
#JsonProperty("name") public abstract String getName();
}
#JsonAutoDetect(fieldVisibility=Visibility.NONE, getterVisibility=Visibility.NONE, isGetterVisibility=Visibility.NONE)
static abstract class MainPartMixin {
#JsonProperty("name") public abstract String getName();
#JsonProperty("subparts") public abstract List<SubPart> getSubParts();
}
#JsonAutoDetect(fieldVisibility=Visibility.NONE, getterVisibility=Visibility.NONE, isGetterVisibility=Visibility.NONE)
static abstract class SubPartMixin {
#JsonProperty("id") public abstract Long getId();
#JsonProperty("name") public abstract String getName();
}
static class MaintenanceTaskSerializer extends JsonSerializer<MaintenanceTask> {
#Override
public void serialize(final MaintenanceTask value, final JsonGenerator gen, final SerializerProvider serializers)
throws IOException, JsonProcessingException {
gen.writeStartObject();
gen.writeNumberField("id", value.getId());
gen.writeStringField("maintenanceRequirementId", value.getMaintenanceRequirementId());
gen.writeStringField("type", value.getType().toString());
gen.writeStringField("title", value.getTitle());
gen.writeStringField("description", value.getDescription());
gen.writeStringField("note", value.getNote());
gen.writeStringField("effectivity", value.getEffectivity());
gen.writeNumberField("procedureReference", value.getReferenceTask().getId());
gen.writeNumberField("aircraftModel", value.getAircraftModel().getId());
gen.writeNumberField("packageId", value.getPack().getId());
gen.writeNumberField("sectionId", value.getSection().getId());
gen.writeStringField("taskType", value.getTaskType().toString());
gen.writeEndObject();
}
}
}
}
But when I use spring data rest serialization without custom serializers, the property is not inserted.
How can I prevent this property from showing?
This is an known issue.
It has been reported here: https://jira.spring.io/browse/DATAREST-504
The issue points to this stack overflow question: Different JSON output when using custom json serializer in Spring Data Rest

Spring Data REST Custom Resource URI works for String but not Long

I have a model:
public class MyModel {
#Id private Long id;
private Long externalId;
// Getters, setters
}
I'd like to use externalId as my resource identifier:
#Configuration
static class RepositoryEntityLookupConfig extends RepositoryRestConfigurerAdapter {
#Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration configuration) {
configuration
.withEntityLookup()
.forRepository(MyRepository.class, MyModel::getExternalId, MyRepository::findByExternalId);
}
}
If externalId is a String, this works fine. But since it's a number (Long)
public interface MyRepository extends JpaRepository<MyModel, Long> {
Optional<MyModel> findByExternalId(#Param("externalId") Long externalId);
}
when invoking: /myModels/1 I get:
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Long
at org.springframework.data.rest.core.config.EntityLookupConfiguration$RepositoriesEntityLookup.lookupEntity(EntityLookupConfiguration.java:213) ~[spring-data-rest-core-2.6.4.RELEASE.jar:na]
at org.springframework.data.rest.core.support.UnwrappingRepositoryInvokerFactory$UnwrappingRepositoryInvoker.invokeFindOne(UnwrappingRepositoryInvokerFactory.java:130) ~[spring-data-rest-core-2.6.4.RELEASE.jar:na]
at org.springframework.data.rest.webmvc.RepositoryEntityController.getItemResource(RepositoryEntityController.java:524) ~[spring-data-rest-webmvc-2.6.4.RELEASE.jar:na]
at org.springframework.data.rest.webmvc.RepositoryEntityController.getItemResource(RepositoryEntityController.java:335) ~[spring-data-rest-webmvc-2.6.4.RELEASE.jar:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_111]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_111]
...
A separate custom EntityLookupSupport<MyModel> component class works.
Am I missing something to get it working for Long using method references in my RepositoryRestConfigurerAdapter?
Try to add this to your RepositoryEntityLookupConfig class:
#Override
public void configureConversionService(ConfigurableConversionService conversionService) {
conversionService.addConverter(String.class, Long.class, Long::parseLong);
super.configureConversionService(conversionService);
}
Do you really need to set configuration by yourself ? You could try to use spring-boot auto-configuration by adding #RepositoryRestResource annotation
#RepositoryRestResource(collectionResourceRel = "myModels", path = "myModels")
public interface MyRepository extends JpaRepository<MyModel, Long> {
Optional<MyModel> findByExternalId(#Param("externalId") Long externalId);
}
Also add #Entity on your model class
#Entity
public class MyModel {
#Id
private Long id;
#Column(name = "EXTERNAL_ID")
// Column annotation is not required if you respect case-sensitive
private Long externalId;
// Getters, setters
}
Apparently, the default BackendIdConverter (see DefaultIdConverter) does nothing with ID conversion and on the other hand Spring Data Rest cannot use the repository ID type. So, you have to either convert it yourself or configure your custom ID converter bean, for example:
#Bean
public BackendIdConverter myModelBackendIdConverter() {
return new BackendIdConverter() {
#Override
public Serializable fromRequestId(final String id, final Class<?> entityType) {
return Optional.ofNullable(id).map(Long::parseLong).orElse(null);
}
#Override
public boolean supports(final Class<?> delimiter) {
return MyModel.class.isAssignableFrom(delimiter);
}
#Override
public String toRequestId(final Serializable id, final Class<?> entityType) {
return Optional.ofNullable(id).map(Object::toString).orElse(null);
}
};
}
See also:
BackendIdHandlerMethodArgumentResolver
#BackendId
The signature of the method you are trying to call seems to be:
forRepository(Class<R> type, Converter<T,ID> identifierMapping,
EntityLookupRegistrar.LookupRegistrar.Lookup<R,ID> lookup)
I don't see how MyModel::getExternalId can be doing the necessary conversion.
I would try something like the following:
#Configuration
static class RepositoryEntityLookupConfig extends RepositoryRestConfigurerAdapter {
#Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration configuration) {
configuration
.withEntityLookup()
.forRepository(MyRepository.class, Long::parseLong, MyRepository::findByExternalId);
}
}

Resources