fetch list based on id present in another entity - spring-boot

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);
}

Related

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?

How to use #NamedEntityGraph with #EmbeddedId?

I'm trying to have Spring Data JPA issue one query using joins to eagerly get a graph of entities:
#Entity
#NamedEntityGraph(name = "PositionKey.all",
attributeNodes = {#NamedAttributeNode("positionKey.account"),
#NamedAttributeNode("positionKey.product")
})
#Data
public class Position {
#EmbeddedId
private PositionKey positionKey;
}
#Embeddable
#Data
public class PositionKey implements Serializable {
#ManyToOne
#JoinColumn(name = "accountId")
private Account account;
#ManyToOne
#JoinColumn(name = "productId")
private Product product;
}
Here's my Spring Data repo:
public interface PositionRepository extends JpaRepository<Position, PositionKey> {
#EntityGraph(value = "PositionKey.all", type = EntityGraphType.LOAD)
List<Position> findByPositionKeyAccountIn(Set<Account> accounts);
}
This produces the following exception:
java.lang.IllegalArgumentException: Unable to locate Attribute with the the given name [positionKey.account] on this ManagedType
I want all of the accounts and products to be retrieved in one join statement with the positions. How can I do this / reference the embedded ID properties?
I would suggest refactoring the entity this way if it possible
#Entity
#NamedEntityGraph(name = "PositionKey.all",
attributeNodes = {#NamedAttributeNode("account"),
#NamedAttributeNode("product")
})
#Data
public class Position {
#EmbeddedId
private PositionKey positionKey;
#MapsId("accountId")
#ManyToOne
#JoinColumn(name = "accountId")
private Account account;
#MapsId("productId")
#ManyToOne
#JoinColumn(name = "productId")
private Product product;
}
#Embeddable
#Data
public class PositionKey implements Serializable {
#Column(name = "accountId")
private Long accountId;
#Column(name = "productId")
private Long productId;
}
Such an EmbeddedId is much easier to use. For instance, when you are trying to get an entity by id, you do not need to create a complex key containing two entities.

Springboot add problem in oneTOMany relation

