Java Domain Design - java

I've been going back and forth on the best way to design and implement a domain layer in Java while using the SpringJDBC Template. I want to design something that is going to be reusable and robust, while avoiding the n+1 query problems and performance problems. I've come up with a couple cases but not sure which one would be best for use with JDBC. I've used Hibernate previously. What is the best way to approach domain design in Java assuming SpringJDBC will be the technology interacting with the database.
As an example lets say I have an Address, which has a State, which has a Country.
Case 1: Domain object exact match to database
public class Address {
private long id;
private String streetLine1;
private String streetLine2;
private String city
private long stateId;
private String zipCode;
...
}
public class State {
private long id;
private String name;
private String code;
private long countryId;
}
public class Country {
private long id;
private String name;
private String code;
}
In this case the domain objects are an exact representation of a database tables, thus row mapping is simple, and the objects can be persisted into the database.
The major downside I can see to this is, lets say on the user interface I need to list all addresses in the application, and the want Street 1, Street 2, City, State (full name), Postal Code, and Country. If there are 100 address that are returned, I then need to issue two additional queries per row to get the State object, and Country object for a ground total of 201 queries executed, OUCH!
Case 2:
Follow the same design as Case 1, but when you encounter a situation where the UI requires components from multiple domain objects, return a custom DTO object by having the RowMapper map to the DTO object instead of the domain, makes it possible to execute one query. The downside I see here is you may end up with a ton of special case DTO / Model objects.
public class AddressDTO {
private long id;
private String streetLine1;
private String streetLine2;
private String city;
private String state;
private String country;
}
Case 3:
Map similar to how you would in hibernate where domain objects reference each other. However now the RowMappers get more complex, and they may be fetching data unnecessarily. At that point it is expected that the objects would always be populated, since there is no lazy loading like what JPA/Hibernate can do. In order to persist and Address, the Address object would need a valid State object associated. This seems like the least practical solution but I maybe there is something I'm not understanding.
public class Address {
private long id;
private String streetLine1;
private String streetLine2;
private String city
private State state;
private String zipCode;
...
}
public class State {
private long id;
private String name;
private String code;
private County country;
}
public class Country {
private long id;
private String name;
private String code;
private Set<State> states;
}

I would go with Case 2 because when you need to query across domains it makes more sense to use usecase driven DTOs and use SQL Join(s) to get data from the linked domains

Related

How to convert nested Entity to DTO using MapStruct or Java Stream?

