Spring Boot: Found shared references to a collection error - mysql

I'm trying to build a small Spring Boot CRUD app with some basic e-commerce functionality (i.e. add to cart, etc.). My Basic entities are customer, cheese, roles and orders.
Customer's have a many-to-many relationship with Cheese (the fictional object I'm selling) objects. In addition, Orders have a many-to-many relationship with Cheese objects. When my customer checks out, I am intending to transfer the cart contents (i.e. the list of Cheeses) to the Order object, along with customer id, total price, etc. I want the "Orders" to be able to be logged by myself, as well as to provide an order history for the customer. The instantiating of the order object with customer.getCheeses() is what is giving me the shared collection error.
I can somewhat get around this by creating new Cheese items, however, that messes up my database, creating duplicates upon every new order.
The processing of orders is done in the completeOrder() function in UserController. All of the html/thymeleaf seems to be working - I can post it if it will help.
Cheese
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;
}
}
Customer
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;
}
}
Orders
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;
}
}
UserController
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";
}
}

So what you are trying to do is fetch and fill the cheese collection when you get a customer? Normally, in order to do that, you must set lazy loading to false, otherwise the session closes before you can fetch the collection.
To be able to load the customer with it's cheese collection, you must got to your Hibernate query and use a "join fetch" command. Something like this.
sessionFactory.getCurrentSession().createQuery("from Customer C join fetch C.cheeses").list();
This will force the query to fetch the cheese collection before the session closes. Also, one more thing, normally I would use a Set to avoid duplicates in the collection. I hope this helps.

Related

Postman SpringBoot RestApi status code 415 on POSTrequest

