I'm currently working on a Spring Boot project for an online shop. It's my first project with Spring Boot (and my first post here), so my coding is not the best.
Context for the questions:
My shop (for now) has a lists of products and whishlists of different users (shopping lists), which have a bidirectional @ManyToMany relation (i left here the relevant details for my question(s)):
Product.java entity:
@Entity
public class Product extends RepresentationModel\<Product\>{
@Id
@GeneratedValue
@JsonView(ProductView.DescriptionExcluded.class)
private Integer id;
@ManyToMany()
@JoinTable(
name = "Shopping_Product",
joinColumns = { @JoinColumn(name = "id", referencedColumnName = "id") },
inverseJoinColumns = { @JoinColumn(name = "list_id", referencedColumnName = "list_id") })
@JsonIgnore
private Set<ShoppingList> shoppinglists = new HashSet<>();
// Constructor, getters, setters ....
ShoppingList.java entity:
@Entity public class ShoppingList {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@JsonView(ShoppingListView.ProductsExcluded.class)
private Integer list_id;
@JsonView(ShoppingListView.ProductsIncluded.class)
@ManyToMany(mappedBy = "shoppinglists")
private Set<Product> products = new HashSet<>();
// Constructor, getters, setters ...
I chose Product as the owner because i wanted to delete (tho it would be more fit to show something like "offer expired", but I'll stick to delete for now) the product from all existing lists when the admin takes it down from the shop, which works as expected:
ProductResource.java (controller):
@DeleteMapping("/categs/*/sub/*/products/{id}")
public ResponseEntity<String> deleteProduct(@PathVariable int id) {
Optional<Product> optional = productRepository.findById(id);
if(!optional.isPresent()) throw new NotFoundException("Product id - " + id);
Product prod = optional.get();
productRepository.delete(prod);
return ResponseEntity.ok().body("Product deleted");
}
My problems now are related to the ShoppingList entity, which is not the owner. Any call I make to the Product resource (controller) works as expected, but anything from the other side either fails or returns incomplete results, like the following:
1.
I call retrieve all products from a list and it returns only the first object (the list has at least 2): ShoppingListResource.java (controller):
@RestController
public class ShoppingListResource {
@Autowired
private ProductRepository productRepository;
@Autowired
private UserRepository userRepository;
@Autowired
private ShoppingListRepository shoppinglistRepository;
@GetMapping("/user/lists/{id}")
public Set<Product> getShoppinglistProducts(@PathVariable int id) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String currentPrincipalName = authentication.getName();
ShoppingList shoppingList = shoppinglistRepository.findById(id).get();
String name = shoppingList.getUser().getUsername();
if(!Objects.equals(currentPrincipalName, name)) throw new IllegalOperation("You can only check your list(s)!");
// All lists are shown for a product
// Product p = productRepository.findById(10111).get();
// Set<ShoppingList> set = p.getShoppinglists();
// set.stream().forEach(e -> log.info(e.toString()));
// Only first product is shown for a list
return shoppingList.getProducts();
This is what hibernate does on the last row (only returns 1/2 products)
Hibernate: select products0_.list_id as list_id2_3_0_,
products0_.id as id1_3_0_,
product1_.id as id1_1_1_,
product1_.description as descript2_1_1_,
product1_.name as name3_1_1_,
product1_.price as price4_1_1_,
product1_.subcat_id as subcat_i5_1_1_ from shopping_product products0_ inner join product product1_ on products0_.id=product1_.id where products0_.list_id=?
As i said above, I can delete a product and it gets removed automatically from all existing lists, but when i try the same from ShoppingList entity does nothing:
Same controller
@DeleteMapping("/user/lists/{id}")
public ResponseEntity<String> deleteShoppinglist(@PathVariable int id) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String currentPrincipalName = authentication.getName();
ShoppingList shoppingList = shoppinglistRepository.findById(id).get();
String name = shoppingList.getUser().getUsername();
if(!Objects.equals(currentPrincipalName, name)) throw new IllegalOperation("You can only delete your list(s)!");
shoppinglistRepository.delete(shoppingList);
return ResponseEntity.ok().body("Shopping list deleted");
}
Also, when i try to add/delete product from an existing list, does nothing. This is my repo with full code, if you'd like to test directly (dev branch is up to date): https://github.com/dragostreltov/online-store/tree/dev
You can just use admin admin as authentication (on the H2 console too). More details on the readme. All DB data at app start is inserted from a .sql file.
I checked other similar questions and tried different methods on my ShoppingList entity (on the delete issue), like:
@PreRemove
public void removeListsFromProducts() {
for(Product p : products) {
p.getShoppinglists().remove(this);
}
}
Spring/Hibernate: associating from the non-owner side
And still doesn't work.
UPDATE:
I found out what issues I was having, I'll post an answer with the solution.