Failed to find by foreign key - spring-boot

I want to display all the rooms belong to the hospital of the current user
Class Room :
#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;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "room")
private Set<Bed> beds;
//getter and setter
Repository :
#Query("SELECT rm from rooms rm where rm.hospital=?1" )
List<Room> findRoomsByHospital(Hospital hospital);
Service :
List<Room> getRoomsByHospital(Hospital hospital);
ServiceImpl :
#Override
public List<Room> getRoomsByHospital(Hospital hospital) {
return roomRepository.findRoomsByHospital(hospital);
}
Controller :
#GetMapping("/display/room")
public ResponseEntity<List<Room>> getRoomsbyHospitalId(Authentication authentication) {
logger.info("Getting all RoomsByHospitalId from room table...Call getRoomsbyHospitalId ");
// to get the current user
Optional<User> user = userRepository.findByUsername(authentication.getName());
// to get the id of the hospital of the current user
Hospital hospital = user.get().getHospital();
logger.info("Data extracted from room table...");
//return ResponseEntity.ok(HospitalRooms);
return new ResponseEntity<>(roomService.getRoomsByHospital(hospital), HttpStatus.OK) ;
}
I had this error :
enter image description here
enter image description here
=========================================================================
help please!

I had the same problem. Solution in to add this notation #JsonIgnore here
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(nullable = false, name = "idhospital")
private Hospital hospital;

Just NullPointerException doesn't really show where It's comming from, to help you debbug in the application.properties write the following:
spring.jpa.show-sql=true
spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=create
When running now It's going to show you how hibernate is handling your queries where are the null Objects or if it's getting the error before that. Here you are getting just an Id and It's being instance a HospitalModel
// to get the id of the hospital of the current user
Hospital hospital = user.get().getHospital();
Try just doing that instead:
Long hospital = user.get().getHospital();

Related

How does caching works for many to one mapping in hibernate

I have two entities station and company .
Station has many to one mapping with company. When Iam trying to fetch the list of stations based on company . It always hit the database. Below is my station entity class
#Table(name = "station")
#Setter
#Getter
#Builder
#Cacheable
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Station {
public Station() {}
#Id
#SequenceGenerator(name = "mySeqGen", sequenceName = "station_seq",allocationSize = 1)
#GeneratedValue(generator = "mySeqGen")
#Column(name = "station_id ")
private Long id;
Double latitude;
Double longitude;
#ManyToOne(fetch = FetchType.LAZY , cascade = CascadeType.ALL)
#JoinColumn(name = "company_id" , unique =true)
#Fetch(value = FetchMode.JOIN)
Below is my code to find station list based on company
#Override
public List<StationDTO> findStations(final FindStationRequestDTO findStationRequestDTO) {
var company = companyRepository.findById(findStationRequestDTO.getRequestCompanyId());
var stationList = stationRepository.findByCompany(company);
return stationList.stream().map( station -> stationMapper.convertToStationDto(station , new StationDTO())).collect(Collectors.toList());
}
caching is working for companyRepository.findById(findStationRequestDTO.getRequestCompanyId());
public interface StationRepository extends JpaRepository<Station, Long> {
#QueryHints({
#QueryHint(name = HINT_CACHEABLE, value = "true")
})
List<Station> findByCompany(final Optional<Company> company);
}
adding QueryHints made it working

Spring Boot JPA Using Many-to-Many relationship with additional attributes in the join table

