Code design: How to ensure that DTO objects refer to the same object, avoid stackoverflow in circular...












0















I have a Person class which has a Company class which also have a List of Person .



Everything works great, the List have the same references to the parent Person. Not having any duplicate of Person. (I think hibernate achieves this, IDK)



Now in DTO, i want to copy the DAO (Person,Company) to (PersonDTO,CompanyDTO)



How can I copy the list of Person so that it has the same Person objects.



public class Person {
String name;
String address;
Company Company;
}
public class Company {
String name;
String phoneNumber;
List<Person> Persons;
}









share|improve this question

























  • Maybe give us some sample codes so that we know what you are talking about.

    – Jai
    Nov 22 '18 at 9:30
















0















I have a Person class which has a Company class which also have a List of Person .



Everything works great, the List have the same references to the parent Person. Not having any duplicate of Person. (I think hibernate achieves this, IDK)



Now in DTO, i want to copy the DAO (Person,Company) to (PersonDTO,CompanyDTO)



How can I copy the list of Person so that it has the same Person objects.



public class Person {
String name;
String address;
Company Company;
}
public class Company {
String name;
String phoneNumber;
List<Person> Persons;
}









share|improve this question

























  • Maybe give us some sample codes so that we know what you are talking about.

    – Jai
    Nov 22 '18 at 9:30














0












0








0








I have a Person class which has a Company class which also have a List of Person .



Everything works great, the List have the same references to the parent Person. Not having any duplicate of Person. (I think hibernate achieves this, IDK)



Now in DTO, i want to copy the DAO (Person,Company) to (PersonDTO,CompanyDTO)



How can I copy the list of Person so that it has the same Person objects.



public class Person {
String name;
String address;
Company Company;
}
public class Company {
String name;
String phoneNumber;
List<Person> Persons;
}









share|improve this question
















I have a Person class which has a Company class which also have a List of Person .



Everything works great, the List have the same references to the parent Person. Not having any duplicate of Person. (I think hibernate achieves this, IDK)



Now in DTO, i want to copy the DAO (Person,Company) to (PersonDTO,CompanyDTO)



How can I copy the list of Person so that it has the same Person objects.



public class Person {
String name;
String address;
Company Company;
}
public class Company {
String name;
String phoneNumber;
List<Person> Persons;
}






java






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 22 '18 at 15:44







InternCoder

















asked Nov 22 '18 at 9:27









InternCoderInternCoder

208




208













  • Maybe give us some sample codes so that we know what you are talking about.

    – Jai
    Nov 22 '18 at 9:30



















  • Maybe give us some sample codes so that we know what you are talking about.

    – Jai
    Nov 22 '18 at 9:30

















Maybe give us some sample codes so that we know what you are talking about.

– Jai
Nov 22 '18 at 9:30





Maybe give us some sample codes so that we know what you are talking about.

– Jai
Nov 22 '18 at 9:30












2 Answers
2






active

oldest

votes


















0














You can use external libraries for that like ModelMapper, Dozer or MapStruct.



Here is an overview of mapping frameworks.






share|improve this answer


























  • This doesn't answer the question. Using Mappers won't protect you from stack overflow on circular references.

    – BackSlash
    Nov 22 '18 at 9:33





















0














Assuming you are using Spring or Apache Commons utils to do the copying:



public static CompanyDTO convertToDTO(Company dao) {
return dao != null ? convertToDTO(dao, null, null) : null;
}

public static PersonDTO convertToDTO(Person dao) {
return dao != null ? convertToDTO(dao, null) : null;
}

private static CompanyDTO convertToDTO(Company dao, Person original, PersonDTO converted) {
CompanyDTO dto = new CompanyDTO();
BeanUtils.copyProperties(dao, dto, "persons");
dto.setPersons(new ArrayList<>());

for (Person person : dao.getPersons()) {
if (person.equals(original)) {
dto.getPersons().add(converted);
continue;
}

PersonDTO personDTO = convertToDTO(person, dto);
dto.getPersons().add(personDTO);
}

return dto;
}

