Spring-boot One-to-One mapping with POST method - spring

Hello I am trying to make a reservation sysytem and create a reservation using a post method and assign one reservation to one item.
Do you have any tips or advices?
#OneToOne(mappedBy = "car")
private Reservation reservation;
#OneToOne(cascade = CascadeType.ALL)
#MapsId
#JoinColumn(name = "fk_car_id")
private Car car;
I think mapping is correct - I have difficulties with creating a post method
#PostMapping("/car/{carId}")
Reservation addReservationWithCar(#RequestBody Reservation reservation, #PathVariable Long carId){
Car car = carRepository.findById(carId);
reservation.assignCar(car);
return reservationRepository.save(reservation);
//return ResponseEntity.status(201).body(this.carTypeRepository.save(carType));
}

Related

Spring Boot: How to create a new entity that references exisiting ones (by Id) [duplicate]

I have been playing around with JPA and came across this scenario and wanted to know how to solve this.
I have 2 tables namely Company and Employee.
So here an employee can work for only 1 company, therefore #OneToOne uni-directional mapping is done in Employee class. And the company details in the Company table would already exist.
So when I try to insert a record into Employee table, I even create a reference to the company, add it to the Employee class and save it using my EmployeeRepository's save method. Since the company_id would already exist in Company table, it should just refer it instead of creating a new company. But this is not happening.
I get an exception saying company object is still in the transient state. I can solve this by adding CascadeType.All but I don't need to insert the company rather I have to just link the company to the employee.
Here are the code snippets.
Employee Class
#Entity
#Table(name = "employee")
#Setter
#Getter
public class Employee
{
#Id
#GeneratedValue
private Long id;
#Column(name = "employee_id")
private Integer employeeId;
#Column(name = "employee_name")
private String employee_name;
#OneToOne
#JoinColumn(name = "company_id")
private Company company;
}
Company class
#Entity
#Table(name = "company")
#Setter
#Getter
public class Company
{
#Id
#GeneratedValue
#Column(name = "company_id")
private Long id;
#Column(name = "company_name")
private String companyName;
}
Company Table
company_id company_name
1 Dell
2 Apple
3 Microsoft
Here is how I am creating the Employee object
Employee emp = new Employee();
emp.setEmployeeId(10);
emp.setEmployeeName("Michael");
Company company = new Company();
company.setId(1);
emp.setCompany(company);
employeeRepository.save(emp);
Please, someone, let me know how to link the company class to an employee class rather than saving one more company.
The best solution for me is to lazy load your company with a proxy. To do it with Spring Data JPA, you need to make your company repository extends JpaRepository. That give you access to the method getReferenceById :
Employee emp = new Employee();
emp.setEmployeeId(10);
emp.setEmployeeName("Michael");
emp.setCompany(companyRepository.getReferenceById(1))
employeeRepository.save(emp);
If there is no company for the given id, an EntityNotFoundException is throw.
With a proxy, you avoid the request to the database in most case because Hibernate use its cache for check the existence of the company. But if my memory serves me correctly, Hibernate gonna make a request to the database at each call to a getter of the proxy, except for getId(). So, in your case it's a good solution, but don't use it all the time.
Assuming the Company may have more than one Employee the relation is a ManyToOne, not a OneToOne.
If you want to reference an existing entity, load it instead of creating a new one:
Employee emp = new Employee();
// ...
emp.setCompany(companyRepository.findById(1));
employeeRepository.save(emp);

Spring MVP Forms overwriting Many to Many ArrayList when updating an object