I have two simple classes Student and Course. I am trying to set up many to many relationship between these classes. I want to use additional table whose PRIMARY KEY is the combination of the primary keys of student and course tables (student_id and course_id).
The student class:
#Entity
#Table(name = "student")
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
public class Student {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "name")
private String name;
#Column(name = "surname")
private String surname;
#OneToMany(mappedBy = "student")
private Set<CourseStudent> courses;
}
The course class:
#Entity
#Table(name = "course")
public class Course {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String courseName;
#OneToMany(mappedBy = "course")
Set<CourseStudent> students;
}
The entity that stores the relationship between course and the student:
#Entity
#NoArgsConstructor
#Data
public class CourseStudent {
#EmbeddedId
CourseStudentKey id;
#ManyToOne
#MapsId("studentId")
#JoinColumn(name = "student_id")
Student student;
#ManyToOne
#MapsId("courseId")
#JoinColumn(name = "course_id")
Course course;
public CourseStudent(Student student, Course course) {
this.student = student;
this.course = course;
this.rating = 0;
}
int rating;
}
Attention: Since I want to have additional features in this entity (for example, storing the rating of the students for courses), I don't want to use #JoinTable idea that we implement in the Student class.
Since I have multiple attributes in the primary key of CourseStudent entity, I used the following class
#Embeddable
#Data
public class CourseStudentKey implements Serializable {
#Column(name = "student_id")
Long studentId;
#Column(name = "course_id")
Long courseId;
}
I have the following POST request to insert the student into a course:
#PostMapping("/insert/students/{studentId}/courses/{courseId}")
public CourseStudent insertStudentIntoCourse(#PathVariable(value = "studentId") Long studentId,
#PathVariable(value = "courseId") Long courseId) {
if (!studentRepository.existsById(studentId)) {
throw new ResourceNotFoundException("Student id " + studentId + " not found");
}
if (!courseRepository.existsById(courseId)) {
throw new ResourceNotFoundException("Course id " + courseId + " not found");
}
CourseStudent courseStudent = new CourseStudent(
studentRepository.findById(studentId).get(),
courseRepository.findById(courseId).get()
);
return courseStudentRepository.save(courseStudent);
}
I have manually added Student and the Course into my local database and send this request by using Postman.
http://localhost:8080/insert/students/1/courses/1
However, I get the following error:
{
"timestamp": "2022-08-04T12:33:18.547+00:00",
"status": 500,
"error": "Internal Server Error",
"path": "/insert/students/1/courses/1"
}
In the console, I get NullPointerException. What is the thing I am doing wrong here?

Spring boot JPA persist manytomany unidirectionnal relationship

I have a Spring Boot project (with JHipster) with 2 JPA Entities : User and Film.
I've created an unidirectionnal ManyToMany relationship between them.
User is the owner of the relationship.
I would like to add films into favorite list of films of user (property 'favorites' in User entity).
But when I try to add a film into favorites list, nothing is persisted into table 'user_film_favorite' (join table between the 2 entities).
The mapping seems ok because when I manually enter data in this join table, I'm able to retrieve the list of films for a user.
I've looked for a lot of similar issues here but can't find where the problem is.
Entity User :
#Entity
#Table(name = "jhi_user")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class User extends AbstractAuditingEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// Other properties
#ManyToMany(cascade = CascadeType.PERSIST)
#JoinTable(
name = "user_film_favorite",
joinColumns = { #JoinColumn(name = "user_id", referencedColumnName = "id") },
inverseJoinColumns = { #JoinColumn(name = "movie_id", referencedColumnName = "id") }
)
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#BatchSize(size = 20)
private List<Film> favorites = new ArrayList<>();
Entity Film :
#Entity
#Table(name = "film")
#Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Film implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#NotNull
#Column(name = "title", nullable = false)
private String title;
#Column(name = "plot")
private String plot;
#Column(name = "rating")
private Float rating;
FilmService :
/**
* Add one film to current user favorites.
*
* #param id the id of the film.
* #return the film saved into user favorites.
*/
#Transactional(readOnly = true)
public Optional<FilmDTO> addToFavorites(Long id) {
log.debug("Request to get Film : {}", id);
Optional<Film> filmOpt = filmRepository.findById(id);
// Get current logged user with his favorites
Optional<User> userOpt = userService.getUserWithFavorites();
if (filmOpt.isPresent() && userOpt.isPresent()) {
User user = userOpt.get();
user.getFavorites().add(filmOpt.get());
userService.save(user);
}
return filmOpt.map(filmMapper::toDto);
}
UserService :
/**
* Save a user.
*
* #param user the entity to save.
* #return the persisted entity.
*/
public User save(User user) {
log.debug("Request to save User : {}", user);
return userRepository.save(user);
}
If anyone could help me that would be really cool ! Thanks in advance :)
You are reading the User from the database so calling save will call EntityManger.merge. Therefor you also need to add
CascadeType.MERGE
to the ManyToMany mapping.