private static PersonDTO convertToDTO(Person dao, Company converted) {
PersonDTO dto = new PersonDTO();
BeanUtils.copyProperties(dao, dto, "company");

if (converted == null) {
converted = convertToDTO(dao.getCompany(), dao, dto);
}

dto.setCompany(converted);

return dto;
}


It's pretty manual here, but it should get the job done. Also note that it may be more reusable if you create more methods that take in objects using generics.



P.S. I didn't test this as I wrote it directly onto SO, so it may not work, or may contain some typoes.



Edit



Usually, DTOs are created without any form of caching, even for non-circular dependency entities. That means if I have a list of Person, and there are multiple people with the same date of birth, the same Date (or LocalDate) is going to be created multiple times.



You can definitely do a cached of created DTOs:



public final class DTOManager {
private final Map<Class<?>, List<SoftReference<Object>>> cache = new HashMap<>();

public static CompanyDTO convertToDTO(Company dao) {
return dao != null ? convertToDTO(dao, null, null) : null;
}

public static PersonDTO convertToDTO(Person dao) {
return dao != null ? convertToDTO(dao, null) : null;
}

private static CompanyDTO convertToDTO(Company dao, Person original, PersonDTO converted) {
List<SoftReference<Object>> c = cache.get(CompanyDTO.class);
Optional<Object> cachedDTO = (c == null) ? Optional.empty() : c.stream()
.filter(ref ->
ref.get() != null &&
ref.get().getPrimaryKey() == dao.getPrimaryKey()) // Use equal() if string key
.findAny();

if (cachedDTO.isPresent()) {
return (CompanyDTO) cachedDTO.get();
}

CompanyDTO dto = new CompanyDTO();
BeanUtils.copyProperties(dao, dto, "persons");
dto.setPersons(new ArrayList<>());

for (Person person : dao.getPersons()) {
if (person.equals(original)) {
dto.getPersons().add(converted);
continue;
}

PersonDTO personDTO = convertToDTO(person, dto);
dto.getPersons().add(personDTO);
}

if (c == null) {
c = new ArrayList<>();
cache.put(CompanyDTO.class, c);
}

c.add(new SoftReference<>(dto));

return dto;
}

private static PersonDTO convertToDTO(Person dao, Company converted) {
List<SoftReference<Object>> c = cache.get(PersonDTO.class);
Optional<Object> cachedDTO = (c == null) ? Optional.empty() : c.stream()
.filter(ref ->
ref.get() != null &&
ref.get().getPrimaryKey() == dao.getPrimaryKey()) // Use equal() if string key
.findAny();

if (cachedDTO.isPresent()) {
return (PersonDTO) cachedDTO.get();
}

PersonDTO dto = new PersonDTO();
BeanUtils.copyProperties(dao, dto, "company");

if (converted == null) {
converted = convertToDTO(dao.getCompany(), dao, dto);
}

dto.setCompany(converted);

if (c == null) {
c = new ArrayList<>();
cache.put(PersonDTO.class, c);
}

c.add(new SoftReference<>(dto));

return dto;
}
}


This method comes with the cost of accuracy - objects in the cache may not be the most updated. You can implement your own cache, mine is just a simple example. My example is also not thread-safe, so you may want to take note.



In my opinion, it is not worth it to implement caching, since DTOs are supposed to be use-and-dump. This is only plausible if you are expecting retrieval of very huge amount of entities in a single transaction. Otherwise, it might be better to let it create more objects, then let the data transfer process take place, and thereafter let the garbage collector reclaim back those memory.






