Conversion of DTO to entity and vice-versa - java

I am using Spring MVC architecture with JPA in my web application. Where to convert data transfer object (DTO) to JPA entity and vice-versa, manually (that is, without using any framework)?

This is an old question with accepted answer but thought to update it with easy way of doing it using model-mapper API.
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>0.7.4</version>
</dependency>
Using this API, you avoid manual setter & getters as explained in accepted answer.
In my opinion, both conversions should happen at controller with the help of private utility methods and using Java8 stream's map ( if a Collection of DTOs is exchanged ) like illustrated in this article.
It should happen at controller because DTOs are meant to be exclusive transfer objects. I don't take my DTOs further way down.
You code your service & data access layers on entities and convert DTOs to entities before calling service methods & convert entities to DTOs before returning response from controller.
I prefer this approach because entities rarely change and data can be added / removed from DTOs as desired.
Detailed model mapper configuration and rules are described here

I suggest another approach without extra dependency:
import org.springframework.beans.BeanUtils
...
BeanUtils.copyProperties(sourceObject, targetObject);
Can be used to convert DTO to entity, or vice-versa, if they have same property types and names.
If you want to ignore some fields, just add them after the targetObject.
BeanUtils.copyProperties(sourceObj, targetObj, "propertyToIgnoreA", "propertyToIgnoreB", "propertyToIgnoreC");
Source: http://appsdeveloperblog.com/dto-to-entity-and-entity-to-dto-conversion/
I think this is the cleanest way. Remember to check the Javadoc for caveats!

