How can i get all my saved posts in spring boot? - spring

I have to entities like User and Food. User can save food posts. I am trying to getting all saved posts of user but how can i do this? I am writing hibernate query in food repository but i can't access saved food posts.
Here is my code:
#Data
#Entity
public class User extends BaseEntity {
#Column(unique = true, nullable = false)
private String username;
#JsonIgnore
#OneToMany
private List<Food> savedRecipes;
}
Food class:
#Data
#Entity
#Where(clause = "deleted = false")
public class Food extends BaseEntity {
private String foodName;
private String recipe;
#OneToMany
private List<Category> categoryList;
#ManyToOne(fetch = FetchType.EAGER)
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
#JoinColumn(name = "user_id", referencedColumnName = "id")
private User user;
}
Repository Code:
#Repository
public interface FoodRepository extends JpaRepository<Food,Long> {
List<Food> findAllByFoodNameContaining(String searchedValue);
List<Food> findAllByCategoryListInAndDeletedFalse(List<Category> categoryList);
List<Food> findAllByUserId(Long id);
List<Food> findAllByUserSavedRecipes(Long id);
}

Try this way.
List <Food> findAllByUser (User user);

You can easily get All User's saved food by call below methods
1st: List<Food> result = User.getSavedRecipes()
2nd: List<Food> result = FoodRepository.findAllByUserId(Long id)

Related

Confused why getting a User from Repository fixed "failed to lazily initialize a collection of role" compared to using SecurityContextHolder

My goal was to pass a List of Businesses to the model from the controller to display it in a view and I have succeeded, but have a bit of confusion.
When I initially tried using:
public User getCurrentAuthenticatedUser() {
UserDetailsImpl user = (UserDetailsImpl) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
return user.getUser();
}
#GetMapping("")
public String list(Model model) {
model.addAttribute("businesses", userService.getCurrentAuthenticatedUser().getBusinesses());
return "business/list";
}
I got this error: "failed to lazily initialize a collection of role: com.xyz.User.businesses could not initialize proxy - no Session"
Then I tried:
#GetMapping("")
public String list(Model model) {
int userId = userService.getCurrentAuthenticatedUser().getId();
User user = userService.getById(userId); // gets User using Spring Data JPA UserRepository
List<Business> businesses = user.getBusinesses();
model.addAttribute("businesses", businesses);
return "business/list";
}
And this worked perfectly fine.
What was the issue using the first method. It seemed more simple rather than calling a User from the UserRepository. I've seen some posts that say you should use EAGER fetching, but that's just seems like a bandaid solution.
From the beginner's understanding: Since fetch type is LAZY the businesses don't exist yet in the User but are fetched on demand later on so there shouldn't be an issue.
Edit: After more thought I remembered that with basic Hibernate you would have to create Transactions and commit transactions. I'm assuming that User is not within a Transaction that's why I can't get businesses using the 1st method.
What would be a better solution to fetch the current Authenticated user? And that user's attributes such as a list of businesses.
Model Classes:
Business:
#Entity
#Table(name = "businesses")
public class Business {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String description;
private LocalDate date;
#ManyToOne(cascade={CascadeType.MERGE})
#JoinColumn(name="user_id")
private User user;
public Business() {
}
public Business(String name, String description, LocalDate date, User user) {
...
}
public Business(Long id, String name, String description, LocalDate date, User user) {
...
}
... getters/setters
}
USER:
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String username;
private String password;
private boolean enabled;
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable( name = "users_roles",
joinColumns = #JoinColumn(name = "user_id"),
inverseJoinColumns = #JoinColumn(name = "role_id"))
private Set<Role> roles = new HashSet<>();
#OneToMany(fetch = FetchType.LAZY, mappedBy="user", cascade={CascadeType.MERGE})
private List<Business> businesses;
... getters/setters
}

ManyToOne mapping and findingBySchoolId