share|improve this answer


























  • Still getting too many instances of PersonDTO

    – InternCoder
    Nov 23 '18 at 15:11











  • your answer avoids stackoverflow but why am I getting too many instances in memory of PersonDTO and CompanyDTO. For example, if I have 30 Person (DAO) and 3 Company (DAO), I think CompanyDTO should be 3 and PersonDTO should be 30. is this possible?

    – InternCoder
    Nov 23 '18 at 15:24











  • @InternCoder May not be what you want, but updated answer.

    – Jai
    Nov 26 '18 at 2:22











Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53427651%2fcode-design-how-to-ensure-that-dto-objects-refer-to-the-same-object-avoid-stac%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























2 Answers
2






active

oldest

votes








2 Answers
2






active

oldest

votes









active

oldest

votes






active

oldest

votes









0














You can use external libraries for that like ModelMapper, Dozer or MapStruct.



Here is an overview of mapping frameworks.






share|improve this answer


























  • This doesn't answer the question. Using Mappers won't protect you from stack overflow on circular references.

    – BackSlash
    Nov 22 '18 at 9:33


















0














You can use external libraries for that like ModelMapper, Dozer or MapStruct.



Here is an overview of mapping frameworks.






share|improve this answer


























  • This doesn't answer the question. Using Mappers won't protect you from stack overflow on circular references.

    – BackSlash
    Nov 22 '18 at 9:33
















0












0








0







You can use external libraries for that like ModelMapper, Dozer or MapStruct.



Here is an overview of mapping frameworks.






share|improve this answer















You can use external libraries for that like ModelMapper, Dozer or MapStruct.



Here is an overview of mapping frameworks.







share|improve this answer














share|improve this answer



share|improve this answer








edited Nov 22 '18 at 9:33

























answered Nov 22 '18 at 9:30









Genetic ForestGenetic Forest

1,98131526




1,98131526













  • This doesn't answer the question. Using Mappers won't protect you from stack overflow on circular references.

    – BackSlash
    Nov 22 '18 at 9:33





















  • This doesn't answer the question. Using Mappers won't protect you from stack overflow on circular references.

    – BackSlash
    Nov 22 '18 at 9:33



















This doesn't answer the question. Using Mappers won't protect you from stack overflow on circular references.

– BackSlash
Nov 22 '18 at 9:33







This doesn't answer the question. Using Mappers won't protect you from stack overflow on circular references.

– BackSlash
Nov 22 '18 at 9:33















0














Assuming you are using Spring or Apache Commons utils to do the copying:



public static CompanyDTO convertToDTO(Company dao) {
return dao != null ? convertToDTO(dao, null, null) : null;
}

public static PersonDTO convertToDTO(Person dao) {
return dao != null ? convertToDTO(dao, null) : null;
}

private static CompanyDTO convertToDTO(Company dao, Person original, PersonDTO converted) {
CompanyDTO dto = new CompanyDTO();
BeanUtils.copyProperties(dao, dto, "persons");
dto.setPersons(new ArrayList<>());

for (Person person : dao.getPersons()) {
if (person.equals(original)) {
dto.getPersons().add(converted);
continue;
}

PersonDTO personDTO = convertToDTO(person, dto);
dto.getPersons().add(personDTO);
}

return dto;
}

private static PersonDTO convertToDTO(Person dao, Company converted) {
PersonDTO dto = new PersonDTO();
BeanUtils.copyProperties(dao, dto, "company");

if (converted == null) {
converted = convertToDTO(dao.getCompany(), dao, dto);
}

dto.setCompany(converted);

return dto;
}


It's pretty manual here, but it should get the job done. Also note that it may be more reusable if you create more methods that take in objects using generics.



P.S. I didn't test this as I wrote it directly onto SO, so it may not work, or may contain some typoes.



Edit



Usually, DTOs are created without any form of caching, even for non-circular dependency entities. That means if I have a list of Person, and there are multiple people with the same date of birth, the same Date (or LocalDate) is going to be created multiple times.



You can definitely do a cached of created DTOs:



public final class DTOManager {
private final Map<Class<?>, List<SoftReference<Object>>> cache = new HashMap<>();

public static CompanyDTO convertToDTO(Company dao) {
return dao != null ? convertToDTO(dao, null, null) : null;
}

public static PersonDTO convertToDTO(Person dao) {
return dao != null ? convertToDTO(dao, null) : null;
}

private static CompanyDTO convertToDTO(Company dao, Person original, PersonDTO converted) {
List<SoftReference<Object>> c = cache.get(CompanyDTO.class);
Optional<Object> cachedDTO = (c == null) ? Optional.empty() : c.stream()
.filter(ref ->
ref.get() != null &&
ref.get().getPrimaryKey() == dao.getPrimaryKey()) // Use equal() if string key
.findAny();

if (cachedDTO.isPresent()) {
return (CompanyDTO) cachedDTO.get();
}

CompanyDTO dto = new CompanyDTO();
BeanUtils.copyProperties(dao, dto, "persons");
dto.setPersons(new ArrayList<>());

for (Person person : dao.getPersons()) {
if (person.equals(original)) {
dto.getPersons().add(converted);
continue;
}

PersonDTO personDTO = convertToDTO(person, dto);
dto.getPersons().add(personDTO);
}

if (c == null) {
c = new ArrayList<>();
cache.put(CompanyDTO.class, c);
}

c.add(new SoftReference<>(dto));

return dto;
}

private static PersonDTO convertToDTO(Person dao, Company converted) {
List<SoftReference<Object>> c = cache.get(PersonDTO.class);
Optional<Object> cachedDTO = (c == null) ? Optional.empty() : c.stream()
.filter(ref ->
ref.get() != null &&
ref.get().getPrimaryKey() == dao.getPrimaryKey()) // Use equal() if string key
.findAny();

if (cachedDTO.isPresent()) {
return (PersonDTO) cachedDTO.get();
}

PersonDTO dto = new PersonDTO();
BeanUtils.copyProperties(dao, dto, "company");

if (converted == null) {
converted = convertToDTO(dao.getCompany(), dao, dto);
}

dto.setCompany(converted);

if (c == null) {
c = new ArrayList<>();
cache.put(PersonDTO.class, c);
}

c.add(new SoftReference<>(dto));

return dto;
}
}


This method comes with the cost of accuracy - objects in the cache may not be the most updated. You can implement your own cache, mine is just a simple example. My example is also not thread-safe, so you may want to take note.



In my opinion, it is not worth it to implement caching, since DTOs are supposed to be use-and-dump. This is only plausible if you are expecting retrieval of very huge amount of entities in a single transaction. Otherwise, it might be better to let it create more objects, then let the data transfer process take place, and thereafter let the garbage collector reclaim back those memory.






share|improve this answer


























  • Still getting too many instances of PersonDTO

    – InternCoder
    Nov 23 '18 at 15:11











  • your answer avoids stackoverflow but why am I getting too many instances in memory of PersonDTO and CompanyDTO. For example, if I have 30 Person (DAO) and 3 Company (DAO), I think CompanyDTO should be 3 and PersonDTO should be 30. is this possible?

    – InternCoder
    Nov 23 '18 at 15:24











  • @InternCoder May not be what you want, but updated answer.

    – Jai
    Nov 26 '18 at 2:22
















0














Assuming you are using Spring or Apache Commons utils to do the copying:



public static CompanyDTO convertToDTO(Company dao) {
return dao != null ? convertToDTO(dao, null, null) : null;
}

public static PersonDTO convertToDTO(Person dao) {
return dao != null ? convertToDTO(dao, null) : null;
}

private static CompanyDTO convertToDTO(Company dao, Person original, PersonDTO converted) {
CompanyDTO dto = new CompanyDTO();
BeanUtils.copyProperties(dao, dto, "persons");
dto.setPersons(new ArrayList<>());

for (Person person : dao.getPersons()) {
if (person.equals(original)) {
dto.getPersons().add(converted);
continue;
}

PersonDTO personDTO = convertToDTO(person, dto);
dto.getPersons().add(personDTO);
}

return dto;
}

