ModelMapper Matching Strategy: Standard with Reference Types - modelmapper

Entity Classes:
class User{
private Name name;
private int age;
private String email;
private Date dob;
private Address address;
// No Arguments Constructor , All Arguments Constructor , Setters, Getters and toString
}
class Name {
private String firstName;
private String lastName;
// No Arguments Constructor , All Arguments Constructor , Setters, Getters and toString
}
class Address {
private String houseNo;
private String street;
private String city;
private Integer pincode;
// No Arguments Constructor , All Arguments Constructor , Setters, Getters and toString
}
DTO:
class UserDTO{
private String firstName;
private String lastName;
private int age;
private String email;
private Date dob;
private String houseNo;
private String street;
private String city;
private Integer pincode;
// No Arguments Constructor , All Arguments Constructor , Setters, Getters and toString
}
Code to convert Entity to DTO:
public class ReferenceTypePropertiesMapper {
#Test
public void shouldPopulateAllSimpleProperties(){
User user = createUser();
ModelMapper modelMapper = new ModelMapper();
UserDTO userDTO = modelMapper.map(user,UserDTO.class);
System.out.println("Source : "+ user);
System.out.println("Destination : "+ userDTO);
}
private User createUser(){
Name name = new Name("Siva", "Prasad");
Address address = new Address("1-93","ABC","HYD",123456);
return new User(name, 29, "Siva#gmail.com", new Date(), address);
}
}
Output:
Source : User(name=Name(firstName=Siva, lastName=Prasad), age=29, email=Siva#gmail.com, dob=Tue Sep 26 14:38:45 IST 2017, address=Address(houseNo=1-93, street=ABC, city=HYD, pincode=123456))
Destination : UserDTO(firstName=Siva, lastName=Prasad, age=29, email=Siva#gmail.com, dob=Tue Sep 26 14:38:45 IST 2017, houseNo=null, street=null, city=null, pincode=null)
I am taking 2 reference types Name and Address in User.java.
While creating object for User , I am passing both Name and Address details as well. When I try to map User object to UserDTO, Name details are getting mapped successfully, but Address details are not getting mapped.
Can any body help me in understanding why its happing like that or am I missing any thing?

With MatchingStrategies.LOOSE everything works well.
The Loose matching strategy allows for source properties to be loosely matched to destination properties by requiring that only the last destination property in a hierarchy be matched. The following rules apply:
Tokens can be matched in any order
The last destination property name must have all tokens matched
The last source property name must have at least one token matched
The loose matching strategy is ideal to use for source and destination object models with property hierarchies that are very dissimilar. It may result in a higher level of ambiguous matches being detected, but for well-known object models it can be a quick alternative to defining mappings.
In this way it is necessary to add only one line:
#Test
public void shouldPopulateAllSimpleProperties() {
User user = createUser();
ModelMapper modelMapper = new ModelMapper();
modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.LOOSE);
UserDTO userDTO = modelMapper.map(user, UserDTO.class);
System.out.println("Source : " + user);
System.out.println("Destination : " + userDTO);
}
Output:
Source : User{name=Name{firstName='Siva', lastName='Prasad'}, age=29, email='Siva#gmail.com', dob=Wed Oct 18 23:44:25 MSK 2017, address=Address{houseNo='1-93', street='ABC', city='HYD', pincode=123456}}
Destination : UserDTO{firstName='Siva', lastName='Prasad', age=29, email='Siva#gmail.com', dob=Wed Oct 18 23:44:25 MSK 2017, houseNo='1-93', street='ABC', city='HYD', pincode=123456}

Related

Flutter/Dart: get and set with private variables

if I read that setters and getters have to be explicity developed in Dart only when you want to do something more than only retrieve those values. But if I have private variables, like:
Class User {
User _user;
String _password;
}
How can I access to those private variables?
Even if I implement the set password like
set password(String value) => _password = value;
It will of course give me an "error".
If you want public getter/setter what's the point of having private variable for that?
Just make it a public variable and be done.
If you insist having a private variable with public access, then you still need to add the getter and setter.
Private variable is import due to some reasons, If you conditionally set or get your Class property values then setter and getter is important on private variables. Example given below:
class User {
int _id;
String _firstName;
String _password;
int get id => _id;
set id(int value) {
_id = value;
}
String get firstName => _firstName;
set firstName(String value) {
if(value.length > 7)
_firstName = value;
}
String get password => _password;
set password(String value) {
if(some condition against this value like password)
_password = value;
}
}
Note: you can set condition in getter like setter that I have given in above example.

Handling aggregate root

I'm new in DDD so I'm doing some practice to undertand a little bit more. I have Course BC with the follow rules:
Course has to be created first and then they can create the modules of one course
Every module will be finished by the user when he upload the homework
The course will be finished by the user when he finished all the modules
Definition:
A course covers a particular topic and it is comprised of module. For instance, sap course has 10 modules such as: module 1: what is it?, module 2: how to use it?…
After this, I realize that Course is the aggregate root of module, because the modules are finished I have to close the status of user with the course.
the model would be:
public class Course : AggregateRoot
{
private string title;
private List<Module> modules;
}
but also module is an aggregate root of homework because when the user upload his homework the module has to be closed. This make me think that this approach is wrong because is not possible in DDD have nested aggregate root. Someone knows what is it wrong?
[UPDATED]
Ok, now I understand how is work and why you split it in 2 BC. However I did some changes and some questions come to my mind.
-I've created enroll method as static and I put the constructor as private.
-Course have to be an array because one student can have more than one.
-I've put more parameters related with the course and also the teacher. Is the teacher and entity of course, right?
-I created status of course to update it when the module is finished this way I don't have to read all the modules to know it. is ok?
-How can I pass more information for every module like title and description? and is the course entity how create all the modules, right?
public class StudentEnrolment: AggregateRoot
{
private StudentId studentId;
private Course courses;
private constructor(
StudentId studentId,
Course course,
){
this.studentId= studentId;
this.courses[] = course;
}
public statuc function enroll(
StudentId studentId,
CourseId courseId,
string courseTitle,
string courseLink,
string teacherId,
string teacherName,
List<Tuple<ModuleId, string>> modules) {
teacher = new Teacher(...);
courseStatus = new courseStatus();
new course(courseTitle, courseLink, courseStatus, teacher);
return new self(studentId, course);
}
public function void uploadModuleHomework(ModuleId moduleId, Homework homework){
/* forward to course.uploadModuleHomework */
}
public boolean isCourseFinished(){
/* forward to course.isFinished */
}
public List<Tuple<ModuleId, string>> getModules(){
/* forward to course.getModules */
}
}
There are two different sub-domains (so we have two bounded contexts):
1.Courses and modules administration where the teachers can administer those; Here Course and Module can be Aggregate roots and a course could hold references to the Modules IDs (not to instances!).
public class Course: AggregateRoot
{
private string title;
private List<ModuleId> modules;
}
2.Student participations to the courses. Here there is a StudentEnrolment Aggregate root that contains references to the Course and Module from the other BC but as Value objects; it models the student participation to a single course; in this bounded context there is a new Entity, Homework, that track the student homework-upload and course participation status.
public class StudentEnrolment: AggregateRoot
{
private StudentId studentId;
private Course course;
private List<Homework> homeworks;
// initialize a student enrolment as public constructor or make constructor private and use a static method
// here is important to notice that only this AR creates its entities, it does not receive them as parameter
public constructor(
StudentId studentId,
Course course,
List<Module> modules){
this.studentId = studentId;
this.course = course;
//build the the homeworks entity list based on the modules parameter
//for each module create a Homework entity, that initially is not uploaded, like:
this.homeworks = modules.map(module => new Homework(module))
}
public function void uploadFileForHomework(ModuleId moduleId, string file){
/* find the homework by module Id and upload file*/
}
public boolean isCourseFinished(){
/*returns true if all homeworks are uploaded*/
/*optimization: you could have a status that is updated when a homework's file is uploaded*/
}
public List<Tuple<ModuleId, string, boolean>> getHomeworks(){
/* returns a list of readonly Homeworks, i.e. Tuple<ModuleId, string /*module title*/, boolean /*is uploaded*/> */
}
}
public class Homework: Entity
{
private Module module;
private string file;
public constructor(Module module){
this.module = module;
}
public void upload(string file){ this.file = file;}
public boolean isUploaded(){return (boolean)this.file;}
public string getUploadedFile(){return this.file;}
public ModuleId getModuleId(){return this.module.getId();}
}
public class Course: ValueObject
{
private string title;
private CourseId id;
public constructor(id, title){...}
public string getTitle(){return this.title;}
public string getId(){return this.title;}
}
public class Module: ValueObject
{
private string title;
private string description;
private ModuleId id;
public constructor(id, title, description){...}
public string getTitle(){return this.title;}
public string getDescription(){return this.description;}
public string getId(){return this.title;}
}
If you need to query the Enrolment to get the homeworks you should not return a list of Homeworks because the client code would think that it can call Homework.upload(file) directly, which is not permitted (only the Aggregate root can modify its internal entities). Instead, you could return a Tuple or better, you can create an immutable version of the Homework class.

How to convert Cassandra UDT to Optional type

I have a User table and its corresponding POJO
#Table
public class User{
#Column(name = "id")
private String id;
// lots of fields
#Column(name = "address")
#Frozen
private Optional<Address> address;
// getters and setters
}
#UDT
public class Address {
#Field(name = "id")
private String id;
#Field(name = "country")
private String country;
#Field(name = "state")
private String state;
#Field(name = "district")
private String district;
#Field(name = "street")
private String street;
#Field(name = "city")
private String city;
#Field(name = "zip_code")
private String zipCode;
// getters and setters
}
I wanna convert UDT "address" to Optional.
Because I use "cassandra-driver-mapping:3.0.0-rc1" and "cassandra-driver-extras:3.0.0-rc1", there are lots of codec I can use them.
For example: OptionalCodec
I wanna register it to CodecRegistry and pass TypeCodec to OptionalCodec's constructor.
But TypeCodec is a abstract class, I can't initiate it.
Someone have any idea how to initiate OptionalCodec?
Thank you, #Olivier Michallat. Your solution is OK!
But I'm a little confused to set OptionalCodec to CodecRegistry.
You must initial a session at first.
Then pass session to MappingManager, get correct TypeCodec and register codecs.
It's a little weird that you must initial session at first, in order to get TypeCodec !?
Cluster cluster = Cluster.builder()
.addContactPoints("127.0.0.1")
.build();
Session session = cluster.connect(...);
cluster.getConfiguration()
.getCodecRegistry()
.register(new OptionalCodec(new MappingManager(session).udtCodec(Address.class)))
.register(...);
// use session to operate DB
The MappingManager has a method that will create the codec from the annotated class:
TypeCodec<Address> addressCodec = mappingManager.udtCodec(Address.class);
OptionalCodec<Address> optionalAddressCodec = new OptionalCodec(addressCodec);
codecRegistry.register(optionalAddressCodec);
Not really an answer but hope it helps. I couldn't make the Optional work with UDT in scala. However List and Array are working fine:
Here is a scala solution for driver version 4.x:
val reg = session.getContext.getCodecRegistry
val yourTypeUdt: UserDefinedType = session.getMetadata.getKeyspace(keyspace).flatMap(_.getUserDefinedType("YOUR_TYPE")).get
val yourTypeCodec: TypeCodec[UserDefinedType] = reg.codecFor(yourTypeUdt)
reg.asInstanceOf[MutableCodecRegistry].register(TypeCodecs.listOf(yourTypeCodec))
Don't forget to use java.util.* instead of your normal scala types.

Creating object in entity after all instance variables were set by Eclipse Link

The following JPA entity is given:
#Entity(name = "MyEntity")
public class MyEntity {
#Id
protected String id = null;
protected String name = null;
protected String adress = null;
#Transient
protected User user = new User(name, adress);
// Required by JPA
protected MyEntity() {
}
public MyEntity(String id, String name, String adress) {
// assign instance variables
}
public String getUser() {
return user.toString();
}
...
}
When getUser() will be called on MyEntity created by Eclipse Link the desired String will not be returned. The problem is that User was instantiated before the instance variables of MyEntity were set by Eclipse Link. In other words User was created with name = null and adress = null.
How can I ensure that User will be created after Eclipse Link has set all instance variables?
You can use the #PostLoad annotation like this:
#PostLoad
private initUser(){
user = new User(name, adress);
}
The method will be executed once your entity is fully loaded and it's fields are set.

JAXB default behavior for attributes

in JAXB, if annotations were not provided, the element names will be derived from the property names not fields, but what about attributes in this case? is there any default behavior for writing out attributes back to the XML file?
1) If annotations were not provided:
Every public getter/setter pair and every public field will be
automatically bound to XML, unless annotated by {#link XmlTransient}
For example
public class Cat
{
public String name = "tomcat";
private String nick = "catalina";
public int getAge() { return 5; }
public void setAge(int age) {}
}
after
JAXB.marshal(cat, System.out);
output is
<cat>
<name>tomcat</name>
<age>5</age>
</cat>
2) What about XML attributes? XML attribute is named like field or getter/setter pair and placed in the root node
for example
#XmlAccessorType(XmlAccessType.FIELD)
public class Cat
{
String name = "tomcat";
#XmlAttribute
String nick = "catalina";
#XmlAttribute
String home = "java.home";
int age = 5;
}
output is
<cat home="java.home" nick="catalina">
<name>tomcat</name>
<age>5</age>
</cat>

Resources