【问题标题】:Spring Boot: Found shared references to a collection errorSpring Boot:找到对集合错误的共享引用
【发布时间】:2018-06-06 23:09:07
【问题描述】:

我正在尝试构建一个具有一些基本电子商务功能(即添加到购物车等)的小型 Spring Boot CRUD 应用程序。我的基本实体是客户、奶酪、角色和订单。

客户与 Cheese(我正在销售的虚构对象)对象具有多对多关系。此外,Orders 与 Cheese 对象具有多对多的关系。当我的客户结账时,我打算将购物车内容(即奶酪列表)连同客户 ID、总价等一起转移到 Order 对象。我希望自己能够记录“订单” ,以及为客户提供订单历史记录。使用 customer.getCheeses() 实例化订单对象是给我共享集合错误的原因。

我可以通过创建新的奶酪项目来解决这个问题,但是,这会弄乱我的数据库,在每个新订单上创建重复项。

订单的处理在UserController的completeOrder()函数中完成。所有的 html/thymeleaf 似乎都在工作 - 如果有帮助,我可以发布它。

奶酪

package com.example.demo.models;

import javax.persistence.*;
import javax.validation.constraints.DecimalMax;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

@Entity
public class Cheese {

    @NotNull
    @Size(min=2, max=20)
    private String name;


    @NotNull
    @Size(min=2, max=20)
    private String description;

    @NotNull
    @DecimalMax("10000.0") @DecimalMin("0.0")
    private BigDecimal price;

    @Id
    @GeneratedValue
    private int id;


    @ManyToMany(mappedBy = "cheeses")
    private List<Customer> customers = new ArrayList<>();

    @ManyToMany(mappedBy = "cheeses")
    private List<Orders> orders = new ArrayList<>();

    public Cheese() {}

    public Cheese(String name, String description, BigDecimal price) {
        this.name = name;
        this.description = description;
        this.price = price;
    }

    public BigDecimal getPrice() {
        return price;
    }

    public void setPrice(BigDecimal price) {
        this.price = price;
    }

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public List<Orders> getOrders() {
        return orders;
    }

    public void setOrders(List<Orders> orders) {
        this.orders = orders;
    }
}

客户

package com.example.demo.models;

import javax.persistence.*;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

@Entity
public class Customer implements Serializable {

    @NotNull
    @Size(min = 2, max = 25)
    private String name;

    @GeneratedValue
    @Id
    private int accountNumber;

    private BigDecimal accountFunds;

    @NotNull
    @Size(min = 2)
    private String password;

    @NotNull
    @Size(min = 2, max = 25)
    @Email
    private String email;

    @ManyToMany(cascade=CascadeType.ALL)
    @JoinTable(name="user_roles",
            joinColumns={@JoinColumn(name="CUSTOMER_EMAIL", referencedColumnName = "email")},
            inverseJoinColumns={@JoinColumn(name="ROLE_ID", referencedColumnName="id")})
    private List<Role> roles;

    //@ElementCollection

    @ManyToMany(cascade=CascadeType.ALL)
    @JoinTable(name="cheese_customers",
            joinColumns={@JoinColumn(name="CUSTOMER_ID", referencedColumnName = "accountNumber")},
            inverseJoinColumns={@JoinColumn(name="PRODUCT_ID", referencedColumnName="id")})
    private List<Cheese> cheeses = new ArrayList<>();


    public Customer(String name, String password, String email) {
        this.name = name;
        this.password = password;
        this.email = email;
        this.accountFunds = new BigDecimal(225.00);
    }

    public Customer() {}

    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAccountNumber() {
        return accountNumber;
    }

    public List<Role> getRoles() {
        return roles;
    }

    public void setRoles(List<Role> roles) {
        this.roles = roles;
    }

    public BigDecimal getAccountFunds() {
        return accountFunds;
    }

    public void setAccountFunds(BigDecimal accountFunds) {
        this.accountFunds = accountFunds;
    }

    public List<Cheese> getCheeses() {
        return cheeses;
    }

    public void setCheeses(List<Cheese> cheeses) {
        this.cheeses = cheeses;
    }
}

订单

package com.example.demo.models;

import javax.persistence.*;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Entity
public class Orders {

    @GeneratedValue
    @Id
    private int orderId;

    @ManyToMany(cascade= CascadeType.ALL)
    @JoinTable(name="customer_orders",
            joinColumns={@JoinColumn(name="ORDER_ID", referencedColumnName = "orderId")},
            inverseJoinColumns={@JoinColumn(name="PRODUCT_ID", referencedColumnName="id")})
    private List<Cheese> cheeses = new ArrayList<>();

    private int customerId;

    private BigDecimal totalPrice;

    private Date date;

    public Orders() {}

    public Orders(List<Cheese> cheeses, int customerId, BigDecimal totalPrice) {

        this.cheeses = cheeses;
        this.customerId = customerId;
        this.totalPrice = totalPrice;

        this.date = new Date();
    }

    private String getFormattedDate() {
        DateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        return format.format(this.date);
    }

    public int getOrderId() {
        return orderId;
    }

    public List<Cheese> getCheeses() {
        return cheeses;
    }

    public void setCheeses(List<Cheese> cheeses) {
        this.cheeses = cheeses;
    }