private static PersonDTO convertToDTO(Person dao, Company converted) {
PersonDTO dto = new PersonDTO();
BeanUtils.copyProperties(dao, dto, "company");

if (converted == null) {
converted = convertToDTO(dao.getCompany(), dao, dto);
}

dto.setCompany(converted);

return dto;
}


It's pretty manual here, but it should get the job done. Also note that it may be more reusable if you create more methods that take in objects using generics.



P.S. I didn't test this as I wrote it directly onto SO, so it may not work, or may contain some typoes.



Edit



Usually, DTOs are created without any form of caching, even for non-circular dependency entities. That means if I have a list of Person, and there are multiple people with the same date of birth, the same Date (or LocalDate) is going to be created multiple times.



You can definitely do a cached of created DTOs:



public final class DTOManager {
private final Map<Class<?>, List<SoftReference<Object>>> cache = new HashMap<>();

public static CompanyDTO convertToDTO(Company dao) {
return dao != null ? convertToDTO(dao, null, null) : null;
}

public static PersonDTO convertToDTO(Person dao) {
return dao != null ? convertToDTO(dao, null) : null;
}

private static CompanyDTO convertToDTO(Company dao, Person original, PersonDTO converted) {
List<SoftReference<Object>> c = cache.get(CompanyDTO.class);
Optional<Object> cachedDTO = (c == null) ? Optional.empty() : c.stream()
.filter(ref ->
ref.get() != null &&
ref.get().getPrimaryKey() == dao.getPrimaryKey()) // Use equal() if string key
.findAny();

if (cachedDTO.isPresent()) {
return (CompanyDTO) cachedDTO.get();
}

CompanyDTO dto = new CompanyDTO();
BeanUtils.copyProperties(dao, dto, "persons");
dto.setPersons(new ArrayList<>());

for (Person person : dao.getPersons()) {
if (person.equals(original)) {
dto.getPersons().add(converted);
continue;
}

PersonDTO personDTO = convertToDTO(person, dto);
dto.getPersons().add(personDTO);
}

if (c == null) {
c = new ArrayList<>();
cache.put(CompanyDTO.class, c);
}

c.add(new SoftReference<>(dto));

return dto;
}

private static PersonDTO convertToDTO(Person dao, Company converted) {
List<SoftReference<Object>> c = cache.get(PersonDTO.class);
Optional<Object> cachedDTO = (c == null) ? Optional.empty() : c.stream()
.filter(ref ->
ref.get() != null &&
ref.get().getPrimaryKey() == dao.getPrimaryKey()) // Use equal() if string key
.findAny();

if (cachedDTO.isPresent()) {
return (PersonDTO) cachedDTO.get();
}

PersonDTO dto = new PersonDTO();
BeanUtils.copyProperties(dao, dto, "company");

if (converted == null) {
converted = convertToDTO(dao.getCompany(), dao, dto);
}

dto.setCompany(converted);

if (c == null) {
c = new ArrayList<>();
cache.put(PersonDTO.class, c);
}

c.add(new SoftReference<>(dto));

return dto;
}
}


This method comes with the cost of accuracy - objects in the cache may not be the most updated. You can implement your own cache, mine is just a simple example. My example is also not thread-safe, so you may want to take note.



In my opinion, it is not worth it to implement caching, since DTOs are supposed to be use-and-dump. This is only plausible if you are expecting retrieval of very huge amount of entities in a single transaction. Otherwise, it might be better to let it create more objects, then let the data transfer process take place, and thereafter let the garbage collector reclaim back those memory.