I am trying to create teacher and school objects where 1 school can have multiple teachers, but each teacher works at one school. I am tying to make a query where I get only teachers that work at certain school by getting its id as a parameter.
School object
#Getter
#Setter
#Entity
#Table(name="school")
#NoArgsConstructor
#AllArgsConstructor
#ToString
public class School {
#Id
#SequenceGenerator(
name = "school_sequence",
sequenceName = "school_sequence",
allocationSize = 1
)
#GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "school_sequence"
)
#Column(name="id")
private Long id;
private String name;
#JsonIgnore
#OneToMany//(mappedBy = "school")
private List<Teacher> teacher;
}
Teacher object
#Getter
#Setter
#Entity
#Table(name="teacher")
#NoArgsConstructor
#AllArgsConstructor
#ToString
public class Teacher {
#Id
#SequenceGenerator(
name = "teacher_sequence",
sequenceName = "teacher_sequence",
allocationSize = 1
)
#GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "teacher_sequence"
)
#Column(name="id")
private Long id;
private String name;
private String email;
/* #Column(name="schoolId")
private Long schoolId;*/
//(cascade = CascadeType.ALL) Don't use this! it will prevent you to have different teacher queries with
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "schoolId", referencedColumnName = "id") //the same school ids.
private School school;
}
Teacher controller
#RestController
#RequestMapping(path = "api/v1/teacher")
public class TeacherController {
private final TeacherService teacherService;
#Autowired
public TeacherController(TeacherService teacherService) {
this.teacherService = teacherService;
}
#GetMapping
public List<Teacher> getTeacher(){
return teacherService.getTeacher();
}
#GetMapping(path = "schoolID")
public List<Teacher> getTeacherBySchool(#PathVariable("schoolId") Long schoolId, School school){
return teacherService.getTeacherBySchool(schoolId, school);
}
Teacher Service
#Service
public class TeacherService {
private final TeacherRepository teacherRepository;
#Autowired
public TeacherService(TeacherRepository teacherRepository) {
this.teacherRepository = teacherRepository;
}
public List<Teacher> getTeacher(){
return teacherRepository.findAll();
}
//public List<Teacher> getTeacherBySchool
How would I implement this on teacher controller and teacher service?
Update on my progress!!!
Teacher Controller
#GetMapping(path = "/teachers/{schoolID}")
public List<Teacher> getTeacherBySchool(#PathVariable("schoolID") Long schoolId){
School school = new School();
school.setId(schoolId);
return teacherService.getTeacherBySchool(school);
}
Teacher Service
public List<Teacher> getTeacherBySchool(Long schoolId){
return teacherRepository.findBySchool(school);
}
Teacher Repository
#Repository
public interface TeacherRepository extends JpaRepository<Teacher, Long> {
#Query
List<Teacher> findBySchool(List<School> school);
These changes do what I want them to do, but as I said. It is not a good coding practice. What I want is that less code on TeacherController, and make my TeacherService communicate with SchoolService to get the right schools.
I think you can just add the following to your repository:
public List<Teacher> findBySchoolId(Long schoolId);
Or just write the query yourself:
entityManager.createQuery("FROM Teacher t WHERE t.school.id = :schoolId")
.setParameter("schoolId", schoolId)
.getResultList();

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

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

How to create JPA Specification for multiple tables?

I had entities User, BlockUnit, Block and Unit as Follows.
User has ManyToMany relation with blockunit.
#Entity
public class User {
#ManyToMany
private Set<BlockUnit> blockUnits;
}
#Entity
public class BlockUnit {
#Id
private Long id;
#OneToOne(targetEntity = Block.class)
#JoinColumn(name = "block_id")
private Block block;
#OneToOne(targetEntity = Unit.class)
#JoinColumn(name = "unit_id")
private Unit unit;
#ManyToMany(fetch = FetchType.LAZY)
private Set<User> users = new HashSet<>();
}
BlockUnit has OneToOne relation with Block and Unit.
#Entity
public class Block {
#Id
private Long id;
}
#Entity
public class Unit {
#Id
private Long id;
}
How should I create the JPA criteria specification for above in order to select user's having Block ID and Unit ID?
Thank you very much in advance!

Resources