I have just started studying Springboot. Everything worked fine until I run into this problem. I've searched every StackOverFlow topic and internet overall and none resolved my problems. I tried to set Content-Type and Accepts the right way but it still didn't work.
UserController:
package com.example.carnet.api;
import com.example.carnet.model.User;
import com.example.carnet.services.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
#RestController
#RequestMapping("/carnet/user")
public class UserController {
private final UserService userService;
#Autowired
public UserController(UserService userService) {
this.userService = userService;
}
#GetMapping("/all")
public List<User> getUsers() {
return userService.getUsers();
}
#GetMapping(path = "/{email}")
public User getUserByEmail(#PathVariable("email") String email) {
return userService.getUserByEmail(email);
}
#GetMapping("/validate")
public boolean validateUser(#RequestParam("email") String email, #RequestParam("password") String password) {
return userService.validateUser(email, password);
}
#PostMapping("/add")
public void addUser(#RequestBody User user) {
userService.addUser(user);
}
#DeleteMapping(path = "/{id}")
public void deleteUserById(#PathVariable("id") int id) {
userService.deleteUserById(id);
}
#PutMapping
public void updatePassword(#RequestBody User user) {
userService.updatePassword(user);
}
}
User Model:
package com.example.carnet.model;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import java.sql.Date;
import java.util.List;
#Table(name = "users")
#Entity
public class User {
#Id
private int user_id;
private String email;
private String password;
private String name;
private String surname;
private Date birth_date;
#OneToMany(mappedBy = "user")
#JsonManagedReference
private List<Rental> rentals;
public User() {
}
public int getUser_id() {
return user_id;
}
public void setUser_id(int user_id) {
this.user_id = user_id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
public Date getBirth_date() {
return birth_date;
}
public void setBirth_date(Date birth_date) {
this.birth_date = birth_date;
}
}
Error after doing POST request with Postman:
{
"timestamp": "2020-05-06T19:01:16.498+0000",
"status": 415,
"error": "Unsupported Media Type",
"message": "Content type 'application/json;charset=UTF-8' not supported",
"path": "/carnet/user/add"
}
SOLVED: Removing #JsonManagedReference from User Model solved the problem!
This problem is because of the #JsonManagedReference in your model class,
Try interchanging #JsonBackReference and #JsonManagedReference. It should work.
Just explore the documentation for more info.
This is one similar issue reported for the same case.

How to store java objects in mysql using jpa?

I have converted the JSON into POJO using GSON.
I am looking to store Employee entity object into mysql using JPA's save() method. But I am getting an error saying "cannot determine the type for Address". So how should I go with this?
Here is the error:
Could not determine type for : Address
Employee.java
package com.example.demo;
import java.math.BigDecimal;
import java.util.Map;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import com.google.gson.annotations.Expose;
#Entity
public class Employee {
#Id
private int id;
private String name;
private int age;
private BigDecimal salary;
private String designation;
private Address address;
private long[] phoneNumbers;
/*Getter and Setter Methods*/
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public BigDecimal getSalary() {
return salary;
}
public void setSalary(BigDecimal salary) {
this.salary = salary;
}
public String getDesignation() {
return designation;
}
public void setDesignation(String designation) {
this.designation = designation;
}
public long[] getPhoneNumbers() {
return phoneNumbers;
}
public void setPhoneNumbers(long[] phoneNumbers) {
this.phoneNumbers = phoneNumbers;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
Address.java
package com.example.demo;
import javax.persistence.Entity;
import javax.persistence.Id;
//#Entity
public class Address {
#Id
private String street;
private String city;
private int zipCode;
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public int getZipCode() {
return zipCode;
}
public void setZipcode(int zipcode) {
this.zipCode = zipcode;
}
#Override
public String toString(){
return getStreet() + ", "+getCity()+", "+getZipCode();
}
}
Controller Class
package com.example.demo;
import net.sf.json.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import net.sf.json.JSONObject;
#Controller // This means that this class is a Controller
#RequestMapping(path="/demo") // This means URL's start with /demo
(after Application path)
public class MainController {
#Autowired // This means to get the bean called userRepository
// Which is auto-generated by Spring, we will use it
to handle the data
private UserRepository userRepository;
#GetMapping(path="/add") // Map ONLY GET Requests
public #ResponseBody String addNewUser (#RequestBody String json) {
// #ResponseBody means the returned String is the response, not a view name
// #RequestParam means it is a parameter from the GET or POST request
//JSONObject jsonObject = JSONObject.fromObject(json);
Gson gson=new GsonBuilder().create();
Employee employee =gson.fromJson(json,Employee.class);
userRepository.save(employee);
return "Successfully added to database using JPA!";
}
#GetMapping(path="/all")
public #ResponseBody Iterable<Employee> getAllUsers() {
// This returns a JSON or XML with the users
return userRepository.findAll();
}
}
Try adding implements Serializable for your Entity classes.
ie.
#Entity
public class Employee implements Serializable{
}
Whenever you use #Entity annotation in your class and trying to save its instance in database. At very intially a create table command is formed in hibernate which creates table in the database with the given specifications. As in employee table you specify the datatypes of all data-members. When cursor come to 'Address', it gave error because hibernate not able to find any datatype related this or any table related this.
As you commented #Entity annoatation in Address class. So no table regarding that will be created in database.
Address is class reference for this hibernate need the class(table). As class(table) is not there ,so the error comes.

Spring Boot: MySQL Duplicate key error when trying to map customers to items

I'm trying to build a small Spring Boot CRUD app with some basic e-commerce functionality (i.e. add to cart, etc.). My Basic entities are customer, cheese and roles.
In trying to map customers to cheeses, it works perfectly fine with only one user. A "customer-cheeses" table is generated with the customer accountNumber (id), along with the Cheese id. Here is a picture of the table:
However, when trying to add items with another account, I get an error like this:
com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException:
Duplicate entry '6' for key 'UK_pg95jxw3noahgyna6qwbl3ivd'
I am assuming that this just means I can only have one cheese id. I have tried to play around with different hibernate annotations (i.e. #ElementCollection, #manytomany, etc.), but have not been able to get this to work.
Any input would be appreciated. Also, please let me know if you need any of my services or controllers, but the actual adding and removing is working fine in my app.
Customer
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
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); // default value
}
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;
}
}
Cheese
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
private List<Customer> customers = 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;
}
}
Hmmm...so, I got it working by doing the following: I changed email to customer_id, which admittedly should not make a difference. For the cheese class, I made the many to many to be "mappedBy" cheeses.
Again, I'm not sure why this works, and the other didn't, but that seems to be the case.
Customer
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;
}
}
Cheese
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<>();
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;
}
}
I'm quite sure you could check your scheme settings to see if that field is configured as a UNIQUE field which means you can't have > 1 = 6.

Spring Security: Getting MySQL duplicate entry error when registering more than one user

