In my Spring boot apps, I use a custom class for mapping from entity to dto or vice versa. On the other hand, I know there are some better options e.g. ModelMapper or Converter class.
After my searches, I have seen some good examples as shown below:
Automatically Mapping DTO to Entity on Spring Boot APIs
It sounds good, but as I have no previous experience, I wanted to be clarified about some points before proceeding. Could you help me please?
1. Is that solution is a proper ModelMapper example and can I also apply that approach for mapping entity to DTO?
2. What about implementing Converter interface without using a 3rd party library? Can I also build a generic mechanism using Converter interface?
Note: I also considered using MapStruct or JMapper, but ModelMapper sounds easier to use and for this reason I decided to use ModelMapper. But if you have some suggestion that clearly shows one of them has more advantegous, feel free to share your suggestions. I could also consider to use it.
Any help would be appreciated.
From my expérience, this is my suggestion. You have 1 entity and 1 entityDTO.
You make a constructor of entity that takes in parameter the entity DTO. Then you make a constructor for entityDTO that takes in parameter an entity.
Finnaly, you create a mapper class for a given element that will use the 2 constructors to generate what you need. Here is an exemple :
Entity.java :
#Entity
#Getter
#Setter
#NoArgsConstructor
#Table(name="entity")
public class Entity{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id_entity;
#Column(name = "name")
private String name;
public Entity(EntityDTO entityDTO){
this.setId_entity(entityDTO.getId_entity())
this.setName(entityDTO.getName())
}
}
EntityDTO.java :
#Getter
#Setter
#NoArgsConstructor
public class EntityDTO{
private Long id_entity;
private String name;
public EntityDTO(Entity entity){
this.setId_Entity(entity.getId_entity())
this.setName(entity.getName())
}
}
EntityMapper.java :
#Component
public class EntityMapper{
public EntityDTO toDto(Entity entity){
return new EntityDTO(entity);
}
public Entity toEntity(EntityDTO entityDTO){
return new Entity(entityDTO);
}
}
These anotations are from lombok library, used for time saving :
#Getter
#Setter
#NoArgsConstructor
Then you can just make requests to Database to get entities, and place the flow of answers through the mapper to convert them to EntityDTO and send them to where ever you need. In your controller :
#Autowired
EntityMapper mapper;
In your functions, you can return :
service.getEntities().stream.map(mapper::toDTO).collect(Collectors.toList();
Related
I have confusion in using Data annotation to DTO class. Here is my sample class.
#Data
public class MyClass {
private Long id;
private String name;
}
I've read online that using Data annotation specifically on DTO class will allow the values of the fields to be changed by use of the generated setters.
Should I remove the lombok Data annotation? And implement the getters and setters manually.
Thanks :)
I would avoid #Data for DTOs since it has too many pitfalls. First of all as you mentioned it is mutable which you don't really want for a DTO. And despite it being mutable, it implements equals() and hashCode() which is only asking for trouble.
You can use #Value for an immutable DTO. For an incoming DTO you may need to add lombok.anyConstructor.addConstructorProperties to your lombok.config, which will allow libraries like jackson to deserialize to your POJO without a default constructor.
The annotation #Data comes from the Project Lombok which is designed to use reflection via annotations mostly. This annotation assures generation of all the setters, getters, a constructor with all the required arguments and overridden Object::toString, Object::equals and Object::hashCode methods.
Briefly said, this annotation "completes" a simple POJO object and generates all the boilerplate without a need to use IDE.
They named the annotation #Data because they support the idea of the understanding objects as data containers only.
As far as I understand, the generation happens only for the missing getters/setters (let's speak about them for brevity). The generated getters/setters are in their pure form as you know:
public int getId() { return this.id; }
public void setId(int id) { this.id = id; }
You can use more verbose setter/getter performing validation or anything similar which override the generated ones. You can both use #Data annotation and write your ones manually.
DTO is used to transmit data information, some information is actually we do not want users to be able to change or access, such as the user password, we do not want to pass to the front end when the user can see the encrypted password, or we do not want users to modify the password while changing their information, and what works in this serialization process is setter and getter, and data annotations that automatically generate getters and setters for all fields.
For example
#Data
class User{
private String userName;
private String pwd;
}
This class, will have all setter and getter. When you trans to web, you will see
{userName: "123", pwd: "xxx"}
This is terrible.
But if you use DTO
class User{
private String userName;
private String pwd;
public String getUserName(){
return userName;
}
}
They only see
{userName: "123"}
By default the #Data lombok annotation will generate setters and getters for all fields in the class.
If you want an immutable data transfer object, annotate it as #Value instead.
If you want a mixure of some immmutable values and some mutable values in your MyClass type, for instance you might want the id field to be immutable and the rest mutable, you would use the #Setter annotation on the field you want to be immutable, specifying an AccessLevel of NONE. For instance:
#Data
public class MyClass {
#Setter(AccessLevel.NONE)
private Long id;
private String name;
}
This will generate a getter but no setter for the id, and a getter and setter for the name.
I'm trying to create JPA entities by using inheritance , I am not using any JPA polymorphic mechanism to do this. The reason is I want model classes to be independent, so if I want to use JPA I can extend the same model classes and create JPA entities and get the job done. My question is, is this possible to achieve without using JPA polymorphic mechanism, because when I try to deal with the JPA entities created after extending the model classes I don't see the properties that are inherited from super class but I can see new properties in the table if I add new properties in to the extended JPA entity.
Here are my entities:
#Data
public abstract class AtricleEntity {
protected Integer Id;
protected String title;
protected Integer status;
protected String slug;
protected Long views;
protected BigDecimal rating;
protected Date createdAt;
protected Date updatedAt;
}
#Data
#Entity
#Table(name="articles_article")
#RequiredArgsConstructor
public class Article extends AtricleEntity {
public static final String TABLE_NAME = "articles_article";
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer Id;
private String title;
}
#Repository
public interface ArticleRepository extends JpaRepository<Article, Integer>{}
I can see a table with a column title created if i run this. that's because I've explicitly added that property in Article , but i want other columns to appear in the table with the help of java inheritance. is this possible?
Simple answer is NO. JPA cannot use object's inheritance out of the box coz of the simple reason that other children will have different column names and other parameters and might choose not even to save these columns.
So JPA has it's own inheritance mappings which an object might have to follow. Usage like MappedSuperclass might help.
Reference : http://www.baeldung.com/hibernate-inheritance for hibernate.
#MappedSuperclass annotation put on your super class should help.
https://docs.oracle.com/javaee/5/api/javax/persistence/MappedSuperclass.html
In FindBugs, I am getting an issue like FCBL_FIELD_COULD_BE_LOCAL on the class name line of this code:
#Entity
#Table(name = "Student")
#Immutable
#Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
public class RetrievableStudent extends BaseStudent
{
#Id
#Column(name = "STUDENT_ID")
private long studentId;
#Column(name = "STUD_NOTE")
private String studenetNote;
}
How can I resolve this issue?
In order to resolve that issue you need to use you fields somewhere in your class. What FindBugs is telling you is that your fields in your class are never used as the fields.
How you using your fields that were retrieved from database? Maybe you need to add getters? For now, your fields are useless (unless you don't using them with reflection, which is not good).
My POJO has already getters setters, equals, hashcode.
It worked when I tried adding #JsonProperty annotation, as my POJO is built for API response purposes.
If the POJO is for ORM purposes (means database entity) the on getters and setters #Column annotation should work.
I have hibernate pojo class which has a ManytoOne relation with another class.
class Employee {
#OneToMany
private String id;
}
class ITEmployee {
private Employee employee;
#ManyToOne
#JoinColumn(name="EMPLOYEE_ID)
public Emplyee getEmployee() {
return employee;
}
}
Now when I retrieve a row and marshall to a JSON/XML REST response, I get nested object of Employee class and ITEmployee class within each object.
Like eg,
{"ITEmployee":[{"id":1234,"Employee":[{"id":222, "ITEmployee":{"id":1234,"Employee":[{"id":222, "Employee":[{"id":222, "ITEmployee": . .. .
and so on.
How can I ignore the ManytoOne relation while marshalling?
I don't want to create another class and map them seperately.
I tried using #JsonIgnore and #Transient but that didn't work.
REST API : JAX-RS
Cheers!!
You can tell Jackson to not marshall some fields.
You have multiple choices. The simpliest is to use #JsonIgnore annotation on your employee Field.
If you want more advanced features, you can check for #JsonView.
EDIT : I see you already tried to use #JsonIgnore. Can you paste your code ? In principe it must work.
I'm having trouble figuring out exactly how to use the #RepositoryRestResource interface to create many-to-many relationships between two fairly simple entities.
For example, I have a simple parent-child entity relationship like this:
#Entity
public class ParentEntity {
#Id
#GeneratedValue
private Long id;
#ManyToMany
private List<ChildEntity> children;
}
#Entity
public class ChildEntity {
#Id
#GeneratedValue
private Long id;
#ManyToMany(mappedBy="children")
private List<ParentEntity> parents;
}
My repositories are using the vanilla Spring #RepositoryRestResource HATEOS API:
#RepositoryRestResource(collectionResourceRel = "parents", path = "parents")
public interface ParentRepository extends PagingAndSortingRepository<ParentEntity, Long> {
}
#RepositoryRestResource(collectionResourceRel = "children", path = "children")
public interface ChildRepository extends PagingAndSortingRepository<ChildEntity, Long> {
}
I’ve been successful in using POST to create the individual ParentEntity and ChildEntity but I can’t seem to figure out how to PUT/PATCH the relationships between the two using the built-in interface.
It seems like I should be able to use a PUT to send JSON to something like http://localhost:8080/api/parents/1/children, but so far I'm not finding a structure that works.
I found an answer here: How to update reference object in Spring-data rest?
By using "Content-Type: text/uri-list" instead of JSON, it is possible to "add" a resource to the collection with a PUT and pass in the URI. You can remove the resource with a DELETE.
After some digging, I discovered that the Spring documentation does describe this: http://docs.spring.io/spring-data/rest/docs/2.2.0.RELEASE/reference/html/#repository-resources.association-resource.
I always hated that text/uri-list content-type, so I did some research and it turned out there is also an undocumented JSON format which can be used:
{
"_links":{
"rel":"/555",
"rel":"/556"
}
}
The rel of the links could be anything except empty string, they could be all the same. The link part could be the whole URL form the self link of the referenced object, but the last part of the URL is enough. ( forseslash +id)