Problems in mapping objects between the model and DTO - java

I am mapping between the following models:
#Entity
#Table(name="account_type")
#NamedQuery(name="AccountType.findAll", query="SELECT a FROM AccountType a")
public class AccountType implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
#Column(name="account_type_id")
private Integer accountTypeId;
#Column(name="account_type_code")
private String accountTypeCode;
#OneToMany(mappedBy="accountType")
private Set<Account> accounts;
Which has a set of Account:
#Entity
#NamedQuery(name="Account.findAll", query="SELECT a FROM Account a")
public class Account implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
#Column(name="account_id")
private Integer accountId;
#Column(name="account_number")
private String accountNumber;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="account_type_id_fk")
private AccountType accountType;
And their DTOs.
I am having problems in mapping complex types like Account:
public static Account getAccount(AccountDTO dto) {
Account model = new Account();
model.setAccountId(dto.getAccountId());
model.setAccountNumber(dto.getAccountNumber());
model.setAccountType(dto.getAccountType());
// Error: can't convert from AccountypeDTO to AccountType
return model;
}
It gives an error that it can't convert from AccountypeDTO to AccountType
so I did the following:
model.setAccountType(getAccountType(dto.getAccountType()));
Where getAccountType method is:
public static AccountType getAccountType(AccountTypeDTO dto) {
AccountType model = new AccountType();
model.setAccountTypeId(dto.getAccountTypeId());
model.setAccountTypeCode(dto.getAccountTypeCode());
model.setAccounts(dto.getAccounts());
// Now here again a similar error
}
I think it's a deep recursive? How to solve this?
My question is how to convert them efficiently.
Annex
The code of acountTypeDTO:
#Component
#Scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS)
public class AccountTypeDTO implements Serializable {
private static final long serialVersionUID = 1L;
#NotNull
#NotEmpty
private Integer accountTypeId;
#NotNull
#NotEmpty
private String accountTypeCode;
private Set<AccountDTO> accounts;
The code of AccountDTO:
#Component
#Scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS)
public class AccountDTO implements Serializable {
private static final long serialVersionUID = 1L;
#NotNull
#NotEmpty
private Integer accountId;
#NotNull
#NotEmpty
private String accountNumber;
private AccountTypeDTO accountType;

Two alternative approaches jump to mind but they will require some change. I will preface this by saying that I have yet to be in a situation where converting to DTOs (even if I'm doing a deep recursive conversion) is the bottleneck. Even if your performance requirements or your scale were so large that it somehow did become a bottleneck then I would personally recommend dividing the work across multiple servers before I started to worry about performance down to that level of detail. Also, it may seem inefficient but performance is rarely intuitive, have you confirmed that this conversion is a bottleneck?
The first alternative is to not use separate classes as DTOs. Some approaches use the same class as the DTO and the underlying entities and some approaches use the DTO as the parent class and the entity class as the child class. This will save you from having to do any kind of DTO<->Entity conversion. There are drawbacks, as this almost always ends up combining two responsibilities into a single class and it can make your code more complex and less readable.
The second alternative is to not return the accounts themselves but instead to convert them to IDs. In this approach your AccountTypeDTO would have a Set<Integer> accountIds instead of a Set<AccountDTO> accounts. However, this only works if your client doesn't always need to operate on every account.

Related

Create a new JPA Entity to produce a new object from two tables

Hello programming council, this is my first use of JPA in anger.
I have 2 Tables:
Entity
#Table(name="category")
public class Category {
#Id
#Column(name="id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
#Column(name="category")
private String category;
#Column(name="budget")
private double budget;
#Column(name="savings")
private String savings;
#Column(name="archive")
private String archive;
Entity
#Table(name="Transaction")
public class Transaction {
#Id
#Column(name="transaction_no")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private long transactionNo;
#Column(name="transaction_date")
private String transactionDate;
#Column(name="transaction_category")
private String transactionCategory;
#Column(name="transaction_description")
private String transactionDescription;
#Column(name="transaction_amount")
private double transcationAmount;
#Column(name="transaction_auto")
private String transactionAuto;
I want to create a new object called Tile which will contain String category and String balance, the SQL for which would be:
select t.transaction_category as category, sum(t.transaction_amount) as balance
from budgeteer.category c
join budgeteer.transaction t
on c.category = t.transaction_category
group by t.transaction_category;
What is the easiest/best way for me to accomplish this?
Thanks in advance.
Ok, so after a little more research, I discovered that I could actually just do this with the same Entity, repository and service without generating a table. You just need to leave out the #Table annotation when you create your entity.

Automatically convert Spring JPA Entity OneToMany to List<ID> in DTO and back (ModelMapper)

Animal.java
#Data
#Entity
public class Animal implements MyEntityInterface {
public enum Sex {MALE, FEMALE}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
private Sex sex;
private boolean castrated;
#OneToMany
private List<Symptom> symptoms;
}
AnimalDTO.java
#Getter
#Setter
public class AnimalDTO implements Serializable {
private long id;
private String name;
private Animal.Sex sex;
private boolean castrated;
private List<Long> symptoms;
}
I wish for a list of Symptoms to be automatically mapped to a list of ID's. This could be achieved in many ways, such as creating a TypeMap, creating a Converter or even just by creating a method in AnimalDTO.java:
public void setSymptoms(List<Symptom> symptoms) {
if (symptoms != null)
this.symptoms = symptoms.stream().map(s -> s.getId()).collect(Collectors.toList());
}
But now imagine it's not only Symptoms, but 50 other fields too. That's a lot of code for the same functionality. And then, it's not only Animal to AnimalDTO, but another 30 different classes with their respective DTOs too.
Also, that still leaves the way back open. From ID to entity. This can (in theory) be achieved easily with the following pseudocode:
List<EntityMemberField.class> list;
for (var entityid : listOfEntityIDsOfDto) {
Object persistedObject = entityManager.find(EntityMemberField.class, entityid);
list.add(persistedObject);
}
...
ModelMapperDestination.setField(list);
This is the same for absolutely every Entity/DTO and should automatically happen for every Entity relationship where the Entity implements MyEntityInterface.
An idea how I could achieve that would be overriding MappingEngineImpl.java from ModelMapper which I register as a Spring Service and inject the EntityManager into, but how could I get ModelMapper to use mine? Or is there maybe an easier way?
The goal is to have a fairly automated conversion from Spring Entities to their corresponding DTO by... just calling modelMapper.map(entity, EntityDTO.class);

How to use part of composite key in JPA repository methods to fetch data?

I have a class Application with EmbeddedId and second column in embbbedID is forgein key and having many to one relationship with offer.
#Entity
public class Application implements Serializable{
private Integer id;
#EmbeddedId
private MyKey mykey;
private String resume;
#Enumerated(EnumType.STRING)
#NotNull
private ApplicationStatus applicationStatus;
#Embeddable
public class MyKey implements Serializable{
private static final long serialVersionUID = 1L;
#NotNull
private String emailId;
#ManyToOne(fetch = FetchType.LAZY)
#NotNull
private Offer offer;
in Offer class mapping is done on jobTitle.
#Repository
interface ApplicationRepository extends JpaRepository <Application,MyKey>
{
List<Application> findAllByMyKey_Offer(String jobTitle);
}
Trying this but getting no success...
I want to fetch All application regarding a specific jobTitle.
What shud be my method name in ApplicationRepository class.
Your methode name is wrong, the right one is findAllByMykey_Offer or findAllByMykeyOffer, as your field is named mykey.
As mentionned in the documentation https://docs.spring.io/spring-data/jpa/docs/2.1.0.RELEASE/reference/html/ using List<Application> findAllByMykey_Offer(String jobTitle); is better than List<Application> findAllByMykeyOffer(String jobTitle);
With composite key, your field name should include name of the field of embedded id. In you case it would be like this (I haven't tested it)
List<Application> findAllByMykeyOffer(String jobTitle);
Notice that k is lower-case here, because your field in class Application is named mykey, not myKey

Group and sort a collection of collections using JPA

I am looking to make a REST controller that will return a sorted list of various objects.
I have created a DTO to hold these collections like the following, but this will not work as it will group by entity:
public class AllReportsDTO {
private List<AReport> aReports;
private List<BReport> bReports;
private List<CReport> cReports;
...
}
I then have the following Domain objects
#Entity
#Table(name = "a_report")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class AReport implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "title")
private String title;
#Column(name = "description")
private String description;
#Column(name = "time_of_report")
private ZonedDateTime timeOfReport;
And one for each Report.
What I want to do is create an endpoint that will return a list of all these reports but in order of time of the report and not grouped by report. How can I achieve this?
I have tried writing it in the repository with a HQL query and grouping by time, but the issue is that each time field has a different name in each report which I can not alter due to this system being used in other places.
You can create methode that sort your sets. Try to adept this one
Collections.sort( aReports, new Comparator<Object>() {
public int compare(MyObject o1, Object o2) {
return o1.getTimeOfReport().compareTo(o2.getTimeOfReport());
}
});
I wouldn't try a pure HQL solution, or a solution from your ORM. I would go to the Java way.
Add an interface
public interface ITimedReport {
ZonedDateTime getTime();
}
Make all your report class implements this interface by returning their own timestamp
Add a method getAllReports on AllReportsDTO.
This method should fill a List<ITimedReport> with all reports, and then sort the list with a Comparator<ITimedReport>. This comparator would rely on the getTime() to compare.
You can add anything meaningfull for a report in the interface, like a getTitle, getDescription, ...

optimization hibernate

I have entity that use storage function. It's good, but some case I don't need in this fields (fieldStoreFunc1, fieldStoreFunc2..) and use Class B and fieldStoreFuncs only when it's necessary:
#Entity
#Table(name = "table")
#SequenceGenerator(name = "JDE_SEQUENCE", sequenceName = "JDE_SEQUENCE", allocationSize = 1)
public class EntityClass implements Trackable {
#Id
#GeneratedValue(generator = "JDE_SEQUENCE", strategy = GenerationType.SEQUENCE)
private Long id;
#Column(name = "name_field")
private String field1;
#Column(name = "name_field")
private String field2;
#Column(name = "name_field")
private String field3;
......
//read only fields
#Formula("store_func(value)")
private String fieldStoreFunc1;
#Formula("store_func(value)")
private String fieldStoreFunc2;
#Formula("store_func(value)")
private String fieldStoreFunc3;
}
so can I divide class into to
class A{
#Column(name = "name_field")
private String field1;
#Column(name = "name_field")
private String field2;
.....
}
class B extends A{
//read only fields
#Formula("store_func(value)")
private String fieldStoreFunc1;
#Formula("store_func(value)")
private String fieldStoreFunc2;
#Formula("store_func(value)")
private String fieldStoreFunc3;
}
No, that's not how you should do.
First, measure, profile, and prove that loading these additional three fields causes a significant overhead and performance problem.
Once, and only once you have this proof, then consider lazy-loading the additional three fields as explained in the documentation. Then measure once again, and prove that the lazy-loading of these three fields does no cause an even bigger overhead and performance problem.
As stated in the documentation:
Hibernate3 supports the lazy fetching of individual properties. This
optimization technique is also known as fetch groups. Please note that
this is mostly a marketing feature; optimizing row reads is much more
important than optimization of column reads. However, only loading
some properties of a class could be useful in extreme cases. For
example, when legacy tables have hundreds of columns and the data
model cannot be improved.
Unless the store_func formula is very expensive, you probably should avoid trying to optimize anything.
Although I agree with JB Nizet if you are still willing to load those properties on demand. And just for the record.
You can achieve in a few ways. The more simplistic one is to create a constructor in your class with the property you always want to load
Please Note that is just an example and is not properly developed
public class A {
...
#Column(name = "name_field")
private String field1;
#Formula("store_func(value)")
private String fieldStoreFunc1;
...
public A(String id, String field1) {
this id = id;
this.field1 = field1;
}
And then in the DAO create a function that loads entities like
public class DAO {
getLight(Long id) {
String named_query = "select a.id, a.field1 from A a where a.id = :id"
// Execute query
}
Another way is to take a look at the Hibernate event or write a custom entity loader.

Categories