I have a simple project that has a User model, Sports team model and a Many To Many table where a user can "like" the sports team.
User
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(
name = "likes",
joinColumns = #JoinColumn(name = "user_id"),
inverseJoinColumns = #JoinColumn(name = "team_id")
)
private List<Team> teamsLiked;
Team
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotBlank
#Size(min=2, max=30)
private String teamName;
#NotBlank
private String city;
private String sport;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(
name = "likes",
joinColumns = #JoinColumn(name = "team_id"),
inverseJoinColumns = #JoinColumn(name = "user_id")
)
private List<User> likers;
My problem is, when I'm using Spring MVC forms for a user to edit a team, upon submission it completely wipes out existing likes on the Team object under likers. On the edit page, I am using #ModelAttribute and pre populating the existing team object, and have tried to put the likers as a hidden attribute so the data will persist, but that throws an error. I've tried on the #PostMapping backend, to set the origin list of likers before re-saving the DB and that's not working either. Besides using Normal HTML forms to update an object, is there a way I can have the list of users who liked a team persist after updating? Thanks in advance.
What you need here is a DTO and map that onto an existing entity. I think this is a perfect use case for Blaze-Persistence Entity Views.
I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.
A DTO model for your use case could look like the following with Blaze-Persistence Entity-Views:
#EntityView(Team.class)
#UpdatableEntityView
public interface TeamDto {
#IdMapping
Long getId();
String getTeamName();
void setTeamName(String teamName);
String getCity();
void setCity(String city);
String getSport();
void setSport(String sport);
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
TeamDto a = entityViewManager.find(entityManager, TeamDto.class, id);
The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features
Page<TeamDto> findAll(Pageable pageable);
The best part is, it will only fetch the state that is actually necessary!
And in your case of saving data, you can use the Spring WebMvc integration
that would look something like the following:
#Transactional
#PostMapping("/teams")
void save(#RequestBody TeamDto dto){
repository.save(dto);
}

Failed jpa query : Cannot call sendError() after the response has been committed

I want to get rooms by HospitalId, there relation between classes like it looks below:
#Entity(name = "rooms")
#Table( name = "rooms",
uniqueConstraints = {
#UniqueConstraint(columnNames = "roomNumber")
})
public class Room {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private Long idRoom;
private String roomNumber;
#ManyToOne
(fetch = FetchType.EAGER)
#JoinColumn(nullable = false, name = "idhospital")
private Hospital hospital;
this is the query i used :
#Query("select s from rooms s where s.hospital.idHospital =:hospital")
List<Room> findRoomsByHospital( #Param("hospital") Long hospital);
Error in the back :
java.lang.IllegalStateException: Cannot call sendError() after the response has been committed
at org.apache.catalina.connector.ResponseFacade.sendError(ResponseFacade.java:472) ~[tomcat-embed-core-9.0.24.jar:9.0.24]
Is send data but wrong form to the frontend :
error
I really need help can't understand how to deal with it.
I fount the problem :The repitation of data
Solution in to add this notation #JsonIgnore like this :
#ManyToOne
(fetch = FetchType.EAGER)
#JoinColumn(nullable = false, name = "idhospital")
#JsonIgnore
private Hospital hospital;
This is used for to restrict the data to repeat itself
I think you are query is not correct. You have used rooms there without using nativeQuery=true.
you can try below in RoomRepository
#Query("select s from Room s where s.hospital.idHospital =:hospital")
List<Room> findRoomsByHospital( #Param("hospital") Long hospital);
changed rooms to Room
Alternatively, you can try query by a method as shown below, you don't need to use the #Query in this case.
If you are passing hospitalId long use below
List<Room> findRoomsByHospitalIdHospital ( #Param("idHospital") Long hospital);
If you are passing hospital Object use below
List<Room> findRoomsByHospital ( #Param("Hospital ") Hospital hospital);

"could not initialize proxy - no Session" For Multiple ManyToMany relationships in the parent

I have a Parent User Class that has multiple ManyToMany Relationships.
#Table(name = "user")
public class User {
..
#ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.MERGE, CascadeType.DETACH})
#JoinTable(
name = "user_address",
joinColumns = { #JoinColumn(name = "user_id")},
inverseJoinColumns = { #JoinColumn(name = "address_id")}
)
#JsonIgnore
private final List<Address> addresses = new ArrayList<Address>();
#ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.MERGE, CascadeType.DETACH})
#JoinTable(
name = "reports",
joinColumns = { #JoinColumn(name = "user_id")},
inverseJoinColumns = { #JoinColumn(name = "reports_id")}
)
#JsonIgnore
private final List<Reports> reports = new ArrayList<Reports>();
}
When I access the FIRST ManyToMany property, everything works fine. However, immediately after
accessing the first, when I try to access the SECOND ManyToMany Property I get the "could not initialize proxy - no Session" exception:
#Component
public class Combiner {
public void combineData() {
...
List<Address> addresses = user.getAddress(); // This works
List<Reports> reports = user.getReports(); // Get the error here
..
}
}
The Address and Reports classes have the inverse relationship as many ManyToMany back to the User Entity Above.
public class Address {
#ManyToMany(mappedBy = "addresses", fetch = FetchType.LAZY)
private final List<User> users = new ArrayList<User>();
}
public class Reports {
#ManyToMany(mappedBy = "reports", fetch = FetchType.LAZY)
private final List<User> users = new ArrayList<User>();
}
I tried searching SO for the same error where there are MULTIPLE relationships like mine and the first passes but second fails, but could'nt find a post (or google couldn't understand the search terms, if anyone knows a pre-existing one - please let me know).
Could someone assess what else Im missing?
I've tried these so far to no avail:
Added #Transactional to the parent Service class that calls Combiner above
Made the second failing relationship EAGER. (as i understand it you cant make BOTH EAGER since i get a multiple bags error probably because of Cartesian join)
AM Using SpringBoot (2.2.4) with Hibernate Core {5.4.10.Final}
Approach one:
Make #ManyToMany uni-directional. The exception clearly says it can not initialize the collection of role you have in User class.
As you asked in the comment section Why can't this use case be Bi Directional - You can make this bi-directional as well.
Approach two: make collection of role EAGER or use Hibernate.initialize() to initialize the collection.
Bonus: you can make both collection EAGER by using Set not List.

Hibernate 2 records being inserted for Single JAVA Object

I am using Hibernate Implementation of JPA with Spring.
Class Country{
#OneToMany(mappedBy="Country", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
List<State> stateList;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "Current_State_ID")
State currnetState;
}
Class State{
#ManyToOne
#JoinColumn(name="Country_ID")
private Country country;
}
State stateObj = new State();
country.getStateList().add(stateObj);
country.setCurrnetState(stateObj);
countryRepository.saveAndFlush(country);
countryRepository is a JPA Repository Implemenntation.
This creates 2 entries for in State Table, which messes up my logic. Can someone please point me what I am doing worng.
I am not sure why but changing my code to following works for me.
List<State> stateList = new ArrayList<State>();
stateList.add(state);
country.setStateList(stateList);
Creating a New List instance and setting it to country.

Resources