In my Spring Boot apps, I generally use projection in order to return joined table results. However, I am trying to use Java Stream to map and return nested entities to the corresponding DTOs. Here is an example that I generally encountered:
Note: I setup entity relationship using Hibernate and that part is completely ok. For this reason, I omitted the related code for brevity. Just concentrate on mapping entities to the nested DTOs.
Country has many States, States has many Towns...
Country:
public class Country {
private Long id;
private String name;
#OneToMany(...)
private Set<State> states = new HashSet<>();
}
State:
public class State {
private Long id;
private Long population;
#ManyToOne(...)
private Country country;
#OneToMany(...)
private Set<Town> towns = new HashSet<>();
}
Town:
public class Town {
private Long id;
private String name;
#ManyToOne()
private State state;
}
I want to get Country list with Country Name, Sum of state population belonging to this country and Town list belonging to the country.
For this purpose, I created the following DTOs, but not sure if it is ok and how can I map the necessary data to this DTO in one step (I don't want to go to database 3 times, instead, I just want to map the country list to the corresponding fields (the list has all of these data as I built the relations properly using Hibernate).
#Data
public class CountryResponse {
private Long id;
private String name;
private Set<StateResponse> states;
private Long population;
private Set<TownResponse> towns;
public CountryResponse(Country country) {
this.id = country.getId();
this.name = country.getName();
// ???
this.states = country.getStates.stream().map(StateResponse::new)
.collect(Collectors.toSet());
this.towns = this.states.stream().map(TownResponse::new)
.collect(Collectors.toSet());
}
}
How can I do this? I would also consider using MapStruct if it is better for this scenario?
To get the sum of state populations, you can use the following stream code
country.getStates().stream().map(State::getPopulation).sum();
and the list of towns can be fetched as follows
country.getStates().stream().map(State::getTowns).flatMap(Set::stream).collect(Collectors.toList());

How can I use the Java stream in order to create an array iterating in all the element of a nested collection?

I am working on a Java application and I am trying to understand if Stream concept can be useful for my use case.
I am trying to do something similar to what I found into an example but my use case seems to be more complex. This was the original example:
String[] profili = utente.getRuoli()
.stream().map(a -> "ROLE_" + a).toArray(String[]::new);
Basically it is creating an array starting from a stream.
Following my situation. I have this main User DTO class representing an user:
#Data
public class User {
private int id;
private String firstName;
private String middleName;
private String surname;
private char sex;
private Date birthdate;
private String taxCode;
private String eMail;
private String pswd;
private String contactNumber;
private Date createdAt;
private Set<Address> addressesList;
private Set<UserType> userTypes;
}
The previous DTO class contains this field representing a collection of user types (for example "ADMIN", "READER", "WRITER", etcetc). This because an user can have multiple types:
private Set<UserType> userTypes;
This is the UserType structure:
#Data
public class UserType {
private int id;
private String typeName;
private String description;
Set<Operation> operations;
}
Also this class contains a field representing a collection of operations (this because a user type can have multiple operation that can perform on the system), this field:
Set<Operation> operations;
Now I am asking if I can use the stream in a similar way to the original example in order to create an array of all the operations contained into all the user types.
Is it possible? What could be a smart solution? Or maybe the best way is the "old style" (iterate on all the object into the userTypes Set, then iterate on all the element into the operations Set and manually add to an array?). It will work but what could be a neat and elegant solution using stream? (if exist)
Use flatMap() on the operations Set, with distinct():
Operation[] operations = user.getUserTypes().stream()
.map(UserType::getOperations)
.flatMap(Set::stream)
.distinct()
.toArray(Operation[]::new);
It sounds like what you want is just
userList.stream()
.flatMap(user -> user.getUserTypes().stream())
.flatMap(userType -> userType.getOperations().stream())
.toArray(Operation[]::new);
...assuming you're using getters. This gives you every operation for every user type for every user.

hibernet one to many on web

I am completely new in Hibernate ORM world, recently reading mapping specially one-to-many relation. But i am facing some problem to understand.
I am using this link to understand hibernate relation.
https://dzone.com/tutorials/java/hibernate/hibernate-example/hibernate-mapping-one-to-many-using-annotations-1.html
please have a look the code.
public class Student {
private long studentId;
private String studentName;
private Set<Phone> studentPhoneNumbers
//setter getter
}
public class Phone {
private long phoneId;
private String phoneType;
private String phoneNumber;
//setter getter
}
i understand it.
confusion is here.
Set<Phone> phoneNumbers = new HashSet<Phone>();
phoneNumbers.add(new Phone("house","32354353"));
phoneNumbers.add(new Phone("mobile","9889343423"));
Student student = new Student("Eswar", phoneNumbers);
session.save(student);
Does this only one code session.save(student), store value in both table ? if no then why we didn't write code to save phoneNumbers like session.save(phoneNumbers).
Once this above code is executed then the value will be stored in both table or object(STUDENT and PHONE) right ?. I think each time we execute this code, this code insert value in both table. i don't like it. i want value should be store on only second table(PHONE).
So how will i store value in only PHONE object ?
or how will we implement this relation where user will select STUDENT through combobox and fill value in PHONE object through simple textboxes. then finally save it.like product/category.
3.please help or suggest some good tutorial where hibernate is implemented (specially one-to-many and many to many relation) in web application. i have seen many tutorial but all these are implemented through main method.
sorry for the poor English
thanks.
Yes it should save both student and phone number in their own table. If you annotate the right way. Student should be something like this:
#Entity
#Table(name = "student")
public class Student {
#id
#GeneratedValue
private long studentId;
#column(name = "studentName")
private String studentName;
#OneToMany
private Set<Phone> studentPhoneNumbers
//setter getter
}
public class Phone {
private long phoneId;
private String phoneType;
private String phoneNumber;
//setter getter
}
Then there is also some configuration to do. I'd advice you to read this tutorial: https://www.tutorialspoint.com/hibernate/index.htm
In the case of your code it doesn't much. But if you configure hibernate the right way you can decide in which table/column a value should be saved
Hibernate tutorial - Tutorials point

Morphia: can two objects pull from the same collection if one is a subset of the other?

Lets say I have two very basic classes that share most of their information that I want to store in mongo. Like this:
public class Customer{
#id
private int customerID;
private String Name;
private int social;
private long balance;
}
public class CustomerInfo{
#id
private int customerID;
private String Name;
private int social;
private long balance;
private Address address;
private PhoneNumber phoneNumber;
}
If I don't want to have two nearly-identical collections when one should do. Can I create a single collection for CustomerInfo and still populate the Customer class with the records contained inside, since they are a subset of CustomerInfo?
I know I can load customerInfo with only the values I care about, but I would like something that is a little more elegant, and which plugs into my existing format better.
#Entity(value="customers", noClassnameStored=false)
public class Customer {
#Id
private ObjectId id;
private String Name;
private int social;
private long balance;
}
public class CustomerInfo extends Customer {
private Address address;
private PhoneNumber phoneNumber;
}
It should work like that. Since you are storing the class name in each document, you can easily query for the one you want to use; even though Morphia would figure this out on its own based on the attributes.
PS: And unless you know what and why you are doing it, keep your ID an ObjectId.
PPS: You might want to use object data types instead of primitives in case they can be empty — in which case they won't be stored and use up space in in your database.

How to save a mapping of similar data in DB using JPA

I don't know how to design this rdbms problem:
Let's say, I have a customer, who is making a purchase. By making the purchase he leaves his data. I have a second dataset with real customer data. After collecting the data, I want to find a mapping of the collected customer data to the real customers by an algorithm.
It's still unclear how to save this mapping/these links created by the algorithm.
Proposal:
#Entity
public class Purchase{
#OneToOne
private Customerdata customerData;
}
#Entity
public class CustomerData{
private String firstName;
private String Lastname;
private String city;
}
#Entity
public class RealCustomer{
#OneToMany(cascade=CascadeType.ALL, mappedBy ="RealCustomer")
private List<CustomerData> customerData = new ArrayList<CustomerData>();
private String firstName;
private String Lastname;
private String city;
}
Now I can save my assumed mapping from RealCustomer data to customer data in the customerData list.
I guess that's not the best idea?
Hoping for nice suggestions from you. Can I take advantage of the similarity of real customer data and obtainer customer data.
Please note that I want to keep inconsistent data e.g. even if city is similar ('Washington D.C.' and 'Washington'. I don't want to lose this information that I can run the matching algorithm later again.
Note: I'm okay, if you provide me the rough idea. Java/JPA-Code not necessary.
Thank you very much in advance!
You can do something like below
#Entity
public class Purchase{
#OneToOne
private Customer customer;
}
#Entity
public class Customer{
private String firstName;
private String Lastname;
private String city;
//Below association will only come in picture if customer has real customer data available when you are collecting the data this way you can clearly identify and separate collected vs real customers
#OneToOne
Customer realCustomer;
}

Categories

Resources