fetch list based on id present in another entity

this is my order entity,
#Data
#NoArgsConstructor
#AllArgsConstructor
#ToString
#Entity
#Table(name = "ordertab")
public class Order {
#Id
private int orderId;
private String orderDate;
#ManyToMany(targetEntity = Medicine.class,cascade = CascadeType.ALL)
#JoinTable(name="ord_med",
joinColumns = {#JoinColumn(name="ord_id")},
inverseJoinColumns = {#JoinColumn(name="med_id")})
private List<Medicine> medicineList;
private String dispatchDate;
private float totalCost;
#ManyToOne(targetEntity = Customer.class,cascade = CascadeType.ALL)
#JoinColumn(name= "custord_fk",referencedColumnName = "customerId")
private Customer customer;
private String status;
}
and this is my medicine entity,
#Data
#NoArgsConstructor
#AllArgsConstructor
#ToString
#Entity
public class Medicine {
#Id
private String medicineId;
private String medicineName;
private float medicineCost;
private LocalDate mfd;
private LocalDate expiryDate;
**#ManyToMany(cascade = CascadeType.ALL, mappedBy = "medicineList")
private List<Order> orderList;** //order/ medicine many to many mapping
// OneToOne Mapping
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "categoryId", referencedColumnName = "categoryId")
private Category category;
in my order service interface i have a method,
List showAllOrder(string medId);
I have to fetch all orders that has the matching med id.
this many to many mapping have created a additional table ord_med with two columns named ord_id,med_id(type foreign keys).In addition to that due to this bidirectional mapping(i believe it is) while creating object of medicine entity its asking me to add orderlist ,how to approach this method or how exactly should i solve this. thankyou.
in your OrderRepository you can implements this method
findByMedicineId(String id);
if i go for findByMedicineId(String id);
it gives error saying no property medicineId is found in Order entity,cuz the property medicineId is in Medicine entity,while defining custom method in repository follows rules, refer https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.query-creation
anyway I have found the solution for this,
public List<Order> getOrderListBasedOnMedicineId(String medicineid) {
Optional<Medicine> med=medicineRepo.findById(medicineid);//find if medicine is present in database with the id.
if(med.isEmpty()) {
return null;
}
List<Order> orders = medicineServ.getOrderList(); //getorderlist defined in service implementation of medicine.
List<Order> ordersWithMedId = new ArrayList();//new list to add all orders that has atleast one medicineId that matches.
for(int i=0;i<orders.size();i++) {
List<Medicine> medicines= orders.get(i).getMedicineList();
for(int j=0;j<medicines.size();j++) {
ordersWithMedId.add(orders.get(i));
}
}
return ordersWithMedId;//returning the list of orders.
}
#Override
public List<Order> getOrderList() {//medicine service implementation
return orderRepo.findAll();
}
//OrderController
#GetMapping("/orders/list/{id}")
public ResponseEntity<List<Order>> getOrderListBasedOnMedicineId(#PathVariable("id") String id) {
List<Order> ord= orderService.getOrderListBasedOnMedicineId(id);
if(ord==null) {
throw new OrderNotFoundException("Order not found with medicine id:"+id);
}
return new ResponseEntity<List<Order>>(orderService.getOrderListBasedOnMedicineId(id),HttpStatus.OK);
}

Unable to save data to composite Table Via Spring Data rest json post

I have 3 Tables in db
training
- training_id (pk)
user_profile
- profile_id (pk)
-training_profile (composite table)
- training_id
- profile_id
I have already record in user_profile table having profile_id=44 and want to create new record for training table ,and also to associate this new training with already existing user_profile record which has id 44,but after post data is saved to training table but it is not inserted into lookup table user_training.
My Object Classes Are
- Training Class
#Entity
#Table(name = "training", schema = "public")
public class Training implements java.io.Serializable {
#Id #GeneratedValue
#Column(name = "training_id", unique = true, nullable = false)
private Long trainingId;
#ManyToMany(fetch = FetchType.LAZY, mappedBy = "trainings")
private Set<UserProfile> userProfiles = new HashSet<UserProfile>(0);
#Column(name = "training_subject", length = 200)
private String trainingSubject;
public Training() {
}
public Long getTrainingId() {
return this.trainingId;
}
public void setTrainingId(Long trainingId) {
this.trainingId = trainingId;
}
public String getTrainingSubject() {
return this.trainingSubject;
}
public void setTrainingSubject(String trainingSubject) {
this.trainingSubject = trainingSubject;
}
public Set<UserProfile> getUserProfiles() {
return this.userProfiles;
}
public void setUserProfiles(Set<UserProfile> userProfiles) {
this.userProfiles = userProfiles;
}
}
UserProfile
#Entity
#Table(name = "user_profile", schema = "public")
public class UserProfile implements java.io.Serializable {
#Id #GeneratedValue
#Column(name = "profile_id", unique = true, nullable = false)
private Long profileId;
#Column(name = "profile_description")
private String profileDescription;
#ManyToMany(fetch = FetchType.EAGER, cascade = { CascadeType.ALL })
#JoinTable(name = "user_training", schema = "public", joinColumns = {
#JoinColumn(name = "profile_id", nullable = false, updatable = false) }, inverseJoinColumns = {
#JoinColumn(name = "training_id", nullable = false, updatable = false) })
private Set<Training> trainings = new HashSet<Training>(0);
public UserProfile() {
}
public String getProfileDescription() {
return this.profileDescription;
}
public void setProfileDescription(String profileDescription) {
this.profileDescription = profileDescription;
}
public Set<Training> getTrainings() {
return this.trainings;
}
public void setTrainings(Set<Training> trainings) {
this.trainings = trainings;
}
}
My json post via postman
And Response I get
Response show that new training record inserted in table having training_id as 67
No association found for this new saved training
again it created new record for training and does not associate with existing user profile , I post curl -i -X POST -H "Content-Type:application/json" -d "{ \"trainingSubject\" : \"Oracle\", \"userProfiles\":[\"/userProfiles/44\"] }" http://localhost:8080/api/trainings
You could use the relative url assignment:
{
"trainingSubject": "oracle",
"userProfiles":["/userProfiles/44"]
}
Maybe also try with the full url: http://localhost:8080/api/userProfiles/44
EDITED
If you move the owning site of the ManyToMany relation to Training it will work with the above JSON. So currently the owner is allowed to set the realtions. If you do it like that:
#ManyToMany
#JoinTable(name = "user_training"
, joinColumns = {#JoinColumn(name = "profile_id") }
, inverseJoinColumns = {#JoinColumn(name = "training_id") })
private List<UserProfile> userProfiles = new ArrayList<>();
plus
#ManyToMany(mappedBy = "userProfiles")
private List<Training> trainings = new ArrayList<>();
Training owns the relation within userProfiles.
I think in your case it's the best option for now. Another option would be, when keeping the owner site at UserProfile on transactions, to update the relation there like:
PATCH http://localhost:8080/api/userProfiles/44
{
"trainings": ["trainings/66", "trainings/67"]
}
But with this you would need multible rest calls (1. POST new training and get the new Id 2. GET current training list 3. PATCH trainings list with newly added training)
Last option would be to add the REST-controller on your own.
Complete files for the first approach:
#Entity
#Table
public class Training implements Serializable {
#Id
#GeneratedValue
private Long trainingId;
#ManyToMany
#JoinTable(name = "user_training"
, joinColumns = {#JoinColumn(name = "profile_id") }
, inverseJoinColumns = {#JoinColumn(name = "training_id") })
private List<UserProfile> userProfiles = new ArrayList<>();
#Column(name = "training_subject", length = 200)
private String trainingSubject;
#Entity
#Table
public class UserProfile implements Serializable {
#Id
#GeneratedValue
private Long profileId;
#Column(name = "profile_description")
private String profileDescription;
#ManyToMany(mappedBy = "userProfiles")
private List<Training> trainings = new ArrayList<>();
public interface TrainingRepository extends JpaRepository<Training, Long> {
}
public interface UserProfileRepository extends JpaRepository<UserProfile, Long> {
}
With the upper JSON this will work, I tested it. You will not see the correct result directly in the response of curl-POST. To see the added relation you must follow the userProfiles-link like GET http://localhost:8080/transactions/<newId>/userProfiles

Resources