I can recommend to use mapstruct library:
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId>
<version>1.2.0.Final</version>
</dependency>
For example, if you have such an entity:
public class Entity {
private Integer status;
private String someString;
private Date startDate;
private Date endDate;
// SKIPPED
And DTO:
public class Dto {
private Boolean status;
private String someString;
private Long startDate;
private Long endDate;
// SKIPPED
Then the transformation can be done in the service layer by this way:
#Service
public class SomeServiceImpl implements SomeService {
#Autowired
SomeDao someDao;
#Autowired
SomeMapper someMapper;
public Dto getSomething(SomeRequest request) throws SomeException {
return someDao.getSomething(request.getSomeData())
.map(SomeMapper::mapEntityToDto)
.orElseThrow(() -> new SomeException("..."));
}
Mapper can be represented as follows:
#Mapper
public interface SomeMapper {
#Mappings(
{#Mapping(target = "entity",
expression = "java(entity.getStatus() == 1 ? Boolean.TRUE : Boolean.FALSE)"),
#Mapping(target = "endDate", source = "endDate"),
#Mapping(target = "startDate", source = "startDate")
})
Dto mapEntityToDto(Entity entity);
}

I think you are asking about where to write whole entity-->DTO conversion logic.
Like Your entity
class StudentEntity {
int age ;
String name;
//getter
//setter
public StudentDTO _toConvertStudentDTO(){
StudentDTO dto = new StudentDTO();
//set dto values here from StudentEntity
return dto;
}
}
Your DTO Should be like
class StudentDTO {
int age ;
String name;
//getter
//setter
public StudentEntity _toConvertStudentEntity(){
StudentEntity entity = new StudentEntity();
//set entity values here from StudentDTO
return entity ;
}
}
And Your Controller should be like
#Controller
class MyController {
public String my(){
//Call the conversion method here like
StudentEntity entity = myDao.getStudent(1);
StudentDTO dto = entity._toConvertStudentDTO();
//As vice versa
}
}

In my opinion
the Entity -> DTO conversion should be done in the Controller before dispatching the jsp page
the DTO -> Entity conversion should be done in the Controller as well after validating the DTO returned from the jsp page
Its gives you more control over the process and you do not have to change the service/persistence classes every time some logic populating the Entity is changed.

Used mapstruct library. Additionally added the following in build.gradle
sourceSets {
main.java.srcDirs += "build/generated/sources/annotationProcessor/java/main/"
}

Related

One way mapping in Dozer using custom converter

Please note: while I would accept an XML-based solution if that's truly the only way to accomplish what I'm looking for, I would greatly prefer a solution using Dozer's Java API.
I am new to Dozer and am trying to figure out how to use its API. It seems to default to field-level mappings (if the field names match) and to allow for custom mappers and converters in the event that field-level mapping (based on field name) is either not possible or not logical for your application needs.
I have a situation where my app will take a DTO, say, ReportedIssue (an issue reported by a user and sent to my application over HTTP), and an Issue entity (a data entity that will be persisted to a MySQL DB).
Here are my two objects:
#Data
public class ReportedIssue {
private String typeRefId;
private String reporterRefId;
private String info;
}
#Entity
#Table(name = "issues")
#Data
public class Issue {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "issue_ref_id")
private String refId;
#Column(name = "issue_tracking_number")
private String trackingNumber;
#OneToOne(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
#JoinColumn(name = "issue_type_id", referencedColumnName = "issue_type_id")
private IssueType type;
#Column(name = "issue_reported_on")
private Date reportedOn;
#OneToOne(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
#JoinColumn(name = "issue_reporter_id", referencedColumnName = "account_id")
private Account reporter;
#Column(name = "issue_info")
private String info;
}
So in the application frontend, a user can report an issue. The frontend sends a JSON version of a ReportedIssue to the backend, where that JSON is deserialized into a ReportedIssue DTO bean. Then I need Dozer to convert my ReportedIssue into an Issue entity that I can then easily save to my MySQL DB.
Here is my best attempt:
public class ReportedIssueConverter extends DozerConverter<ReportedIssue, Issue> {
private AuthService authService;
public ReportedIssueConverter(AuthService authService, Class<ReportedIssue> prototypeA, Class<Issue> prototypeB) {
super(prototypeA, prototypeB);
this.authService = authService;
}
public ReportedIssueConverter(Class<ReportedIssue> prototypeA, Class<Issue> prototypeB) {
super(prototypeA, prototypeB);
}
#Override
public Issue convertTo(ReportedIssue source, Issue destination) {
Issue issue = new Issue();
issue.setRefId(UUID.randomUUID().toString());
issue.setType(IssueUtils.determineType(source));
issue.setReportedOn(DateTimeUtils.nowInUTC());
issue.setReporter(authService.currentUser());
issue.setInfo(destination.getInfo());
return issue;
}
#Override
public ReportedIssue convertFrom(Issue source, ReportedIssue destination) {
throw new UnsupportedOperationException("we currently don't map from issues to reported issues");
}
}
Several concerns here. For one, is such a custom converter even necessary? Or is there a "better" (more standards compliant or using generally-accepted Dozer practices) way to use the Dozer API to perform this conversion? But mainly, this DozerConverter seems to be intended for bi-directional mapping use cases. Whereas, in my application, I will never have an Issue instance and need to map it back to a ReportedIssue DTO instance. So I only need one-way mapping from ReportedIssue --> Issue. Am I using Dozer correctly by throwing an UnsupportedOperationException or is there another interface or API trick I can use to only leverage the one-way mapping I need?
It could actually be done without a custom converter using custom getter methods in your dto class corresponding to fields in Issue. Dozer works by mapping each field in destination class by trying to invoke the getter method of the corresponding name in the source class.
public class ReportedIssue {
// fields.......
public String getRefId() {
UUID.randomUUID().toString()
}
public IssueType getType() {
IssueUtils.determineType(this);
}
// similarly create getters for other required fields.
}
But for reporter field in Issue, you need an AuthService object. I would suggest writing a static method as below:
public static Issue getIssue(AuthService auth, ReportedIssue dto) {
Issue issue = //map using dozer
issue.setReporter(authService.currentUser());
return issue;
}
Gauntham answer will work. Another option:
Implement a com.github.dozermapper.core.BeanFactory
Your custom BeanFactory can handle
Issue issue = new Issue();
issue.setRefId(UUID.randomUUID().toString());
issue.setReportedOn(DateTimeUtils.nowInUTC());
issue.setReporter(authService.currentUser());
Then depending on your preferences, this could also go into the bean factory
issue.setType(IssueUtils.determineType(source));
Or you could handle that separately in the mapping. Something would need to know how to call IssueUtils, so that is either 1) a customer converter or 2) a change to the DTO or entity to have the functionality through a getter or setter.
Finally, this line would be handled in the Dozer Java API mapping
issue.setInfo(destination.getInfo());
Personally, I like Dozer's com.github.dozermapper.core.loader.api.BeanMappingBuilder where you can explicitly tell it how to map 2 beans, specify the bean factory to use and the custom converter for a specific field.
mapping(ReportedIssue.class, Issue.class, oneWay(), wildcard(true), beanFactory(IssueBeanFactory.class.getName()).fields("this", "type", customConverter(IssueTypeConverter.class)
oneWay(), wildcard(boolean), and beanFactory(String) are found in Dozer's TypeMappingOptions and customConverter(Class.class) is found in Dozer's FieldMappingOptions.
oneWay() makes the mapping work only in the direction specified in the BeanMappingBuilder.
wildcard(true) tells Dozer to automatically map matching fields (this is default behavior).

Evaluate lazily with spring-data

I would like to have the roles set fetch lazily. Hibernate fetchType.Lazy doesn't work for this cases, where spring data is used. I 've been trying lots of possibilities like Entitygraph but none of them works on this, or I'm using them wrong.
I have the next classes:
A class User:
#Entity
#JsonRootName(value = "user")
#Table(name = "web_users", schema = "t_dw_comercial")
public class User {
#Id
private int userId;
private String fullName;
#OneToMany(fetch=FetchType.LAZY)
#JoinTable(name="web_users_roles",
joinColumns = {#JoinColumn(name="user_id")},
inverseJoinColumns = {#JoinColumn(name="role_id")}
)
private List<Role> roles;
}
A class Role:
#Entity
#JsonRootName(value = "roles")
#Table(name = "web_roles", schema = "t_dw_comercial")
public class Role {
#Id
private int roleId;
private String roleName;
}
Service:
#Service
public class UserService implements IUserService{
#Autowired
UserRepository repository;
public User findUserByLdapId(String loginName) {
return repository.findUserByLdapId(loginName);
}
}
Repository:
#Repository
public interface UserRepository extends CrudRepository<User, Long>{
#Query("SELECT u FROM User u where u.ldapId= ?1")
public User findUserByLdapId(String loginName);
}
Controller:
#Controller
#RestController
public class UserController {
#Autowired
private IUserService userService;
#CrossOrigin
#RequestMapping(value = "/dashboard", params = {"user"}, method = RequestMethod.GET, produces = "application/json")
public ResponseEntity<User> getUser(#RequestParam(value = "user") String ldapId) {
User user = userService.findUserByLdapId(ldapId);
if(user == null)
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
return new ResponseEntity<>(user, HttpStatus.OK);
};
}
So a json would looks like:
{
"user": {
"userId": 1,
"fullName": "Carolina Ponce",
"roles":[]
}
}
Thanks in advance!
It seems you're asking for two different things: how to fetch #OneToMany association lazily (which should work out of the box) and how to make your JSON look like the above (which has nothing to do with how your JPA entity fetching is configured).
If you serialize your entity using Jackson, by default all the fields will get serialized, including those that are fetched lazily. If the persistence context is still open when you begin to serialize the entities, Jackson will simply trigger lazy loading by accessing the property. As a result, regardless of whether you use FetchType.EAGER or FetchType.LAZY, roles will be included in the result (I assume it is the case, since you'd be getting a LazyInitializationException if the context was closed).
The solution is, simply, to tell Jackson to refrain from serializing roles using #JsonIgnore if you don't want the property in the result.
First of all Hibernate and Spring Data are totally different tools designed for different purposes and they work with each other just fine. You can read more about the differences between both here: What Is the Difference Between Hibernate and Spring Data JPA?
Spring Data has nothing to do with how you fetch entity's related collections, Hibernate, as an implementation of JPA, does.
I assume you indeed fetch your collection lazily and that happens during serialization of your entity so the mechanism works as it should.
I suggest you to create some sort of DTO object that you could return from your controller rather than returning an entity. It seems like you don't want to expose user's roles so creating a DTO without roles may be a good idea. Other solution is to instruct Jackson to ommit roles property during serialization using #JsonIgnore annotation.

Is it possible to use a MongoRepository with not-fixed document structure? [duplicate]

Mongodb is a no-schema document database, but in spring data, it's necessary to define entity class and repository class, like following:
Entity class:
#Document(collection = "users")
public class User implements UserDetails {
#Id private String userId;
#NotNull #Indexed(unique = true) private String username;
#NotNull private String password;
#NotNull private String name;
#NotNull private String email;
}
Repository class:
public interface UserRepository extends MongoRepository<User, String> {
User findByUsername(String username);
}
Is there anyway to use map not class in spring data mongodb so that the server can accept any dynamic JSON data then store it in BSON without any pre-class define?
First, a few insightful links about schemaless data:
what does “schemaless” even mean anyway?
“schemaless” doesn't mean “schemafree”
Second... one may wonder if Spring, or Java, is the right solution for your problem - why not a more dynamic tool, such a Ruby, Python or the Mongoshell?
That being said, let's focus on the technical issue.
If your goal is only to store random data, you could basically just define your own controller and use the MongoDB Java Driver directly.
If you really insist on having no predefined schema for your domain object class, use this:
#Document(collection = "users")
public class User implements UserDetails {
#Id
private String id;
private Map<String, Object> schemalessData;
// getters/setters omitted
}
Basically it gives you a container in which you can put whatever you want, but watch out for serialization/deserialization issues (this may become tricky if you had ObjectIds and DBRefs in your nested document). Also, updating data may become nasty if your data hierarchy becomes too complex.
Still, at some point, you'll realize your data indeed has a schema that can be pinpointed and put into well-defined POJOs.
Update
A late update since people still happen to read this post in 2020: the Jackson annotations JsonAnyGetter and JsonAnySetter let you hide the root of the schemaless-data container so your unknown fields can be sent as top-level fields in your payload. They will still be stored nested in your MongoDB document, but will appear as top-level fields when the ressource is requested through Spring.
#Document(collection = "users")
public class User implements UserDetails {
#Id
private String id;
// add all other expected fields (getters/setters omitted)
private String foo;
private String bar;
// a container for all unexpected fields
private Map<String, Object> schemalessData;
#JsonAnySetter
public void add(String key, Object value) {
if (null == schemalessData) {
schemalessData = new HashMap<>();
}
schemalessData.put(key, value);
}
#JsonAnyGetter
public Map<String, Object> get() {
return schemalessData;
}
// getters/setters omitted
}

Design of DTO from Model for an API

I am designing an API using Spring boot for some social network backend. My current model looks like this:
public class User {
private long id;
private String handle;
private String name;
private List<User> followers;
private List<User> following;
// Getters setters etc
Now, I have created DTO which closely resembles above structure. My problem is that sometimes I want to return exactly what's above (which is fine) but sometimes, I don't want that.
For example when someone is only interested in finding followers of user, I don't want to include followers and following (I am simply interested in id, handle and name so computing followers and following for all of those users would be an incredible waste of resources).
In my current implementation those fields are returned with null values, which I don't think is a great idea. Should I create a separate DTO without those lists with just id, handle and name? Or is there more elegant way to do it?
It is a controversial issue. If you don't want to create separate dto there are several ways to do it. It depends on what data access approach you are going to use:
Using Spring Data JPA it is possible to return an entity in projection. You just need to add an additional constructor to your entity:
public interface UserRepository extends JpaRepository<User, Long> {
#Query("select new User(u.id,u.name) from User u")
List<User> findAllUserItems();
}
Or same using JPA EntityManger:
public List<User> findAllUserItems() {
return entityManager.createQuery("select new User(u.id,u.name) from User u", User.class)
.getResultList();
}
If you wonder about unnecessary null fields and you are using Jackson, it is possible to configure to ignore null fields. For Spring Boot:
spring.jackson.default-property-inclusion=non_null
Or with Java config:
#Bean
public Jackson2ObjectMapperBuilder objectMapperBuilder() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.serializationInclusion(JsonInclude.Include.NON_NULL);
return builder;
}
Or for not Spring Boot project:
#Configuration
#EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(converter());
}
#Bean
public HttpMessageConverter converter() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
return new MappingJackson2HttpMessageConverter(objectMapper);
}
}
Also, if you going to use Hibernate Session. To map directly to dto you may use AliasToBeanResultTransformer:
public List<UserDto> findAllUserItems() {
return session.createQuery("select u.id as id,u.name as name from User u")
.setResultTransformer(Transformers.aliasToBean(UserDto.class))
.list();
}
Suppose you can try like this.
#Entity
public class User implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#ManyToOne
private User parent;
#OneToMany(mappedBy="parent")
private List<User> followers;
#OneToMany(mappedBy="parent")
private List<User> following;
// Getters, Setters etc....
}
Also this post may be helpful.