share|improve this answer


























  • Still getting too many instances of PersonDTO

    – InternCoder
    Nov 23 '18 at 15:11











  • your answer avoids stackoverflow but why am I getting too many instances in memory of PersonDTO and CompanyDTO. For example, if I have 30 Person (DAO) and 3 Company (DAO), I think CompanyDTO should be 3 and PersonDTO should be 30. is this possible?

    – InternCoder
    Nov 23 '18 at 15:24











  • @InternCoder May not be what you want, but updated answer.

    – Jai
    Nov 26 '18 at 2:22














0












0








0







Assuming you are using Spring or Apache Commons utils to do the copying:



public static CompanyDTO convertToDTO(Company dao) {
return dao != null ? convertToDTO(dao, null, null) : null;
}

public static PersonDTO convertToDTO(Person dao) {
return dao != null ? convertToDTO(dao, null) : null;
}

private static CompanyDTO convertToDTO(Company dao, Person original, PersonDTO converted) {
CompanyDTO dto = new CompanyDTO();
BeanUtils.copyProperties(dao, dto, "persons");
dto.setPersons(new ArrayList<>());

for (Person person : dao.getPersons()) {
if (person.equals(original)) {
dto.getPersons().add(converted);
continue;
}

PersonDTO personDTO = convertToDTO(person, dto);
dto.getPersons().add(personDTO);
}

return dto;
}

private static PersonDTO convertToDTO(Person dao, Company converted) {
PersonDTO dto = new PersonDTO();
BeanUtils.copyProperties(dao, dto, "company");

if (converted == null) {
converted = convertToDTO(dao.getCompany(), dao, dto);
}

dto.setCompany(converted);

return dto;
}


It's pretty manual here, but it should get the job done. Also note that it may be more reusable if you create more methods that take in objects using generics.



P.S. I didn't test this as I wrote it directly onto SO, so it may not work, or may contain some typoes.



Edit



Usually, DTOs are created without any form of caching, even for non-circular dependency entities. That means if I have a list of Person, and there are multiple people with the same date of birth, the same Date (or LocalDate) is going to be created multiple times.



You can definitely do a cached of created DTOs:



public final class DTOManager {
private final Map<Class<?>, List<SoftReference<Object>>> cache = new HashMap<>();

public static CompanyDTO convertToDTO(Company dao) {
return dao != null ? convertToDTO(dao, null, null) : null;
}

public static PersonDTO convertToDTO(Person dao) {
return dao != null ? convertToDTO(dao, null) : null;
}

private static CompanyDTO convertToDTO(Company dao, Person original, PersonDTO converted) {
List<SoftReference<Object>> c = cache.get(CompanyDTO.class);
Optional<Object> cachedDTO = (c == null) ? Optional.empty() : c.stream()
.filter(ref ->
ref.get() != null &&
ref.get().getPrimaryKey() == dao.getPrimaryKey()) // Use equal() if string key
.findAny();

if (cachedDTO.isPresent()) {
return (CompanyDTO) cachedDTO.get();
}

CompanyDTO dto = new CompanyDTO();
BeanUtils.copyProperties(dao, dto, "persons");
dto.setPersons(new ArrayList<>());

for (Person person : dao.getPersons()) {
if (person.equals(original)) {
dto.getPersons().add(converted);
continue;
}

PersonDTO personDTO = convertToDTO(person, dto);
dto.getPersons().add(personDTO);
}

if (c == null) {
c = new ArrayList<>();
cache.put(CompanyDTO.class, c);
}

c.add(new SoftReference<>(dto));

return dto;
}

private static PersonDTO convertToDTO(Person dao, Company converted) {
List<SoftReference<Object>> c = cache.get(PersonDTO.class);
Optional<Object> cachedDTO = (c == null) ? Optional.empty() : c.stream()
.filter(ref ->
ref.get() != null &&
ref.get().getPrimaryKey() == dao.getPrimaryKey()) // Use equal() if string key
.findAny();

if (cachedDTO.isPresent()) {
return (PersonDTO) cachedDTO.get();
}

PersonDTO dto = new PersonDTO();
BeanUtils.copyProperties(dao, dto, "company");

if (converted == null) {
converted = convertToDTO(dao.getCompany(), dao, dto);
}

dto.setCompany(converted);

if (c == null) {
c = new ArrayList<>();
cache.put(PersonDTO.class, c);
}

c.add(new SoftReference<>(dto));

return dto;
}
}


