I have OneToMany bidirectional mapping for two entities Cart and CartProduct. Whenever we insert a Cart object with cart products, CartProduct table should fill with cart_id. Here is the problem, when I insert cart object, everything seems to be fine except, JoinColumn(card_id) which results in a null value in CartProduct table. Am I doing this right?
Cart.Java
package com.springtesting.model.cart;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.springtesting.model.AbstractAuditingEntity;
import com.springtesting.model.user.UserProfile;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
#EqualsAndHashCode(callSuper = true)
#Entity
#Data
#Table(name = "cart")
public class Cart extends AbstractAuditingEntity
{
private static final long serialVersionUID = 6294902210705780249L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#OneToOne
#JoinColumn(name = "user_profile_id")
#JsonIgnoreProperties(value = {"addresses"})
private UserProfile userProfile;
#ManyToOne
#JoinColumn(name = "cart_status")
private CartStatus cartStatus;
#OneToMany(mappedBy = "cart", cascade = CascadeType.ALL,fetch = FetchType.EAGER)
//#ElementCollection(targetClass = CartProduct.class)
private List<CartProduct> cartProducts=new ArrayList<>();
}
CartProduct.Java
package com.springtesting.model.cart;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.springtesting.model.AbstractAuditingEntity;
import com.springtesting.model.product.Product;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.persistence.*;
#EqualsAndHashCode(callSuper = true)
#Entity
#Data
#Table(name = "cart_product")
public class CartProduct extends AbstractAuditingEntity
{
private static final long serialVersionUID = 6498067041321289048L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#OneToOne
#JoinColumn(name = "product_id")
private Product product;
#Column(name = "quantity")
private Integer quantity;
#ManyToOne
#JoinColumn(name = "cart_id",referencedColumnName = "id")
#JsonIgnoreProperties(value = {"userProfile","cartStatus","cartProducts"})
private Cart cart;
}
TestCase.java
#Test
public void insertCart()
{
Cart cart=new Cart();
cart.setUserProfile(userProfileRepository.findAllByUserId(1L).get());
cart.setCartStatus(cartStatusRepository.findById(1L).get());
List<CartProduct> cartProducts=new ArrayList<>();
CartProduct cartProduct=new CartProduct();
cartProduct.setProduct(productRepository.findById(1L).get());
cartProduct.setQuantity(2);
cartProducts.add(cartProduct);
cartProduct=new CartProduct();
cartProduct.setProduct(productRepository.findById(2L).get());
cartProduct.setQuantity(1);
cartProducts.add(cartProduct);
cart.setCartProducts(cartProducts);
cartRepository.saveAndFlush(cart);
}
Yes, your fix is the addition of cartProduct.setCart(cart); This is because the CartProduct is the owning entity and is the keeper of the foreignKey. The above statement sets the FK.
The way to think about this is the concept of owning entity. When you have mappedBy="cart" you are saying that the CartProduct class owns the relationship. This means that only the CartProduct class is doing the persisting. This tells JPA to create a FK in the CartProduct table. However, we notice that save is not being called on CartProduct but rather on Cart and yet cartProducts is still being saved. This is because you have the cascade = CascadeType.ALL annotation. This tells JPA to cascade certain operations when they are done to Cart, in this case the save operation.
You should have SQL statements printed and examine the differences with different configurations and test cases. This will help you understand better.
You also have FetchType.EAGER. This is generally a bad habit and usually leads to endless problems.
A good way to think about a bidirectional mapping is that the List<CartProducts> cartProducts is a query only field. In order to save a CartProduct you would call save on the cartProductRepository directly. E.g.
CartProduct cartProduct=new CartProduct();
cartProduct.setProduct(productRepository.findById(1L).get());
cartProduct.setQuantity(2);
cartProduct.setCart(cart);
cartProductRepository.save(cartProduct);
and then
cart.getCartProducts().add(cartProduct);
and remove all the cascade and eager fetch annotations. When hibernate says that you must management both sides of the relationship this is what is meant.
Doing it this way will result in one query for the save. By using a cascade annotation you will find that as you add items to the cart and call save on it the sql generated will first delete all the existing cartProducts items from the database and re-add them along with the new one every time you call save. For a cart with 10 items instead of a single save you will have a delete and 10 new saves. Definitely less desirable. If you have to reload the cart from scratch the most efficient method is to get the cart and then cart.setCartProducts(cartProductRepository.findAllByCart(cart)); which is what FetchType.EAGER is doing anyway. When you understand all this then you realize that you don't need a = new ArrayList<>(); for your cartProducts.
I think I figured out the solution. Based Hibernate docs
Whenever a bidirectional association is formed, the application
developer must make sure both sides are in-sync at all times.
So I manually added the cart object to cartProduct object, which saves cart_id in CartProduct table
CartController.java
import com.pj.springsecurity.model.cart.Cart;
import com.pj.springsecurity.model.cart.CartProduct;
import com.pj.springsecurity.repo.CartRepository;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Optional;
#RestController
#RequestMapping("/api/v1/cart")
public class CartController
{
private final CartRepository cartRepository;
public CartController(CartRepository cartRepository)
{
this.cartRepository = cartRepository;
}
#GetMapping(path = "/list")
public List<Cart> getAllCarts()
{
return cartRepository.findAll();
}
#GetMapping(path = "/find/user/{id}")
public Optional<Cart> getCartBasedOnUserId(#PathVariable Long id)
{
return cartRepository.findAllByUserProfileUserId(id);
}
#PostMapping(path = "/product/add")
public Cart addProductToCart(#RequestBody Cart cart)
{
List<CartProduct> cartProducts=cart.getCartProducts();
for(CartProduct cartProduct: cartProducts)
{
cartProduct.setCart(cart);
}
return cartRepository.saveAndFlush(cart);
}
#PutMapping(path = "/update")
public Cart updateCart(#RequestBody Cart cart)
{
return cartRepository.saveAndFlush(cart);
}
#DeleteMapping(path = "/delete")
public Cart createEmptyCart(#RequestBody Cart cart)
{
return cartRepository.saveAndFlush(cart);
}
#DeleteMapping(path = "/product/delete")
public void deleteProductFromCart(#RequestBody Cart cart)
{
List<CartProduct> cartProducts=cart.getCartProducts();
for(CartProduct cartProduct: cartProducts)
{
cartProduct.setCart(null);
}
cartRepository.delete(cart);
}
}
and Test case updated with the same
#Test
public void insertCart()
{
Cart cart=new Cart();
cart.setUserProfile(userProfileRepository.findAllByUserId(1L).get());
cart.setCartStatus(cartStatusRepository.findById(1L).get());
List<CartProduct> cartProducts=new ArrayList<>();
CartProduct cartProduct=new CartProduct();
cartProduct.setProduct(productRepository.findById(1L).get());
cartProduct.setQuantity(2);
cartProduct.setCart(cart);
cartProducts.add(cartProduct);
cartProduct=new CartProduct();
cartProduct.setProduct(productRepository.findById(2L).get());
cartProduct.setQuantity(1);
cartProduct.setCart(cart);
cartProducts.add(cartProduct);
cart.setCartProducts(cartProducts);
cartRepository.saveAndFlush(cart);
}
Related
My classes are like..
Employee Entity:
import javax.persistence.Entity;
import lombok.Data;
import org.hibernate.annotations.DynamicUpdate;
import javax.persistence.PrePersist;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
#Entity
#Data
#DynamicUpdate
#EntityListeners(AuditingEntityListener.class)
#Table(name = "tbl_employee", indexes = {
#Index(name = "idx_employee_status", columnList = "status"),
#Index(name = "idx_employee_createdAt", columnList = "createdAt") })
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
protected Long id;
private String name;
private String remarks;
private String status;
#PrePersist
public void setCreatedAt() {
this.createdAt = OffsetDateTime.now();
}
}
Repository:
public interface EmployeeRepository extends CrudRepository<Employee, Long>, PagingAndSortingRepository<Employee, Long>{
}
Service methods:
createEmployee(){
Employee employee = new Employee();
employee.setName("John");
employee.setRemarks("Very Good Performance");
employeeRepository.save(employee);
}
updateEmployee(){
Employee employee = employeeRepository.findById(1L); // Id is long
employee.setName("Thomas");
employee.setRemarks("Above average Performance");
employeeRepository.save(employee);
}
createEmployee() is working and data is getting saved in DB (MySQL), but updateEmployee() is not. No sql query is generated in the eclipse console.
AM I missing something in configuration?
Make sure that the service methods are annotated with #Transactional.
Otherwise the hibernate cannot track changes which are needed for #DynamicUpdate.
Alternatively you can also try to remove the annotation.
Hibernate/JPA only access the database when it has to.
In the create variant it needs to return an instance with a updated ID value. The id value gets generated by the database when the insert is performed.
Therefore the insert is performed immediately.
In the other case no information from the database is required.
Therefore Hibernate/JPA only marks the entity as dirty and moves on.
Only when the transaction ends the update is actually performed.
I have 3 entities -
Course
Module
Timeline
Course is an independent entity with following attributes:
Course - (id Integer Primary Key, course_name)
#Id
#Column(name = "id")
Integer courseId;
#Column(name = "course_name")
String course_name;
Next up is another entity Module,
Every row in module is related to one course, and hence there is a one to one relationship between Module and Course.
Module - (module_id, module_name, module_type, duration)
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "module_id")
Integer module_id;
#Column(name = "module_name")
String module_name;
#Column(name = "duration")
Integer duration;
#ManyToOne
#JoinColumn(name="timeline_id", nullable=false)
private Timeline timeline;
Now, next is a timeline entity, which is also related to course i.e every timeline id belongs to one course id, but one timeline id can belong to multiple module_ids, and hence below code:
#Id
#Column(name = "timeline_id")
Integer timelineId;
#OneToMany( mappedBy = "timeline" )
private List<Module> module;
#OneToOne( cascade = CascadeType.ALL)
private Course course;
Can you please tell me what is the error over here.
ModuleRepository:
#Repository
public interface ModuleRepository extends JpaRepository<Module, Integer>{
public List<Module> findAllByTimelineTimelineId(Integer timelineId);
}
IModuleService
public interface IModuleService {
public List<Module> findByTimelineId(Integer timelineId);
}
ModuleServiceImpl
public List<Module> findByTimelineId(Integer timelineId) {
// TODO Auto-generated method stub
return moduleRepo.findAllByTimelineTimelineId(timelineId);
}
Controller
#RequestMapping("/gettimeline/{timeline_id}")
public List<Module> findByTimelineId(#PathVariable Integer timeline_id){
return moduleService.findByTimelineId(timeline_id);
}
Now when I run this url in Postman: http://localhost:8083/gettimeline/1
I get an infinite loop, I am unable to decode the error, also is there any problem with OneToMany mapping, I am new to JPA:
[{"module_id":1,"module_name":"Sleep","duration":10,"timeline":{"timelineId":1,"module":[{"module_id":1,"module_name":"Sleep","duration":10,"timeline":{"timelineId":1,"module":[{"module_id":1,"module_name":"Sleep","duration":10,"timeline":{"timelineId":1,"module":[{"module_id":1,"module_name":"Sleep","duration":10,"timeline":{"timelineId":1,"module":[{"module_id":1,"module_name":"Sleep","duration":10,"timeline":{"timelineId":1,"module":[
Please help, thank you in advance :)
The infinite loop issue is caused by the one-to-many relation. There are several ways of fixing this, but I find view model classes like shown below as the cleanest approach.
Please note that the owning side of the one-to-many relation is not included in the code below, only the many-to-one. This can be done the other way around, but from your code, I guess this is what you want.
TimelineVM class
package no.mycompany.myapp.misc;
import lombok.Data;
import lombok.NoArgsConstructor;
#Data
#NoArgsConstructor
public class TimelineVM {
private Integer timelineId;
public TimelineVM(Timeline timeline) {
this.timelineId = timeline.getTimelineId();
}
}
ModuleVM class
package no.mycompany.myapp.misc;
import lombok.Data;
import lombok.NoArgsConstructor;
#Data
#NoArgsConstructor
public class ModuleVM {
private Integer module_id;
private String module_name;
private Integer duration;
private TimelineVM timeline;
public ModuleVM(Module module) {
this.module_id = module.getModule_id();
this.module_name = module.getModule_name();
this.duration = module.getDuration();
this.timeline = new TimelineVM(module.getTimeline());
}
}
Controller method
#RequestMapping("/gettimeline/{timeline_id}")
public List<ModuleVM> findByTimelineId(#PathVariable Integer timeline_id){
return moduleService.findByTimelineId(timeline_id).stream().map(ModuleVM::new).collect(Collectors.toList());
}
Note: I have Used Spring Data Jpa for persistence.
Problem:
I have two Models: User and Badge
I have a List of Badges owned By a User as data member in User class.
I also have User as data member in Badge class (i.e. The creator of the badge)
I want to make relationship between user and List of badges data member.
relationship is of type OneToMany (i.e. One User will going to have Many Badges) and vice versa also.
I want it to work in this way,
in code ,
When I save badge object with issuer (aka user) set to a particular user object , then need not to add it (the badge) into user's List of badges Owned by it.
I have tried to create the relationship but it returns an empty list of User Owned Badge in REST API response.
Badge Model
import javax.persistence.*;
#Entity
#Table(name = "badges")
public class Badge {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "badge_id")
private int mId;
#Column(name = "badge_name" , nullable = false , unique = true)
private String mName;
#Column(name = "badge_description")
private String mDescription;
#Lob
#Column(name = "badge_logo" , nullable = false)
private String mLogo;
#ManyToOne
#JoinColumn(name = "issuer_id")
private User mIssuer;
}
User Model
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
#Entity
#Table(name = "users")
public class User {
#Id#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "user_id")
private long mId;
#Column(name = "username" , nullable = false , unique = true)
private String mUserName;
#Column(name = "fullname",nullable = false)
private String mFullName;
#Column(name = "salt")
private String mSalt;
#OneToMany(mappedBy = "mIssuer",cascade = CascadeType.ALL)
private List<Badge> mOwnedBadges;
#OneToMany
#JoinColumn(name = "received_badges_id")
private List<Badge> mReceivedBadges;
}
CommandLineRunner
import com.badging.spinnerbadger.SpinnerBadger.Models.Badge;
import com.badging.spinnerbadger.SpinnerBadger.Models.User;
import com.badging.spinnerbadger.SpinnerBadger.Services.Intefaces.BadgeSerivce;
import com.badging.spinnerbadger.SpinnerBadger.Services.Intefaces.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import java.util.Arrays;
#Component
public class StartupExecutor implements CommandLineRunner {
#Autowired
private BadgeSerivce mBadgeSerivce;
#Autowired
private UserService mUserService;
#Override
public void run(String... args) throws Exception {
//TODO:: issuer cannot issue badge to itself
final User user1 = new User();
user1.setFullName("User1 FullName");
user1.setSalt("salt1");
user1.setUserName("User1 UserName");
mUserService.save(user1);
final User user2 = new User();
user2.setFullName("User2 FullName");
user2.setSalt("salt2");
user2.setUserName("User2 UserName");
mUserService.save(user2);
Badge badge1 = new Badge();
badge1.setDescription("Desc1");
badge1.setLogo("Logo1");
badge1.setName("Badge1");
badge1.setIssuer(user1);
mBadgeSerivce.save(badge1);
Badge badge2 = new Badge();
badge2.setDescription("Desc2");
badge2.setLogo("Logo2");
badge2.setName("Badge2");
badge2.setIssuer(user2);
mBadgeSerivce.save(badge2);
Badge badge3 = new Badge();
badge3.setDescription("Desc3");
badge3.setLogo("Logo3");
badge3.setName("Badge3");
badge3.setIssuer(user1);
mBadgeSerivce.save(badge3);
user1.setReceivedBadges(Arrays.asList(badge2));
user2.setReceivedBadges(Arrays.asList(badge1,badge3));
}
}
Note: It doesn't save user Received Badges list also , if you can figure that out too , then I will really be thankful to you.
BadgeRepo
import com.badging.spinnerbadger.SpinnerBadger.Models.Badge;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface BadgeRepo extends PagingAndSortingRepository<Badge,Long> {
}
UserRepo
import com.badging.spinnerbadger.SpinnerBadger.Models.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface UserRepo extends JpaRepository<User,Long> {
}
BadgeServiceImpl
package com.badging.spinnerbadger.SpinnerBadger.Services.Implentations;
import com.badging.spinnerbadger.SpinnerBadger.Repository.BadgeRepo;
import com.badging.spinnerbadger.SpinnerBadger.Models.Badge;
import com.badging.spinnerbadger.SpinnerBadger.Services.Intefaces.BadgeSerivce;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
#Service
public class BadgeServiceImpl implements BadgeSerivce {
#Autowired
private BadgeRepo mBadgeRepo;
#Override
public List<Badge> getAllBadges(int pageNumber , int sizeOfPage) {
if (sizeOfPage > 20) {
sizeOfPage = 20;
}
final Page<Badge> allPages = mBadgeRepo.findAll(PageRequest.of(pageNumber,
sizeOfPage));
if (allPages.getTotalElements() > 0) {
return allPages.toList();
} else{
return new ArrayList<Badge>();
}
}
#Override
public void save(Badge badge) {
mBadgeRepo.save(badge);
}
}
UserServiceImpl
import com.badging.spinnerbadger.SpinnerBadger.Models.Badge;
import com.badging.spinnerbadger.SpinnerBadger.Models.User;
import com.badging.spinnerbadger.SpinnerBadger.Repository.UserRepo;
import com.badging.spinnerbadger.SpinnerBadger.Services.Intefaces.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
#Service
public class UserServiceImpl implements UserService {
#Autowired
private UserRepo mUserRepo;
#Override
public void save(User user) {
mUserRepo.save(user);
}
#Override
public List<Badge> getUsersReceivedBadgeList(long userId) {
final Optional<User> byId = mUserRepo.findById(userId);
return byId.orElse(new User()).getReceivedBadges();
}
#Override
public List<Badge> getUserOwnedBadgeList(long userId) {
final Optional<User> byId = mUserRepo.findById(userId);
return byId.orElse(new User()).getReceivedBadges();
}
}
Generated SQL by Hibernate -> 1st for User model and 2nd for Badge Model
Hibernate: insert into users (fullname, salt, username, user_id) values (?, ?, ?, ?)
Hibernate: insert into badges (badge_description, issuer_id, badge_logo, badge_name, badge_id) values (?, ?, ?, ?, ?)
I see a couple of things that might go wrong here.
You don't seem to have transactions specified. Add #Transactional to those beans or methods that should participate in a transaction. At the very least that should include everything that modifies (eventually) the database, i.e. any statement modifying a managed entity, including the one that loads it from the database and save statements. I'm expecting this to be the actual cause of the problem you are seeing.
You don't seem to have code in place that synchronises the both sides of a bidirectional relationship. So, when you call
badge1.setIssuer(user1),
user1 does not get updated, so if you call user1.getOwnedBadges()
it will still return the unchanged (empty) value.
I doubt it is a problem in this case, but it will result in the
relationship looking different within a single transaction,
depending from which side you are looking at it. And changes to the
non-owning side (User in your case) will not get persisted. So this should be fixed
anyways. See also https://vladmihalcea.com/jpa-hibernate-synchronize-bidirectional-entity-associations/
When saving an entity, you should use the instance returned by the save method, not the one passed to save as an argument. Often they are the same but when they aren't modifying the one passed to save might not result in the state persisted to the database.
If these things are fixed and problems persist I recommend the following to gather more information about what is going on:
Activate logging of SQL statements including parameters in order to see what is actually persisted (and when).
Create a JUnit test testing your services. This makes it much clearer what is actually executed and allows to create variants to compare.
Product and ProductTag form a one-to-many relationship, as shown below.
#Entity
public class Product {
#Id
Long id;
#OneToMan(mappedBy = "product")
List<ProductTag> productTags;
}
#Entity
public class ProductTag {
#Id
Long id;
String content;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "product_id")
Product product;
}
Now I have an API that searches products, then returns them with their tags. Every time I call product.getProductTags(), Hibernate will fire an SQL query. Since the MySQL server is far away from the app server, I would like to cache product.getProductTags() call. How do I achieve that?
Use a specific query to fetch the tags and store them in a cache:
public TagRepository extends JpaRepository<Tag, Long> {
#Cacheable("tagsByProducts")
#Query("Select t from ProductTag t where t.product = ?product")
List<Tag> findByProduct(Product product);
}
somewhere you need some method to evict the cache: annotated by#CacheEvict("tagsByProducts")
But to be honest: I doubt that is a good idea to store JPA Entities in a cache! I think this would lead to many many strange problems. So better query just for the tag names (or content) instead of the tag-entities.
public TagRepository extends JpaRepository<Tag, Long> {
#Cacheable("tagsByProducts")
#Query("Select t.content from ProductTag t where t.product = ?product")
List<String> findTagContentByProduct(Product product);
}
#Entity
public class Product {
#Id
Long product_id;
#OneToMany(casacade=CascadeType.ALL,
fetch=FetchType.Eager,mappedBy = "product")
#JsonManagedReference(value="product-tag")
List<ProductTag> productTags;
}
#Entity
public class ProductTag {
#Id
Long id;
String content;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "product_id")
#JsonBackReference(value="product-tag")
Product product;
}
I have an Order Entity that has many Review Entities. Order has many OrderItem entities that's also has many Review Entities. So User can enter reviews for specific Order as common review or specific Order Item as item review.
Now I want to List the Orders with reviews available and Order with No reviews. For this I have to check Order || OrderItem has reviews in Reviews Table simultaneously. I think count query with Join clause to be performed. Please solve this problem.
OrderDO.java
#Table(name = "ORDER")
public class OrderDO implements Serializable{
#Column(name = "ORDER_NUMBER", nullable = false)
private String orderNumber;
#OneToMany(cascade = CascadeType.ALL, mappedBy="order")
private Set<ReviewDO> reviews = new HashSet<>();
}
OrderItemDO.java
#Table(name = "ORDER_ITEM")
public class OrderItemDO implements Serializable{
#Column(name = "PRODUCT_NAME",nullable = false)
private String name;
#OneToMany(cascade = CascadeType.ALL, mappedBy="item")
private Set<ReviewDO> reviews = new HashSet<>();
}
ReviewDO.java
#Table(name = "REVIEW")
public class ReviewDO implements Serializable {
#Column(name = "RATING")
private double rating;
#Column(name = "REVIEW_TEXT")
private String reviewText;
#ManyToOne()
#JoinColumn(name = "ITEM_ID", referencedColumnName="ID")
private OrderItemDO item;
#ManyToOne()
#JoinColumn(name = "ORDER_ID", referencedColumnName="ID")
private OrderDO order;
}
OrderRepository.java
#Repository
public interface OrderRepository extends CrudRepository<OrderDO, Long>{
#Query("select order from OrderDO order where order.orderNumber = ?1")
public Optional<OrderDO> findByOrderNumber(String orderNumber);
}
To find those reviews that are associated with orders or order-items, you can query on ReviewDO object by placing a ReviewRepository as is shown below:-
#Repository
public interface ReviewRepository extends CrudRepository<ReviewDO, Long>{
#Query(value = "SELECT r FROM ReviewDO r where r.item is not null or r.order is not null")
List<ReviewDO> findAllByOrdersOrOrderItems();
}
Please check the test case given below matching to your requirements or not
import static org.junit.Assert.assertEquals;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import com.raj.so.artefacts.OrderDO;
import com.raj.so.artefacts.OrderItemDO;
import com.raj.so.artefacts.OrderItemRepository;
import com.raj.so.artefacts.OrderRepository;
import com.raj.so.artefacts.ReviewDO;
import com.raj.so.artefacts.ReviewRepository;
#DataJpaTest
#RunWith(SpringRunner.class)
public class ReviewRepositoryTest {
#Autowired
private OrderRepository orderRepository;
#Autowired
private OrderItemRepository orderItemRepository;
#Autowired
private ReviewRepository reviewRepository;
#Test
public void testRepositoryFetch() {
// creating 2 orders
OrderDO orderDO1 = new OrderDO();
orderDO1.setOrderNumber("1");
orderRepository.save(orderDO1);
OrderDO orderDO2 = new OrderDO();
orderDO2.setOrderNumber("2");
orderRepository.save(orderDO2);
// creating an order item
OrderItemDO orderItemDO1 = new OrderItemDO();
orderItemDO1.setName("order item 1");
orderItemRepository.save(orderItemDO1);
// creating 2 reviews - one for order; other for item
ReviewDO reviewDO1 = new ReviewDO();
reviewDO1.setRating(5.0);
reviewDO1.setReviewText("rate five");
reviewDO1.setOrder(orderDO1);
reviewRepository.save(reviewDO1);
ReviewDO reviewDO2 = new ReviewDO();
reviewDO2.setRating(4.0);
reviewDO2.setReviewText("rate four");
reviewDO2.setItem(orderItemDO1);
reviewRepository.save(reviewDO2);
// query all reviews associated to orders or items
// returned review objects will have details about orders and items
List<ReviewDO> reviews = reviewRepository.findAllByOrdersOrOrderItems();
assertEquals(2, reviews.size());
List<String> orderNumbers = reviews.stream().map(item -> {
if (item.getOrder() != null) {
return item.getOrder().getOrderNumber();
} else {
return null;
}
})
.filter(item -> item != null)
.collect(Collectors.toList());
assertEquals("1", orderNumbers.get(0));
List<String> orderItemNames = reviews.stream().map(item -> {
if (item.getItem() != null) {
return item.getItem().getName();
} else {
return null;
}
})
.filter(item -> item != null)
.collect(Collectors.toList());
assertEquals("order item 1", orderItemNames.get(0));
}
}