Code design: How to ensure that DTO objects refer to the same object, avoid stackoverflow in circular...
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
add a comment |
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
Maybe give us some sample codes so that we know what you are talking about.
– Jai
Nov 22 '18 at 9:30
add a comment |
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
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
java
edited Nov 22 '18 at 15:44
InternCoder
asked Nov 22 '18 at 9:27
InternCoderInternCoder
188
188
Maybe give us some sample codes so that we know what you are talking about.
– Jai
Nov 22 '18 at 9:30
add a comment |
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
add a comment |
2 Answers
2
active
oldest
votes
You can use external libraries for that like ModelMapper, Dozer or MapStruct.
Here is an overview of mapping frameworks.
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
add a comment |
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.
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
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
You can use external libraries for that like ModelMapper, Dozer or MapStruct.
Here is an overview of mapping frameworks.
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
add a comment |
You can use external libraries for that like ModelMapper, Dozer or MapStruct.
Here is an overview of mapping frameworks.
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
add a comment |
You can use external libraries for that like ModelMapper, Dozer or MapStruct.
Here is an overview of mapping frameworks.
You can use external libraries for that like ModelMapper, Dozer or MapStruct.
Here is an overview of mapping frameworks.
edited Nov 22 '18 at 9:33
answered Nov 22 '18 at 9:30
Axel PAxel P
1,81821425
1,81821425
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
add a comment |
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
add a comment |
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.
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
add a comment |
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.
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
add a comment |
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.
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.
edited Nov 26 '18 at 2:20
answered Nov 23 '18 at 2:00
JaiJai
5,82811231
5,82811231
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
add a comment |
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
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
Maybe give us some sample codes so that we know what you are talking about.
– Jai
Nov 22 '18 at 9:30