Spring: Persisting a list of attributes which has list of other attributes in itself - spring

I am trying to make webservice for food order management . I have a Entities Product and Restaurant and I am struggling to make Order Entity .
#Entity
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#ManyToOne
#JoinColumn(name = "restaurant_id", referencedColumnName = "id", insertable=false, updatable=false)
#JsonBackReference
private Restaurant restaurant;
private int restaurant_id;
private String name;
private float price;
}
#Entity
public class Restaurant {
#Id
#GeneratedValue
private int id;
#NotNull
private String name;
private String city;
private String phone;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "restaurant", cascade = CascadeType.ALL)
#JsonManagedReference
private List<Product> menu;
}
I want the Order entity to have a List. If i keep an attribute OrderId in Product , to Join tables Product and Order, will that be a good idea? Is there any better way to do it?
PS: I don't want to keep order Id with Product because i think Product should not be aware of it

Yes, that's a fine idea.
You can use List in Order entity and join them by having id of order in product.
it's kinda hard to tell what you really want, but this site (under headline relationships) has description of best practices for likely to all of your use cases!
https://vladmihalcea.com/tutorials/hibernate/
Cheers!

Related

How to save an object that contains a list of integers in a postgres database

i have an object called Cart i want to save it in the database with an array/list of integers that rappresents Product's id.
This is my Cart class at the moment:
public class Cart {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private Integer id;
#Column(name = "customer_fk")
private Integer customerFk;
#Column(name = "product_list")
private List<Integer> productList;
}
i saw that postgres has the integer[] datatype
I'm using spring.jpa.hibernate.ddl-auto=create to create the database
How do i tell to spring and jpa to save that list as an integer in the DB?
(if i can)
Is this a good solution?
I actually found a solution that works, using Strings, but im too curious to see if find a way to do it with arrays
The canonical approach here would be to maintain a table of products and then map carts to products in a one to many relationship. You could try:
#Entity
#Table(name = "Cart")
public class Cart {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private Integer id;
#Column(name = "customer_fk")
private Integer customerFk;
#Column(name = "product_list")
#OneToMany(mappedBy="cart", cascade = CascadeType.ALL)
private List<Product> productList;
}
#Entity
#Table(name = "Product")
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private Integer id;
#ManyToOne
#JoinColumn(name = "FK_Cart")
private Cart cart;
// more fields, getters and setters
}
This is not a complete implementation, but the idea is that each product ID should exist in a dedicated products table, and each cart entity should track its product references via a foreign key relationship.

Join Column between entities get NULL value instead of parent entity id number

