Как работать со связанными таблицами в Spring Data JPA
Я готовлю не большой проект в виде панели администратора для небольшого магазина. Есть модели:
- Product
- Category
Использую шаблонизатор thymeleaf
Я хочу чтобы в колонке каждого отдельного продукта отображалась его категория.
Я сделал связь продуктов и категорий как многие ко многим. Так как один продукт может иметь несколько категорий, так и в категории может находится множество продуктов.
Я заметил, что при создание нового продукта (эта функция уже реализована), создается связывающая таблица, в которой есть колонки с id товара и id категории которую я выбрал при создании товара.
Как сделать так чтобы на основе этой таблицы я мог в колонке с товарами (страница сайта) показывать категорию каждого товара.
Возможно есть какой-то другой путь осуществления такого функционала?

Код Product:
package com.example.cmspanel.models;
import lombok.Data;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.ArrayList;
import java.util.List;
@Data
@Entity
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@NotNull
@Size(min = 5, message = ("Имя должно быть больше 5 символов."))
private String name;
private String description;
private double price;
private double productWeight;
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "prod_cats",
joinColumns = {@JoinColumn(name = "product_id")},
inverseJoinColumns = {@JoinColumn(name = "category_products_id")})
private List<CategoryProduct> categoryProducts = new ArrayList<>();
}
Код Category:
package com.example.cmspanel.models;
import lombok.Data;
import javax.persistence.*;
import javax.validation.constraints.NotBlank;
import java.util.ArrayList;
import java.util.List;
@Data
@Entity
public class CategoryProduct {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@NotBlank(message = "Необходимо ввести название категории.")
private String name;
@ManyToMany(fetch = FetchType.EAGER, mappedBy = "categoryProducts")
private List<Product> products = new ArrayList<>();
}
Репозиторий Product у Category выглядит аналогично:
package com.example.cmspanel.repositories;
import com.example.cmspanel.models.Product;
import org.springframework.data.repository.CrudRepository;
import java.util.List;
public interface ProductRepositories extends CrudRepository<Product, Long> {
List<Product> findAll();
}
ProductController:
package com.example.cmspanel.controllers;
import com.example.cmspanel.models.CategoryProduct;
import com.example.cmspanel.models.Product;
import com.example.cmspanel.repositories.CategoryRepositories;
import com.example.cmspanel.repositories.ProductRepositories;
import com.example.cmspanel.service.CategoryService;
import com.example.cmspanel.service.ProductService;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.support.SessionStatus;
@Data
@Controller
@RequestMapping("/admin-panel")
public class ProductController {
private final ProductService productService;
private final CategoryService categoryService;
@ModelAttribute
public void addProductToModel(Model model) {
model.addAttribute("prodlist", productService.listProduct());
}
// Указываем представление
@GetMapping("/product-list")
public String showProductPages() {
return "product-list";
}
// Удаляем товар
@PostMapping("/product-list/delete/{id}")
public String deleteProduct(@PathVariable Long id) {
productService.deleteProduct(id);
return "redirect:/admin-panel/product-list";
}
// Создание товара
@GetMapping("/product-list/product-create")
public String showProdCreatePage() {
return "product-create";
}
@ModelAttribute(name = "newProd")
public Product getNewProd() {
return new Product();
}
@ModelAttribute(name = "categories")
public void addCatToModel(Model model) {
model.addAttribute("categories", categoryService.listCategory());
}
// @GetMapping("/product-list/product-create")
// public void addCatToModel(Model model) {
// model.addAttribute("categories", categoryService.listCategory());
// }
// Сохранение товара
@PostMapping("/product-list/product-create")
public String saveNewProd(Product product, SessionStatus sessionStatus) {
productService.saveProduct(product);
sessionStatus.setComplete();
return "redirect:/admin-panel/product-list";
}
}
CategoryController:
package com.example.cmspanel.controllers;
import com.example.cmspanel.models.CategoryProduct;
import com.example.cmspanel.service.CategoryService;
import lombok.Data;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
@Data
@Controller
@RequestMapping("/admin-panel")
public class CategoryController {
private final CategoryService categoryService;
@ModelAttribute
public void addCategoryToModel(Model model) {
model.addAttribute("category", categoryService.listCategory());
}
// Отображение/удаление категорий
@GetMapping("/categories")
public String showHomePage() {
return "categories";
}
@PostMapping("/categories/delete/{id}")
public String deleteCategories(@PathVariable Long id) {
categoryService.deleteCategory(id);
return "redirect:/admin-panel/categories";
}
// Создание категории
@GetMapping("/categories/create")
public String showCreatePage() {
return "create-category";
}
@ModelAttribute(name = "categoryProductName")
public CategoryProduct addCategoryProduct() {
return new CategoryProduct();
}
@PostMapping("/categories/create")
public String createCategory(CategoryProduct categoryProduct) {
categoryService.saveCategory(categoryProduct);
return ("redirect:/admin-panel/categories");
}
}
Представление(Thymeleaf) страницы с отображением всех товаров:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Панель управления</title>
<link th:href="@{/css/style.css}" rel="stylesheet"/>
<!-- <link rel="stylesheet" href="../css/style.css">-->
<!-- <link rel="stylesheet" href="/css/normalize.css">-->
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-eOJMYsd53ii+scO/bJGFsiCZc+5NDVN2yr8+0RDqr0Ql0h+rP48ckxlpbzKgwra6" crossorigin="anonymous">
</head>
<body>
<header class="header header-pad" th:insert="fragments/header :: header">
</header>
<main>
<div class="wraper flex-1-0-auto">
<div class="container-fluid flex-1-0-auto">
<div class="row flex-1-0-auto">
<div class="col-xxl-2 no-padding-no-margin" th:insert="fragments/sidebar :: sidebar">
</div>
<!-- Окно -->
<div class="col-xxl-8">
<div class="row">
<div class="col-xxl-12">
<div class="title-window-wraper">
<p class="title-window">Все продукты</p>
</div>
<div class="col-xxl-12">
<div class="but-window-wraper">
<a class="a-but-win" th:href="@{/admin-panel/product-list/product-create}">Создать продукт</a>
</div>
</div>
<tr th:each="prod : ${prodlist}">
<form method="POST">
<!-- Колонка товара -->
<div class="col-xxl-12">
<div class="product-column-wraper">
<div class="col-xxl-2">
<div class="product-img-wraper">
<img class="product-img" th:src="@{/img/product/cola.png}" alt="">
</div>
</div>
<div class="col-xxl-6">
<div class="description-product-wraper">
<p class="title-product" th:text="${prod.name}">Название
продукта</p>
<p class="description-product" th:text="${prod.description}">
Описание продукта, текст и все что с ним связано</p>
<div class="col-xxl-12">
<div class="cat-and-price-product-wraper">
<p class="cat-text-1">Категория: </p>
<p class="cat-text-2" >
Напитки</p>
<p class="price-text-1">Цена: </p>
<p class="price-text-2" th:text="${prod.price}">1800р</p>
Руб.
</div>
</div>
</div>
</div>
<div class="col-xxl-4">
<div class="categoty-count-wraper-2 product-count-wraper">
<a class="a-but-win red-but-prod" href="">Редактировать</a>
<p class="palka-p">/</p>
<button class="a-but-win but-del-win"
th:formaction="@{/admin-panel/product-list/delete/{id}(id=${prod.id})}">
Удалить
</button>
</div>
</div>
</div>
</div>
<!-- \\\\\\Колонка товара -->
</form>
</tr>
</div>
</div>
</div>
</main>
<!--Footer-->
<div th:insert="fragments/footer :: footer"></div>
<script src="js/js.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"
integrity="sha384-JEW9xMcG8R+pH31jmWH6WWP0WintQrMb4s7ZOdauHnUtxwoG2vI5DkLtS3qm9Ekf"
crossorigin="anonymous"></script>
</body>
</html>
Ответы (1 шт):
Идея решения такая:
<p class="cat-text-2" >
<ul class="categories">
<li class="category-name" th:each="category: ${prod.categoryProducts}">
<span th:text="${category.name}">Имя категории<span>
</li>
</ul>
</p>