This method comes with the cost of accuracy - objects in the cache may not be the most updated. You can implement your own cache, mine is just a simple example. My example is also not thread-safe, so you may want to take note.



In my opinion, it is not worth it to implement caching, since DTOs are supposed to be use-and-dump. This is only plausible if you are expecting retrieval of very huge amount of entities in a single transaction. Otherwise, it might be better to let it create more objects, then let the data transfer process take place, and thereafter let the garbage collector reclaim back those memory.






share|improve this answer















Assuming you are using Spring or Apache Commons utils to do the copying:



public static CompanyDTO convertToDTO(Company dao) {
return dao != null ? convertToDTO(dao, null, null) : null;
}

public static PersonDTO convertToDTO(Person dao) {
return dao != null ? convertToDTO(dao, null) : null;
}

private static CompanyDTO convertToDTO(Company dao, Person original, PersonDTO converted) {
CompanyDTO dto = new CompanyDTO();
BeanUtils.copyProperties(dao, dto, "persons");
dto.setPersons(new ArrayList<>());

for (Person person : dao.getPersons()) {
if (person.equals(original)) {
dto.getPersons().add(converted);
continue;
}

PersonDTO personDTO = convertToDTO(person, dto);
dto.getPersons().add(personDTO);
}

return dto;
}

private static PersonDTO convertToDTO(Person dao, Company converted) {
PersonDTO dto = new PersonDTO();
BeanUtils.copyProperties(dao, dto, "company");

if (converted == null) {
converted = convertToDTO(dao.getCompany(), dao, dto);
}

dto.setCompany(converted);

return dto;
}


It's pretty manual here, but it should get the job done. Also note that it may be more reusable if you create more methods that take in objects using generics.



P.S. I didn't test this as I wrote it directly onto SO, so it may not work, or may contain some typoes.



Edit



Usually, DTOs are created without any form of caching, even for non-circular dependency entities. That means if I have a list of Person, and there are multiple people with the same date of birth, the same Date (or LocalDate) is going to be created multiple times.



You can definitely do a cached of created DTOs:



public final class DTOManager {
private final Map<Class<?>, List<SoftReference<Object>>> cache = new HashMap<>();

public static CompanyDTO convertToDTO(Company dao) {
return dao != null ? convertToDTO(dao, null, null) : null;
}

public static PersonDTO convertToDTO(Person dao) {
return dao != null ? convertToDTO(dao, null) : null;
}

private static CompanyDTO convertToDTO(Company dao, Person original, PersonDTO converted) {
List<SoftReference<Object>> c = cache.get(CompanyDTO.class);
Optional<Object> cachedDTO = (c == null) ? Optional.empty() : c.stream()
.filter(ref ->
ref.get() != null &&
ref.get().getPrimaryKey() == dao.getPrimaryKey()) // Use equal() if string key
.findAny();

if (cachedDTO.isPresent()) {
return (CompanyDTO) cachedDTO.get();
}

CompanyDTO dto = new CompanyDTO();
BeanUtils.copyProperties(dao, dto, "persons");
dto.setPersons(new ArrayList<>());

for (Person person : dao.getPersons()) {
if (person.equals(original)) {
dto.getPersons().add(converted);
continue;
}

PersonDTO personDTO = convertToDTO(person, dto);
dto.getPersons().add(personDTO);
}

if (c == null) {
c = new ArrayList<>();
cache.put(CompanyDTO.class, c);
}

c.add(new SoftReference<>(dto));

return dto;
}

private static PersonDTO convertToDTO(Person dao, Company converted) {
List<SoftReference<Object>> c = cache.get(PersonDTO.class);
Optional<Object> cachedDTO = (c == null) ? Optional.empty() : c.stream()
.filter(ref ->
ref.get() != null &&
ref.get().getPrimaryKey() == dao.getPrimaryKey()) // Use equal() if string key
.findAny();

if (cachedDTO.isPresent()) {
return (PersonDTO) cachedDTO.get();
}

PersonDTO dto = new PersonDTO();
BeanUtils.copyProperties(dao, dto, "company");

if (converted == null) {
converted = convertToDTO(dao.getCompany(), dao, dto);
}

dto.setCompany(converted);

if (c == null) {
c = new ArrayList<>();
cache.put(PersonDTO.class, c);
}

c.add(new SoftReference<>(dto));

return dto;
}
}