I am Using Spring Boot on Java to create user's order on his checkout. A new Orders object is created which has a Linked Set of Items. Those items are user's cart contents.
Order is created, but its set of Items is null. The set size is 0. I checked that in JUnit tests. Can you help me to find out what is wrong? Maybe I have defined entities incorrectly? Have a look at the picture of the database:
And check the entities, Orders:
#Entity
public class Orders {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#NotEmpty
#DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime submitedAt;
#NotEmpty
private String orderName;
#NotEmpty
#Column(name="`User`")
private String username;
#Enumerated(EnumType.STRING)
#Column
private OrderStatus status;
#OneToMany(mappedBy = "orders", cascade = { CascadeType.ALL}, fetch = FetchType.LAZY)
private Set<Item> items;
Item:
#Entity
public class Item {
#Id
private Integer id;
#Column(name="`NAME`")
private String dishName;
#Column(name = "`DESCRIPTION`", length = 2000)
private String dishDescription;
#Column(name = "`QUANTITY`")
private Integer quantityInCart;
#Column(name = "`USER`")
private String username;
#ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH })
#JoinColumn(name = "ORDERS_ID")
private Orders orders;
How to do entities relation correctly? Should it be one direction or bi-directional relationship?
What are differences of these relations? And what kind of relationship I should use? Why?
I was doing JUnit tests for the Orders service methods. It turns out that it can create orders. And Order items from user's cart.
But when it is time to show order (GetMapping) then it returns Orders entity with empty items set.
I think it happens because JPA cannot find foreign key of items for its designated order. It is null.
Why is it null?
And this is the service method that creates such order by user request:
#Transactional
public ResponseEntity<String> createOrder (String username) {
User user = userService.findByUsername(username);
List<CartItem> items = cartRepo.findByUser(user);
if(items.size() > 0) {
Orders newOrder = new Orders();
Set<Item> orderItems = new LinkedHashSet<>();
for(CartItem item : items) {
// new Item(Integer id, String dishName, String dishDescription, Integer quantityInCart, String username)
Item orderItem = new Item(item.getId(), item.getDish().getName(),
item.getDish().getDescription(), item.getQuantity(), item.getUser().getUsername());
orderItems.add(orderItem);
}
newOrder.setItems(orderItems);
newOrder.setOrderName(user.getUsername()+"'s order");
newOrder.setStatus(OrderStatus.SUBMIT);
newOrder.setSubmitedAt();
newOrder.setUsername(username);
orderDao.save(newOrder);
cartService.removeAllUserProducts(username);
LOG.info("[{}]: A new order is created successfully.", username);
return new ResponseEntity<String>("A new order is created successfully.", HttpStatus.CREATED);
}
//...
}
I tried to do one direction relationship for other entities and it really created foreign keys on joined column fields. But I want to find out why my bidirectional way of joining is wrong. Maybe someone who really knows can explain.
The Order class should be like this:
#Entity
public class Orders {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#NotEmpty
#DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime submitedAt;
#NotEmpty
private String orderName;
#NotEmpty
#Column(name="`User`")
private String username;
#Enumerated(EnumType.STRING)
#Column
private OrderStatus status;
#OneToMany(cascade = { CascadeType.ALL}, fetch = FetchType.LAZY, orphanRemoval = true)
#JoinColumn(name="ORDERS_ID")
private Set<Item> items;
And Item class without Orders class and its ManyToOne relationship.
Now relationship is unidirectional. Item entity has foreign keys column name ORDERS_ID that has id's of Orders for which Items belong.

Troubles with Bidirectional One-To-One JPA

I'm coding a CRUD JPA web application. My goal is that a given parent Vehicle can only have a single child Driver, but during runtime this same Driver can instead be assigned to another Vehicle and vice versa. To my understanding, this could be accomplished via an OneToOne relationship.
I've tried some different approaches, but to no success. I can assign a Vehicle to a Driver just fine, but when I try to update/create a new Vehicle and give him a Driver, via controllers, nothing happens. I can only do it the other way around. I'm assuming this is because Vehicle is the parent and I can only create a new relation by updating a parent.
My question is, is it possible to make these updates bidirectional and how can I achieve that?
I've tried using a shared primary key, using a foreign key, using a join table. The result is always the same and I can't quite grasp why. I have an OneToMany relationship working on this application and it works as I expect it to work. I can update on one side or the other, delete on one side or the other. Both entities have been updated. OneToOne? Parent seems to have all the power.
This is what I'm working with right now:
Driver
#Entity
#Table(name= "drivers")
public class Driver {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(nullable = false)
private String name;
#Column(nullable = false)
private int age;
#OneToOne(mappedBy = "driver")
private Vehicle vehicle;
Vehicle
#Entity
#Table(name= "vehicles")
public class Vehicle {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(nullable = false)
private String make;
#Column(nullable = false)
private String model;
#Column(nullable = false)
private int mileage;
#Column(nullable = false)
private int year;
#Column(nullable = false)
private int fuel;
#OneToOne
#JoinColumn(name = "driver_id")
private Driver driver;
And just for reference, this is the OneToMany relationship I have and that I'm happy with. I'd like my OneToOne to have the same behavior, except I don't need to save a list of entities, only one.
#Entity
#Table(name="stops")
public class Stop {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(nullable = false)
private String name;
#ManyToOne
#JoinColumn(name="route_id")
private Route route;
#Entity
#Table(name="routes")
public class Route {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(nullable = false)
private String name;
#OneToMany
#JoinColumn(name = "route_id")
private List<Stop> stops = new ArrayList<>();
Any tips would be appreciated, thank you for your time.

Problem with relation #ManyToOne in Spring Boot, Hibernate, JPA

I have classes -> Country and City.
I wanna create works request, that when I call to get all countries, I will get all countries, with cities.
When I call to get all cities, I will get all cities with only countries from Country model.
I wanna add new cities with relation to countries.
My Country model class:
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Id
private Long id;
#NotEmpty(message = "Name of country is mandatory.")
#Column(unique = true)
private String nameOfCountry;
#NotBlank(message = "Name of capital is mandatory.")
private String capital;
#Max(value = 17098242L, message = "Maximum value for population = 10000000000")
private Long surface;
private String countryCode;
private String telephoneCode;
#OneToMany(cascade = CascadeType.PERSIST)
#JoinColumn(name = "country_Id", updatable = false, insertable = false)
private List<CityModelDao> cityModelDao;
My City model class:
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Id
private Long id;
#NotEmpty(message = "Name of country is mandatory.")
#NotNull
#Column(nullable = false, unique = true)
private String nameOfCity;
I know that I don't have here #ManyToOne, but I still do it wrong and now I haven't got more ideas.
My response from get countries:
And this is it what i want.
But when i call to get cities my response is:
Unfortunately I havent got information about country.
In db I have in cities information about fk from country:
Could you help me to do works relation? I ve tried something like:
CityModel:
#ManyToOne()
#JoinColumn(name = "country_Id")
private CountryModelDao countryId;
CountryModel:
#OneToMany(mappedBy = "countryId", orphanRemoval = true, cascade = CascadeType.PERSIST)
private List<CityModelDao> cityModelDao;
But it was wrong. And when I tried with above relation city, I got error.
Could You tell me how to do correct #ManyToOne in this case? What I do wrong?
Thanks
The most simplistic bi-directional OneToMany relationship model should be:
#OneToMany(mappedBy = "countryId")
private List<CityModelDao> cityModelDao;
You set Country as the owner of the relationship Country - City;
You expect an attribute 'countryId' in the CityModelDao;
#ManyToOne
#JoinColumn(name = "country_id")
private CountryModelDao countryId;
You will populate with data based on a join operation that will be executed on the column country_id from the CityModelDao table.
Of course, afterwards, you can enrich the annotations with orphan removal, cascade type etc.
LE:
You are using this via REST and you need to avoid the infinite loop.
Please update the relations to:
#JsonManagedReference
#OneToMany(mappedBy = "countryId")
private List<CityModelDao> cityModelDao;
#JsonBackReference
#ManyToOne
#JoinColumn(name = "country_id")
private CountryModelDao countryId;

FindBy and OrderBy doesn't order accordingly - Spring Data Jpa

I am using this method :
List<Client>findTop10ByGenderOrderBySurvey_Results_ScoreDesc(char gender);
The logic is this :
I have a Client Model, with a reference OneToMany to the Survey Model, and the Survey Model has a reference of OneToOne with the Results model which has the score field.
So one Client can have many surveys each of which has a score.
I wanted to order the Clients By their score, in descending order, and then get top10 Male Clients with highest score.
The method I'm using does filter By Gender, and returns 10 Clients.
But it returns the same Client more than once,because it has several surveys. And not in an ordered manner.
What am I doing wrong and how do I fix this ?
public class Client {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "client_id")
private Long id;
#Column(name = "gender")
private char gender;
#OneToMany(mappedBy = "client", cascade = CascadeType.ALL)
#JsonManagedReference
private List<Survey> survey= new ArrayList<Survey>();
}
public class Survey{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "SURVEY_ID")
private Long Id;
#ManyToOne
#JsonBackReference
#JoinColumn(name = "client_id")
public Client client;
#OneToOne(cascade=CascadeType.ALL)
#JsonManagedReference
#JoinColumn(name = "surveyresult_id")
private Results surveyResults;
}
public class Results {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "SURVEYRESULT_ID")
private Long Id;
private Double score;
}
To filter out duplicate results, use the distinct keyword:
List<Client> findDistinctTop10ByGenderOrderBySurvey_Results_ScoreDesc(char gender);
The OrderBy syntax is incorrect. To order by multiple properties, simply append them like this:
List<Client> findDistinctTop10ByGenderOrderBySurveyDescResultsDescScoreDesc(char gender);
Note: When method names become very long, it is a sign the query might be too complex to be a derived query. It is then recommended to use #Query with a shorter, higher-level method name to describe the query.

Resources