I am trying to register and log-in users with Spring Security, as well as give them roles, which is required by spring security. I am currently receiving the following error when registering more than one user.
The error is:
com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry 'USER' for key 'UK_8sewwnpamngi6b1dwaa88askk'
The role_name can be the same for many users, so I made an 'id' for roles that is the primary key, and is autogenerated. My "USER_ROLES" table in my MySQL database only has one entry, which is the first email, and role_name, "USER". The Customer table has all the entries, regardless of errors. I'll keep working on this.
Thanks for looking.
Customer.java
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.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_NAME", referencedColumnName = "name")
})
private List<Role> roles;
public Customer(String name, String password, String email) {
this.name = name;
this.password = password;
this.email = email;
}
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;
}
}
Role.java
package com.example.demo.models;
import javax.persistence.*;
import java.util.List;
#Entity
public class Role {
#Id
#GeneratedValue
private int id;
private String name;
#ManyToMany(mappedBy = "roles")
private List<Customer> customers;
public Role(String name, List<Customer> customers) {
this.name = name;
this.customers = customers;
}
public Role(String name) {
this.name = name;
}
public Role() {}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Customer> getCustomers() {
return customers;
}
public void setCustomers(List<Customer> customers) {
this.customers = customers;
}
}
UserService.java
package com.example.demo.models.services;
import com.example.demo.models.Customer;
import com.example.demo.models.Role;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
#Service
public class UserService {
#Autowired
private UserRepository userRepository;
public void createUser(Customer customer) {
Role userRole = new Role("USER");
List<Role> roles = new ArrayList<>();
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
customer.setPassword(encoder.encode(customer.getPassword()));
roles.add(userRole);
customer.setRoles(roles);
userRepository.save(customer);
}
public void createAdmin(Customer customer) {
Role userRole = new Role("ADMIN");
List<Role> roles = new ArrayList<>();
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
customer.setPassword(encoder.encode(customer.getPassword()));
roles.add(userRole);
customer.setRoles(roles);
userRepository.save(customer);
}
}
UserRepository
package com.example.demo.models.services;
import com.example.demo.models.Customer;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<Customer, String> {
}
SecurityConfig
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import javax.sql.DataSource;
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
DataSource dataSource;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(dataSource)
.usersByUsernameQuery("select email as principal, password as credentials, true from customer where email=?")
.authoritiesByUsernameQuery("select customer_email as principal, role_name as role from user_roles where customer_email=?")
.passwordEncoder(passwordEncoder()).rolePrefix("ROLE_");
}
#Override
protected void configure(HttpSecurity http) throws Exception{
http
.csrf().disable()
.authorizeRequests()
.antMatchers(
"/**/webjars/**",
"/cheese/signup",
"/cheese/login",
"/cheese/success").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/cheese/login")
.defaultSuccessUrl("/cheese/account")
.permitAll();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Ok, so I got it to work. SecurityConfig is the SAME, except you need to change 'role_name' to 'role_id'.
Just to give a little bit of explanation, I did the following:
Add id to roles, make it Primary Key/autogenerated.
For the Customer many-to-many, I joined the customer email to (inverse join) the role 'id'
For the Role, I did a one-to-many mapping. joining the role 'id' with (inverse join) customer email.
This way I ended up with a 'user_roles' table where the role_id was mapped to the customer email, and a 'role' table where the role_id was mapped to the role (i.e. "USER", "ADMIN", etc.), allowing for duplicates. I am still a beginner, so maybe even this is not the best way to do it, but it worked for me.
The code...
Customer.java
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.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;
public Customer(String name, String password, String email) {
this.name = name;
this.password = password;
this.email = email;
}
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;
}
}
Role.java
package com.example.demo.models;
import javax.persistence.*;
import java.util.List;
#Entity
public class Role {
#Id
#GeneratedValue
private int id;
private String name;
#OneToMany(cascade=CascadeType.ALL)
#JoinTable(name="user_roles",
joinColumns={#JoinColumn(name="ROLE_ID", referencedColumnName="id")},
inverseJoinColumns={#JoinColumn(name = "CUSTOMER_EMAIL", referencedColumnName = "email")
})
private List<Customer> customers;
public Role(String name, List<Customer> customers) {
this.name = name;
this.customers = customers;
}
public Role(String name) {
this.name = name;
}
public Role() {}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Customer> getCustomers() {
return customers;
}
public void setCustomers(List<Customer> customers) {
this.customers = customers;
}
}

show details of all orders placed by customer

