Mapstruct passing enclosed object to the mapping method - mapstruct

Is there a way to pass the enclosed object reference as a parameter to the mapping method?
public class Car {
public int id;
public Person driver;
}
public class Person {
public int carId;
}
I need a reference to the enclosing car object when mapping the person object in the personToPersonDtoAfterMapping custom method.
#Mapper
public abstract class CarMapper {
public abstract CarDto carToCarDto(Car car);
public abstract PersonDto personToPersonDto(Person person);
#AfterMapping
protected void personToPersonDtoAfterMapping(Person person, #MappingTarget PersonDto dto, Car enclosedCar) {
dto.setCarId(enclosedCar.getId())
}
}

Simply use #Context annotation. Did you try?
#Mapper
public abstract class CarMapper {
public abstract CarDto carToCarDto(Car car);
public abstract PersonDto personToPersonDto(Person person, #Context Car enclosedCar);
#AfterMapping
protected void personToPersonDtoAfterMapping(Person person, #MappingTarget PersonDto dto, #Context Car enclosedCar) {
dto.setCarId(enclosedCar.getId())
}
}

Related

C# issue with class instantiation

I'm running a C# project on VS2019 with the following code structure:
In the Class1.cs file:
public class Class1
{
public class MyClass2 : Class2
{
...
}
private void RunAlgorithm<T>() where T : Class2, new()
{
T argInstance = new T();
...
}
static void Main(string[] args)
{
RunAlgorithm<MyClass2>();
}
}
In the Class2.cs file:
public class Class2
{
public Class2() { }
public string setParameters { get; set; }
}
I'm getting the following error for the line RunAlgorithm<MyClass2>();
'Class1.MyClass2' must be a non-abstract type with a public
parameterless constructor in order to use it as parameter 'T' in the
generic type or method 'Class1.RunAlgorithm()'
even if I change it to Public, the error persists
Well, minimally, it'll have to be protected so that MyClass can access it..
https://dotnetfiddle.net/XFeEdQ
public class Class1
{
class MyClass2 : Class2
{
}
private void RunAlgorithm<T>() where T : Class2, new()
{
T argInstance = new T();
}
public static void Main(string[] args)
{
new Class1().RunAlgorithm<MyClass2>();
}
}
public class Class2
{
protected Class2() { }
public string setParameters { get; set; }
}
So your "Class1.MyClass2
must have a public parameterless constructor" message is saying that your MyClass needs a constructor. Mine above has such a constructor even though it's not in the code; in the absence of the developer providing a constructor the compiler provides one that does nothing other than call the base parameterless constructor...
...which leads me to the next point; your MyClass2 extends Class2, and hence Class2's constructor needs to be accessible to it. While Class2's constructor is private, MyClass2's constructor can't call it. Every constructor on c# has to either call another constructor or a base constructor. If you don't specify which, the compiler will insert a call to base() for you, which will fail if the base constructor is inaccessible
For this all to work out you need a public parameterless constructor in MyClass2:
public MyClass2():base(){}
or without the base(compiler will add the base call)
or blank (compiler will add all of it)
and you need something that makes Class2's constructor accessible to MyClass2, ie declaring Class2's constructor as public or protected

How to prevent creation of empty objects

I'm trying to map a model of a webservice where every List is inside nested object to something more simple.
Model 1
public class Parent {
private Children children;
}
public class Children {
private List<Child> children;
}
public class Child {
}
Model 2(simplified)
public class Parent2 {
private List<Child2> children;
}
public class Child {
}
The mapping is pretty straightforward:
#Mappings({#Mapping(source = "entity.children.children", target = "children")})
Parent2 parentToParent2(Parent entity);
#InheritInverseConfiguration
Parent parent2ToParent(Parent2 entity);
Mapping works fine except for one problem. When I map Parent with null children to Parent2 and back to Parent, the Children object is created with empty list. Is there some way to prevent that?
You can achieve that with both a mapper decorator or an AfterMapping hook.
Decorator
Decorator:
public abstract class MapperDecorator implements Mapper {
private final Mapper delegate;
public MapperDecorator(Mapper delegate) {
this.delegate = delegate;
}
#Override
public Parent parent2ToParent(Parent2 entity) {
Parent parent = delegate.parent2ToParent(entity);
if (entity.getChildren() == null) {
parent.setChildren(null);
}
return parent;
}
}
Mapper:
#org.mapstruct.Mapper
#DecoratedWith(MapperDecorator.class)
public interface Mapper {
#Mapping(source = "entity.children.children", target = "children")
Parent2 parentToParent2(Parent entity);
#InheritInverseConfiguration
Parent parent2ToParent(Parent2 entity);
Child2 childToChild2(Child entity);
Child child2ToChild(Child2 entity);
}
AfterMapping
Mapper:
#org.mapstruct.Mapper
public abstract class Mapper {
#Mapping(source = "entity.children.children", target = "children")
public abstract Parent2 parentToParent2(Parent entity);
#InheritInverseConfiguration
public abstract Parent parent2ToParent(Parent2 entity);
public abstract Child2 childToChild2(Child entity);
public abstract Child child2ToChild(Child2 entity);
#AfterMapping
public void afterParent2ToParent(Parent2 source,
#MappingTarget Parent target) {
if (source.getChildren() == null) {
target.setChildren(null);
}
}
}

MapStruct bulk conversion with #aftermapping

I want to apply a decoration using a dedicated #AfterMapping after the single item conversion of a DTO and another dedicated #AfterMapping when dealing with its collection conversion flavor but not both.
public abstract CatUI convert(Cat cat);
public abstract List<CatUI> convert(List<Cat> cats);
#AfterMapping
public void populateCatName(Cat cat, #MappingTarget CatUI catUI) {
String name = _someRemoteService.getCatName(catUI.getId());
catUI.setName(name);
}
#AfterMapping
public void populateCatNames(List<Cat> cats, #MappingTarget List<CatUI> catUIs) {
Map<Integer,String> idToNameMap = _someRemoteService.getCatNames(catUIs.stream().map((c) -> c.getId() ).collect(Collectors.toList());
catUIs.forEach((c) -> c.setName(idToNameMap(c.getId())));
}
I don't want populateCatName to be called when dealing with List conversion and hence duplicate my decoration.
Anyway to do this?
With 1.1.0.Final you will have to define 2 entry points (Mappers) one with the conversion on list and the other one without it.
I would suggest you try out the new 1.2.0.Beta2. With that one you can use the new #Context.
You can have an interface like:
public interface CatMappingContext {
#AfterMapping
public default void populateCatName(Cat cat, #MappingTarget CatUI catUI) {
//nothing per default
}
#AfterMapping
public void populateCatNames(List<Cat> cats, #MappingTarget List<CatUI> catUIs) {
//nothing per default
}
}
And 2 implementations:
public class SingleMappingContext implements CatMappingContext {
#AfterMapping
public void populateCatName(Cat cat, #MappingTarget CatUI catUI) {
String name = _someRemoteService.getCatName(catUI.getId());
catUI.setName(name);
}
}
public class ListMappingContext implements CatMappingContext {
#AfterMapping
public void populateCatNames(List<Cat> cats, #MappingTarget List<CatUI> catUIs) {
Map<Integer,String> idToNameMap = _someRemoteService.getCatNames(catUIs.stream().map((c) -> c.getId() ).collect(Collectors.toList());
catUIs.forEach((c) -> c.setName(idToNameMap(c.getId())));
}
}
Finally your mapper can look like:
public interface CatMapper {
public CatUI convert(Cat cat, #Context CatMappingContext context);
public List<CatUI> convert(List<Cat> cats, #Context CatMappingContext context);
}
You will then need to call your methods with the correct instance of the context SingleMappingContext or the ListMappingContext.

How is entities inheritance implemented in Spring data mongodb

I've two entities Person, Employee and Employee1. I want to implement entities inheritance in Spring Data MongoDB. Like in Spring Data JPA, what are the equivalent annotations for #Inheritance and #PrimaryKeyJoinColumn in Spring Data MongoDB. Right now, I've implemented something like this:
interface Person {
String getId();
void setId(String id);
String getName();
void getName(String name);
}
#Document(collection = "person")
class PersonImpl implements Person {
#Id
String id;
// Getters and setters
// Constructors, equals, hashcode and toString methods
}
interface Employee extends Person {
int getNumberOfDependents();
void getNumberOfDependents(int numberOfDependents);
}
#Document(collection = "employee")
class EmployeeImpl extends PersonImpl implements Employee {
// Getters and setters
// Constructors, equals, hashcode and toString methods
}
interface Employee1 extends Person {
int getNumberOfDependents();
void getNumberOfDependents(int numberOfDependents);
}
#Document(collection = "employee1")
class Employee1Impl extends PersonImpl implements Employee1 {
// Getters and setters
// Constructors, equals, hashcode and toString methods
}
Repository structure:
public interface PersonRepository extends MongoRepository<Person, String> {
}
public interface EmployeeRepository extends MongoRepository<Employee, String> {
}
public interface Employee1Repository extends MongoRepository<Employee1, String> {
}
I'm saving the Person object first and then taking the ID of it and creating an Employee object with the same ID and saving it. This creates new object and hence I'm losing all the Person object stuff.
I also feel that I've to get the NoRepositoryBean implemented also.
I'm confused. Please help.
Here is one approach:
#Document(collection = "person")
class Person {
#Id
String id;
// Getters and setters
// Constructors, equals, hashcode and toString methods
}
Note that the collection field refers to "person" and not to "employee"
#Document(collection = "person")
class Employee extends Person {
String jobTitle;
// Getters and setters
// Constructors, equals, hashcode and toString methods
}
In this method you do not need to create a repository for each derived class
#Repository
public interface PersonRepository extends MongoRepository<Person, String> {}
Code example:
#Autowired
private PersonRepository personRepo;
public void test() {
Employee employee = new Employee("1", "teacher")
personRepo.save(employee)
Optional<Person> optionalPerson = personRepo.findById("1");
Employee employeeFromDb;
if (optionalPerson.isPresent()) {
employeeFromDb = (Employee)optionalPerson.get()
}
else {
// could not find in db
}
}
if you want to find all employees you should have a methode on MongoRepository
called
List<Employee> findAll();

Morphia: inheritance not handled properly?

I have a class that implements an interface. Why are the arraylist contents not stored in the database? Here is some code to illustrate the problem.
The class
#Entity
public class MyClass implements MyInterface {
#Id
#Indexed
public String id;
public String someField;
public MyClass(String id, String someField){
this.id = id;
this.someField = someField;
}
}
The interface
public interface MyInterface {
#Embedded
public List<String> mylist = new ArrayList<String>();
}
Test code
#Test
public void test() {
testInheritance();
}
public void testInheritance() {
MyClass myClass = new MyClass("test", "someField");
myClass.myList.add("wow");
MyClassDao dao = new MyClassDao();
dao.save(myClass);
}
public class MyClassDao extends BasicDAO<MyClass, ObjectId> {
public MyClassDao() {
super(MyClass.class, MorphiaManager.getMongoClient(), MorphiaManager.getMorphia(), MorphiaManager.getDB().getName());
}
}
Result in DB
{
"_id" : "test",
"className" : "gr.iti.mklab.simmo.util.MyClass",
"someField" : "someField"
}
Interfaces can only declare method signatures and constants (static final variables). What you want to use is an abstract base class from which you inherit.
Additional observations from your code:
The id should be ob the type ObjectId and is automatically indexed, you don't need the #Indexed
Attributes should be private or protected and you need to provide getters and setters for them
You need a default no-arg constructor in your entity class