I'm writing 3 tables in the following relation:
Club class:
#Setter
#Getter
#Entity
#Table(name = "Club")
public class Club {
#Id
#GeneratedValue
private Long id;
private String name;
private String type;
private String mainPage;
private String logo;
#OneToMany(mappedBy="clubProductKey.club", cascade = CascadeType.ALL)
#JsonIgnoreProperties(value = "clubProductKey.club", allowSetters=true)
private Set<ClubProduct> clubProducts;
...
Product class:
#Setter
#Getter
#Entity
#Table(name = "Product")
public class Product {
#Id
#GeneratedValue
private Long id;
#OneToMany(mappedBy="clubProductKey.product", cascade = CascadeType.ALL)
#JsonIgnoreProperties(value = "clubProductKey.product", allowSetters=true)
private Set<ClubProduct> clubProducts;
...
ClubProduct class:
#Setter
#Getter
#Entity
#Table(name = "ClubProduct")
public class ClubProduct {
#EmbeddedId
private ClubProductKey clubProductKey;
...
ClubProductKey class:
#Setter
#Getter
#Embeddable
public class ClubProductKey implements Serializable {
#ManyToOne(cascade = {CascadeType.MERGE,CascadeType.REFRESH })
#JoinColumn(name = "club_id", referencedColumnName = "id")
#JsonIgnoreProperties(value = "clubProducts", allowSetters=true)
private Club club;
#ManyToOne(cascade = {CascadeType.MERGE,CascadeType.REFRESH })
#JoinColumn(name = "product_id", referencedColumnName = "id")
#JsonIgnoreProperties(value = "clubProducts", allowSetters=true)
private Product product;
...
ClubProductRepository class:
public interface ClubProductRepository extends JpaRepository<ClubProduct, ClubProductKey> {
public List<ClubProduct> findByClubProductKeyClub(Club club);
public List<ClubProduct> findByClubProductKeyProduct(Product product);
}
I try to save clubProduct like this:
#Service
public class ClubProductServiceImp implements ClubProductService {
#Autowired
private ClubProductRepository clubProductRepository;
...
ClubProduct savedClubProduct = clubProductRepository.save(clubProduct);
return savedClubProduct;
}
However I find that the clubProduct is not saved in the clubProducts list in the club or product entity, the list is null. Must I add lines like club.getClubProducts.add(clubProduct) or is there any other way to make it added automatically?
Thank you.
The #OnetoMany mapping in your Club class uses the attribute mappedby which means that it represents the owning side of the relation responsible for handling the mapping. However, we still need to have both sides in sync as otherwise, we break the Domain Model relationship consistency, and the entity state transitions are not guaranteed to work unless both sides are properly synchronized.
The answer is yes, you have to manage the java relations yourself so that the clubProducts gets persisted. You are using an instance of the repository class club to persist the data so , you should add a setter method like :
public void addClubProduct(ClubProduct clubProduct) {
if (clubProduct!= null) {
if (clubProduct== null) {
clubProduct= new ArrayList<ClubProduct>();
}
clubProducts.add(clubProduct);
clubProduct.setClubProduct(this);
}
}
also a method to remove it from the list and use these method in your code to set the values to the list properly before initiating save . Read related article

Shared Primary Key between two Entities Not Working

I have created two Entities namely Teacher and Detail, the code snippet is shown below
Teacher.java
#Entity
#Table(name = "teacher")
public class Teacher implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private long id;
#Column(name = "name")
private String name;
#Column(name = "age")
private int age;
#OneToOne(mappedBy = "teacher", cascade = CascadeType.ALL)
private Detail detail;
public Teacher() {
}
public Teacher(String name, int age) {
this.name = name;
this.age = age;
}
//getter and setter
}
Detail.java
#Entity
#Table(name = "detail")
public class Detail implements Serializable {
#Id
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "id")
private Teacher teacher;
#Column(name = "subjects")
private String subjects;
public Detail() {
}
public Detail(String subjects) {
this.subjects = subjects;
}
//getter and setter
}
I am trying to achieve one to one mapping with the shared primary key concept
but when i execute the controller, only Teacher table is updating with the value
try {
Teacher teacher=new Teacher("xyz",23);
Detail detail=new Detail("Java,c,c++");
teacher.setDetail(detail);
session.beginTransaction();
session.save(teacher);
session.getTransaction().commit();
model.addAttribute("added", "data inserted");
session.close();
}
After executing only Teacher table is updated with the specified values.Detail table is still showing empty
It does not work exactly like that. You still need the id field in your Detail, so add:
#Id
private long id;
to your Deatail class.
And - as comment suggests - replace the #Id annotation in field Teacher to #MapsId. This way the id of Teacher is mapped to the id of Detail BUT ONLY if you also set the teacher to the detail - you always need to set both sides of relationship - like:
teacher.setDetail(detail);
detail.setTeacher(teacher);

How Do I Create Many to Many Hibernate Mapping for Additional Property from the Join Table?

I need a many to many hibernate mapping needed 3 joins. I've tried to find out a solution without intermediate entity like LecturerCourse.
I have a many to many relation in my database between my lecturer and course tables. A course can be given by several lecturer while a lecturer can give several courses.
I have courses stored before hand. However, I need to assign courses to lecturer. When I assign courses I also store the capacity of that course.
My database diagram:
I use hibernate and spring. I need a hibernate mapping when a course is assign any lecturer. I need to add values to capacity field.
My lecturer mapping :
#Entity
#Table(name="LECTURER")
public class Lecturer {
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="LECTURER_ID_SEQ")
#SequenceGenerator(name="LECTURER_ID_SEQ", sequenceName="LECTURER_ID_SEQ")
private Long Id;
#Column(name="NAME")
private String name;
#Column(name="SURNAME")
private String surname;
#Column(name="EMAIL")
private String email;
#Column(name="USERNAME")
private String username;
#Column(name="PASSWORD")
private String Password;
#ManyToMany
#JoinTable(
name="LECTURER_COURSE",
joinColumns=#JoinColumn(name="LECTURER_ID"),
inverseJoinColumns=#JoinColumn(name="COURSE_ID")
)
private List<Course> courses;
//getters - setters
}
My course mapping :
#Entity
#Table(name="COURSE")
public class Course {
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="COURSE_ID_SEQ")
#SequenceGenerator(name="COURSE_ID_SEQ", sequenceName="COURSE_ID_SEQ")
private Long id;
#Column(name="NAME")
private String name;
#Column(name="CODE")
private String code;
}
Any idea how to solve my problem ?
You need to use #EmbeddedId and #Embeddable annotations to solve this issue:
Lecturer Class:
#Entity
#Table(name="LECTURER")
public class Lecturer {
#OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.lecturer", cascade=CascadeType.ALL)
Set<LecturerCourse> lecturerCourses == new HashSet<LecturerCourse>();
//all others properties Setters and getters are less relevant.
}
Course class:
#Entity
#Table(name="COURSE")
public class Course {
#OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.course", cascade=CascadeType.ALL)
Set<LecturerCourse> lecturerCourses == new HashSet<LecturerCourse>();
//all others properties Setters and getters are less relevant.
}
LecturerCourse Class:
#Entity
#Table(name = "lecturer_course")
#AssociationOverrides({
#AssociationOverride(name = "pk.lecturer",
joinColumns = #JoinColumn(name = "LECTURER_ID")),
#AssociationOverride(name = "pk.course",
joinColumns = #JoinColumn(name = "COURSE_ID")) })
public class LecturerCourse {
private LecturerCourseID pk = new LecturerCourseID();
#Column(name = "CAPACITY", nullable = false, length = 10)
private String capacity;
#EmbeddedId
public LecturerCourseID getPk() {
return pk;
}
}
Now the Primary Key:
#Embeddable
public class LecturerCourseID implements java.io.Serializable {
private Lecturer lecturer;
private Course course;
#ManyToOne
public Stock getLecturer() {
return lecturer;
}
public void setLecturer(Lecturer lecturer) {
this.lecturer= lecturer;
}
#ManyToOne
public Course getCourse() {
return course;
}
public void setCourse(Course course) {
this.course= course;
}
}
now Your Main should be something like this:
Lecturer lecturer1 = new Lecturer();
Course math = new Course();
LecturerCourse lecturer1math = new LecturerCourse();
lecturer1math.setCapacity("capacity");
lecturer1math.setLecturer(lecturer1);
lecturer1math.setCourse(math);
lecturer1.getLecturerCourses().add(lecturer1math);
//saving object
session.save(lecturer1);
You need to be sure that class marked as #Embeddable should implement Serializable marker interface.
Hope it helps.

Resources