    public int getCustomerId() {
        return customerId;
    }

    public void setCustomerId(int customerId) {
        this.customerId = customerId;
    }

    public BigDecimal getTotalPrice() {
        return totalPrice;
    }

    public void setTotalPrice(BigDecimal totalPrice) {
        this.totalPrice = totalPrice;
    }

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }
}

用户控制器

package com.example.demo.controllers;

import com.example.demo.models.Customer;
import com.example.demo.models.Orders;
import com.example.demo.models.data.CheeseDao;
import com.example.demo.models.data.CustomerDao;
import com.example.demo.models.data.OrdersDAO;
import com.example.demo.models.services.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;
import java.util.List;

@Controller
@RequestMapping("cheese")
public class UserController {

    @Autowired
    private CustomerDao customerDao;

    @Autowired
    UserService userService;

    @Autowired
    CheeseDao cheeseDao;

    @Autowired
    OrdersDAO ordersDAO;

    @RequestMapping(value = "login")
    public String loginPage(Model model) {
        model.addAttribute("title", "Login Page");
        model.addAttribute("customer", new Customer());
        return "cheese/login";
    }

    @RequestMapping(value = "account")
    public String accountInfo(Model model) {

        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        Customer customer = customerDao.findByEmail(authentication.getName());

        model.addAttribute("name", customer.getName());
        model.addAttribute("funds", customer.getAccountFunds());
        model.addAttribute("customer", customer);
        model.addAttribute("cheeses", customer.getCheeses());
        model.addAttribute("total", userService.getCartTotal(customer));

        return "cheese/account";
    }

    @PostMapping(value = "account")
    public String removeItem(@RequestParam int cheeseId) {

        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        Customer customer = customerDao.findByEmail(authentication.getName());

        if (customer.getCheeses().contains(cheeseDao.getCheeseById(cheeseId))) {
            customer.getCheeses().remove(cheeseDao.getCheeseById(cheeseId));
        }

        customerDao.save(customer);

        return "redirect:/cheese/account";

    }

    @RequestMapping(value = "checkout")
    public String orderCheckout(Model model) {

        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        Customer customer = customerDao.findByEmail(authentication.getName());

        model.addAttribute("cheeses", customer.getCheeses());
        model.addAttribute("total", userService.getCartTotal(customer));

        return "cheese/checkout";
    }

    @GetMapping("signup")
    public String displaySignUpForm(Model model) {
        model.addAttribute("title", "Sign Up");
        model.addAttribute("customer", new Customer());
        return "cheese/signup";
    }

    @PostMapping(value = "signup")
    public String processSignUp(Model model, @ModelAttribute Customer customer, Errors errors) {

        if (errors.hasErrors()) {
            return "cheese/signup";
        }

        userService.createUser(customer);
        return "cheese/success";
    }

    @GetMapping("ordersuccess")
    public String showForm() {
        return "cheese/ordersuccess";
    }


    @PostMapping("checkout")
    public String completeOrder() {

        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        Customer customer = customerDao.findByEmail(authentication.getName());

        double accountFunds = customer.getAccountFunds().doubleValue();
        double cartTotal = userService.getCartTotal(customer).doubleValue();

        if (accountFunds >= cartTotal) {
            accountFunds = accountFunds - cartTotal;

        customer.setAccountFunds(new BigDecimal(accountFunds));

        Orders order = new Orders(customer.getCheeses(), customer.getAccountNumber(), new BigDecimal(cartTotal));
        customer.getCheeses().clear();

        customerDao.save(customer);
        ordersDAO.save(order);

        return "redirect:/cheese/ordersuccess";
        }
        return "redirect:cheese/checkout";
    }

    @GetMapping("orders")
    public String viewOrderHistory(Model model) {

        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        Customer customer = customerDao.findByEmail(authentication.getName());

        List<Orders> orders = ordersDAO.findOrdersByCustomerId(customer.getAccountNumber());
        model.addAttribute("orders", orders);

        return "cheese/orders";
    }

}

【问题讨论】:

    标签: mysql spring hibernate spring-boot spring-data-jpa


    【解决方案1】:

    那么您要做的是在获得客户时获取并填充奶酪系列?通常,为了做到这一点,您必须将延迟加载设置为 false,否则会话会在您获取集合之前关闭。

    为了能够加载客户的奶酪集合,您必须进入 Hibernate 查询并使用“join fetch”命令。像这样。

    sessionFactory.getCurrentSession().createQuery("from Customer C join fetch C.cheeses").list();
    

    这将强制查询在会话关闭之前获取奶酪集合。另外,还有一件事,通常我会使用 Set 来避免集合中的重复。我希望这会有所帮助。

    【讨论】:

    • 感谢您的回答 - 那么,我需要将延迟加载设置为 false 并使用上面的查询吗?我会在我的 completeOrder() 函数中编写查询吗?
    • 查询应该进入你的customerDao,你从中获取你的客户。我相信它应该去这里 customerDao.findByEmail()。使用上面的查询也会将延迟加载设置为 false。
    猜你喜欢
    • 2020-07-19
    • 2019-10-24
    • 2012-08-12
    • 2010-12-14
    • 1970-01-01
    • 2011-08-07
    • 2012-10-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多