How to achive lazy loading using spring data jpa?

In my project, I am using Spring Data JPA and extend the JpaRepository interface for my data fetching class.
OrganizationMaster class :
#Entity
#Table(name="organization_master")
public class OrganizationMaster {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="organization_id")
private int organizationId;
#OneToMany(mappedBy="organizationMaster")
private List<CompanyMaster> companyMasters;
}
CompanyMaster Class:
Entity
#Table(name="company_master")
public class CompanyMaster {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="company_id")
private int companyId;
#ManyToOne
#JoinColumn(name="organization_id")
private OrganizationMaster organizationMaster;
}
My Controller
#RequestMapping(value = "/GetOrganization", method = RequestMethod.GET)
public
#ResponseBody
List<OrganizationMaster> getOrganization(){
return organizationService.getOrganization();
}
OrganizationService:
public interface OrganizationService {
List<OrganizationMaster> getOrganization();
}
OrganizationServiceImpl:
#Service
public class OrganizationServiceImpl implements OrganizationService{
#Autowired
private OrganizationDao organizationDao;
#Override
public List<OrganizationMaster> getOrganization() {
return organizationDao.findAll();
}
}
OrganizationDao Interface:
public interface OrganizationDao extends JpaRepository<OrganizationMaster,Long> {
}
My Output Response is:
[{"organizationId":5,"companyMasters":[{"companyId":29},{"companyId":30}]}]
But my need is
[{"organizationId":5}]
When I am trying to get data from the organization master using findall() method it also fetches data from the company master based on the relationship. How can I achieve lazy fetching (get data only from organization master) using spring data JpaRepository
All XToOne associations are default EAGER. You have a bidirectional relationship, so you should use FetchType.LAZY on your #ManyToOne side.
#ManyToOne(fetch = FetchType.LAZY)
Also if you use any serializer (like json serializer) when it serialize it calls getter methods and it may causes to load lazy items unneccessarly.
Another consideration is while using Lombok, #Data annotation causes to load lazy items without need. So be careful when using Lombok.
So in your case, beacuse of you return entity itself, while serialization it serializes the child too, it causes to load lazly child entity.
You need to return a dto which represents only your parent entity to prevent serialization of child entity. If you call child with getter method, it laods lazly child entity from database.
Take a look for further information associations:
https://vladmihalcea.com/the-best-way-to-map-a-onetomany-association-with-jpa-and-hibernate/
I believe this question is asked before!you can use this annotation:
#OneToMany( fetch = FetchType.LAZY )
read this article for better view in this point:
https://howtodoinjava.com/hibernate/lazy-loading-in-hibernate/

Categories