This method comes with the cost of accuracy - objects in the cache may not be the most updated. You can implement your own cache, mine is just a simple example. My example is also not thread-safe, so you may want to take note.



In my opinion, it is not worth it to implement caching, since DTOs are supposed to be use-and-dump. This is only plausible if you are expecting retrieval of very huge amount of entities in a single transaction. Otherwise, it might be better to let it create more objects, then let the data transfer process take place, and thereafter let the garbage collector reclaim back those memory.







share|improve this answer














share|improve this answer



share|improve this answer








edited Nov 26 '18 at 2:20

























answered Nov 23 '18 at 2:00









JaiJai

5,87411233




5,87411233













  • Still getting too many instances of PersonDTO

    – InternCoder
    Nov 23 '18 at 15:11











  • your answer avoids stackoverflow but why am I getting too many instances in memory of PersonDTO and CompanyDTO. For example, if I have 30 Person (DAO) and 3 Company (DAO), I think CompanyDTO should be 3 and PersonDTO should be 30. is this possible?

    – InternCoder
    Nov 23 '18 at 15:24











  • @InternCoder May not be what you want, but updated answer.

    – Jai
    Nov 26 '18 at 2:22



















  • Still getting too many instances of PersonDTO

    – InternCoder
    Nov 23 '18 at 15:11











  • your answer avoids stackoverflow but why am I getting too many instances in memory of PersonDTO and CompanyDTO. For example, if I have 30 Person (DAO) and 3 Company (DAO), I think CompanyDTO should be 3 and PersonDTO should be 30. is this possible?

    – InternCoder
    Nov 23 '18 at 15:24











  • @InternCoder May not be what you want, but updated answer.

    – Jai
    Nov 26 '18 at 2:22

















Still getting too many instances of PersonDTO

– InternCoder
Nov 23 '18 at 15:11





Still getting too many instances of PersonDTO

– InternCoder
Nov 23 '18 at 15:11













your answer avoids stackoverflow but why am I getting too many instances in memory of PersonDTO and CompanyDTO. For example, if I have 30 Person (DAO) and 3 Company (DAO), I think CompanyDTO should be 3 and PersonDTO should be 30. is this possible?

– InternCoder
Nov 23 '18 at 15:24





your answer avoids stackoverflow but why am I getting too many instances in memory of PersonDTO and CompanyDTO. For example, if I have 30 Person (DAO) and 3 Company (DAO), I think CompanyDTO should be 3 and PersonDTO should be 30. is this possible?

– InternCoder
Nov 23 '18 at 15:24













@InternCoder May not be what you want, but updated answer.

– Jai
Nov 26 '18 at 2:22





@InternCoder May not be what you want, but updated answer.

– Jai
Nov 26 '18 at 2:22


















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53427651%2fcode-design-how-to-ensure-that-dto-objects-refer-to-the-same-object-avoid-stac%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

"Incorrect syntax near the keyword 'ON'. (on update cascade, on delete cascade,)

Alcedinidae

Origin of the phrase “under your belt”?