I am developing an E-Commerce website(Spring MVC, java, mySql, Hibernate) for my college project. I have various models like customer,product,orders,custOrderHistory, etc.
I am able to show all customer details(in admin page) and all products for a customer to browse through.
But the problem I am facing is, how do I show the Orders and order history of a customer?
My orders table has orderId, productId, quantity and total price. but to show the product name, I have to use the productId and rerieve the Product name from product table.
I have no clue on how to do it.
Till now, i was using the jstl foreach loop to iterate the products in the productList.jsp page and displaying them in a table.
But as i told above, i need to retrieve data from 3-4 tables at once, like productName(from product table), productManuacturer(also from product table), ShippingAddress (from customer table). And I am unsure how to do this.
Edit 1: I asked this question from someone else's computer and didn't have my code with me then.
So, I am giving my code here.
Customer.java
package com.site.model;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.hibernate.validator.constraints.NotEmpty;
import javax.persistence.*;
import java.io.Serializable;
#Entity
public class Customer implements Serializable {
private static final long serialVersionUID = -3280023076408333682L;
#Id
#GeneratedValue
private int customerId;
#NotEmpty(message = " The customer name must not be blank.")
private String customerName;
#NotEmpty (message = " The customer email must not be blank.")
private String customerEmail;
private String customerPhone;
#NotEmpty (message = " The username must not be blank.")
private String username;
#NotEmpty (message = " The password must not be blank.")
private String password;
private boolean enabled;
#OneToOne
#JoinColumn(name = "billingAddressId")
private BillingAddress billingAddress;
#OneToOne
#JoinColumn(name = "shippingAddressId")
private ShippingAddress shippingAddress;
#OneToOne()
#JoinColumn(name = "cartId")
#JsonIgnore
private Cart cart;
public int getCustomerId() {
return customerId;
}
public void setCustomerId(int customerId) {
this.customerId = customerId;
}
public String getCustomerName() {
return customerName;
}
public void setCustomerName(String customerName) {
this.customerName = customerName;
}
public BillingAddress getBillingAddress() {
return billingAddress;
}
public void setBillingAddress(BillingAddress billingAddress) {
this.billingAddress = billingAddress;
}
public ShippingAddress getShippingAddress() {
return shippingAddress;
}
public void setShippingAddress(ShippingAddress shippingAddress) {
this.shippingAddress = shippingAddress;
}
public String getCustomerPhone() {
return customerPhone;
}
public void setCustomerPhone(String customerPhone) {
this.customerPhone = customerPhone;
}
public String getCustomerEmail() {
return customerEmail;
}
public void setCustomerEmail(String customerEmail) {
this.customerEmail = customerEmail;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public Cart getCart() {
return cart;
}
public void setCart(Cart cart) {
this.cart = cart;
}
}
Cart.java
package com.site.model;
import com.fasterxml.jackson.annotation.JsonIgnore;
import javax.persistence.*;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
#Entity
public class Cart implements Serializable{
private static final long serialVersionUID = -2479653100535233857L;
#Id
#GeneratedValue
private int cartId;
#OneToMany(mappedBy = "cart", cascade= CascadeType.ALL, fetch = FetchType.EAGER)
private List cartItems = new ArrayList();
#OneToOne
#JoinColumn(name = "customerId")
#JsonIgnore
private Customer customer;
private double grandTotal;
public int getCartId() {
return cartId;
}
public void setCartId(int cartId) {
this.cartId = cartId;
}
public double getGrandTotal() {
return grandTotal;
}
public void setGrandTotal(double grandTotal) {
this.grandTotal = grandTotal;
}
public List getCartItems() {
return cartItems;
}
public void setCartItems(List cartItems) {
this.cartItems = cartItems;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
}
CartItem.java
package com.site.model;
import com.fasterxml.jackson.annotation.JsonIgnore;
import javax.persistence.*;
import java.io.Serializable;
import java.util.List;
#Entity
public class CartItem implements Serializable{
private static final long serialVersionUID = -904360230041854157L;
#Id
#GeneratedValue
private int cartItemId;
#ManyToOne
#JoinColumn(name="cartId")
#JsonIgnore
private Cart cart;
#ManyToOne
#JoinColumn(name = "productId")
private Product product;
private int quantity;
private double totalPrice;
#ManyToMany(cascade=CascadeType.ALL, mappedBy="cartItems")
#JsonIgnore
private List<OrderHistory> orderHistoryList;
public int getCartItemId() {
return cartItemId;
}
public void setCartItemId(int cartItemId) {
this.cartItemId = cartItemId;
}
public Product getProduct() {
return product;
}
public void setProduct(Product product) {
this.product = product;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
public double getTotalPrice() {
return totalPrice;
}
public void setTotalPrice(double totalPrice) {
this.totalPrice = totalPrice;
}
public Cart getCart() {
return cart;
}
public void setCart(Cart cart) {
this.cart = cart;
}
public List<OrderHistory> getOrderHistoryList() {
return orderHistoryList;
}
public void setOrderHistoryList(List<OrderHistory> orderHistoryList) {
this.orderHistoryList = orderHistoryList;
}
}
CustomerOrder.java
package com.site.model;
import javax.persistence.*;
import java.io.Serializable;
#Entity
public class CustomerOrder implements Serializable {
private static final long serialVersionUID = -3608286390950243118L;
#Id
#GeneratedValue
private int customerOrderId;
#OneToOne
#JoinColumn(name = "cartId")
private Cart cart;
#OneToOne
#JoinColumn(name = "customerId")
private Customer customer;
#OneToOne
#JoinColumn(name = "billingAddressId")
private BillingAddress billingAddress;
#OneToOne
#JoinColumn(name = "shippingAddressId")
private ShippingAddress shippingAddress;
public int getCustomerOrderId() {
return customerOrderId;
}
public void setCustomerOrderId(int customerOrderId) {
this.customerOrderId = customerOrderId;
}
public Cart getCart() {
return cart;
}
public void setCart(Cart cart) {
this.cart = cart;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
public BillingAddress getBillingAddress() {
return billingAddress;
}
public void setBillingAddress(BillingAddress billingAddress) {
this.billingAddress = billingAddress;
}
public ShippingAddress getShippingAddress() {
return shippingAddress;
}
public void setShippingAddress(ShippingAddress shippingAddress) {
this.shippingAddress = shippingAddress;
}
}
OrderHistory.java
package com.site.model;
import javax.persistence.*;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
#Entity
public class OrderHistory implements Serializable {
private static final long serialVersionUID = 1083533250613139445L;
#Id
#GeneratedValue
private int orderHistoryId;
private int customerId;
private String customerName;
private int customerOrderId;
private int cartId;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "cartItem_orderHistory", joinColumns = #JoinColumn(name = "orderHistoryId"),
inverseJoinColumns = #JoinColumn
(name = "cartItemId"))
private List<CartItem> cartItems = new ArrayList<CartItem>();
private double grandTotal;
private String billingAddress;
private String shippingAddress;
public int getOrderHistoryId() {
return orderHistoryId;
}
public void setOrderHistoryId(int orderHistoryId) {
this.orderHistoryId = orderHistoryId;
}
public int getCustomerId() {
return customerId;
}
public void setCustomerId(int customerId) {
this.customerId = customerId;
}
public String getCustomerName() {
return customerName;
}
public void setCustomerName(String customerName) {
this.customerName = customerName;
}
public int getCustomerOrderId() {
return customerOrderId;
}
public void setCustomerOrderId(int customerOrderId) {
this.customerOrderId = customerOrderId;
}
public int getCartId() {
return cartId;
}
public void setCartId(int cartId) {
this.cartId = cartId;
}
public List<CartItem> getCartItems() {
return cartItems;
}
public void setCartItems(List<CartItem> cartItems) {
this.cartItems = cartItems;
}
public double getGrandTotal() {
return grandTotal;
}
public void setGrandTotal(double grandTotal) {
this.grandTotal = grandTotal;
}
public String getBillingAddress() {
return billingAddress;
}
public void setBillingAddress(String billingAddress) {
this.billingAddress = billingAddress;
}
public String getShippingAddress() {
return shippingAddress;
}
public void setShippingAddress(String shippingAddress) {
this.shippingAddress = shippingAddress;
}
}
edit 2: how do I store the data in the ustomerorder table and orderhistory table, and also, how do i show it on a jsp page?
you should have a CustomerId column in order table for detecting the order is associated with which customer. Simply insert logged in userid in that and fetch the customer name by making join with customer table.
You should have customerid in your order table. So that you can fetch the customer's order history by providing customerid.