I have 5 models:
admin
food
orders
payments
users
admin - admin can view/add/edit/delete food, and getting all users data(this part is done).
users - users can create orders.
so by my understanding, I did the mapping as follows:
users(1)-->orders(M)
orders(1)-->food(M)
orders(1)-->payments(1)
here is my code for all entity.
public class Users {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="user_id")
private Long usersId;
#Column(name="user_fName")
private String firstName;
#Column(name="user_lName")
private String lastName;
#Column(name="user_email")
private String email;
#Column(name="user_password")
private String password;
#Column(name="user_phnNumber")
private String phNumber;
#Column(name="user_address")
private String address;
#ToString.Exclude
#EqualsAndHashCode.Exclude
#OneToMany(cascade = CascadeType.PERSIST,mappedBy= "users")
private Set<Orders> order;
}
public class Payments {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="payment_id")
private Long paymentId;
#Column(name="payment_amount")
private Long paymentAmount;
#Column(name="payment_date")
private Date paymentDate;
#Column(name="payment_transactionId")
private Long paymentTransactionId;
#OneToOne
#JoinColumn(name="order_id")
Orders orders;
public Payments(Long paymentAmount, Date paymentDate, Long paymentTransactionId,Orders orders1) {
super();
this.paymentAmount = paymentAmount;
this.paymentDate = paymentDate;
this.paymentTransactionId = paymentTransactionId;
this.orders = orders1;
}
}
public class Orders {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="order_id")
private Long orderId;
#ManyToOne
#JoinColumn(name="user_id")
Users users;
#OneToMany(cascade = CascadeType.ALL)
private Set<Food> food;
#OneToOne(cascade = CascadeType.ALL)
private Payments payments;
#Column(name="order_quantity")
private Long orderQuantity;
#Column(name="order_date")
private Date orderDate;
#Column(name="order_status")
private Boolean orderStatus;
public Orders(Long orderQuantity, Date orderDate, Boolean orderStatus,Users users1) {
super();
this.orderQuantity = orderQuantity;
this.orderDate = orderDate;
this.orderStatus = orderStatus;
this.users = users1;
}
}
public class Food {
#Id
#SequenceGenerator(
name="food_sequence",
sequenceName="food_sequence",
allocationSize=1
)
#GeneratedValue(
strategy=GenerationType.SEQUENCE,
generator="food_sequence"
)
#Column(name="food_id")
private Long foodId;
#Column(name="food_name")
private String foodName;
#Column(name="food_desc")
private String foodDesc;
#Column(name="food_price")
private String foodPrice;
#Column(name="food_status")
private Boolean foodStatus;
#Column(name="food_img")
private String foodImg;
#ManyToOne
#JoinColumn(name="orders_id")
Orders orders;
public Food(String foodName,
String foodDesc,
String foodPrice,
Boolean foodStatus,
String foodImg,
Orders order1
) {
super();
this.foodName = foodName;
this.foodDesc = foodDesc;
this.foodPrice = foodPrice;
this.foodStatus = foodStatus;
this.foodImg = foodImg;
this.orders = order1;
}
}
So my Question: how to create an order. and when I see food table it has column orders_id. but here first admin will add food and then in the users side user will create orders using those food.
I am stuck at creating an order.
It would be better to remove the Payments column in the Orders class and the Orders column in the Food class.
Every order has a Payment, and you already do this by joining Order inside the Payments class. Likewise, there can be more than one Food in an Order, and you do this inside the Orders class.
To create an order, as you said, first the admin needs to add food to the table. After the food data is added to the table, the user will select food to create an order and you will add the selected food to the Order object's food attribute.(This could be better btw)
Then you will create a Payment with the Order that you created and manage the Payment separately.
Related
I am Using Spring Boot on Java to create user's order on his checkout. A new Orders object is created which has a Linked Set of Items. Those items are user's cart contents.
Order is created, but its set of Items is null. The set size is 0. I checked that in JUnit tests. Can you help me to find out what is wrong? Maybe I have defined entities incorrectly? Have a look at the picture of the database:
And check the entities, Orders:
#Entity
public class Orders {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#NotEmpty
#DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime submitedAt;
#NotEmpty
private String orderName;
#NotEmpty
#Column(name="`User`")
private String username;
#Enumerated(EnumType.STRING)
#Column
private OrderStatus status;
#OneToMany(mappedBy = "orders", cascade = { CascadeType.ALL}, fetch = FetchType.LAZY)
private Set<Item> items;
Item:
#Entity
public class Item {
#Id
private Integer id;
#Column(name="`NAME`")
private String dishName;
#Column(name = "`DESCRIPTION`", length = 2000)
private String dishDescription;
#Column(name = "`QUANTITY`")
private Integer quantityInCart;
#Column(name = "`USER`")
private String username;
#ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH })
#JoinColumn(name = "ORDERS_ID")
private Orders orders;
How to do entities relation correctly? Should it be one direction or bi-directional relationship?
What are differences of these relations? And what kind of relationship I should use? Why?
I was doing JUnit tests for the Orders service methods. It turns out that it can create orders. And Order items from user's cart.
But when it is time to show order (GetMapping) then it returns Orders entity with empty items set.
I think it happens because JPA cannot find foreign key of items for its designated order. It is null.
Why is it null?
And this is the service method that creates such order by user request:
#Transactional
public ResponseEntity<String> createOrder (String username) {
User user = userService.findByUsername(username);
List<CartItem> items = cartRepo.findByUser(user);
if(items.size() > 0) {
Orders newOrder = new Orders();
Set<Item> orderItems = new LinkedHashSet<>();
for(CartItem item : items) {
// new Item(Integer id, String dishName, String dishDescription, Integer quantityInCart, String username)
Item orderItem = new Item(item.getId(), item.getDish().getName(),
item.getDish().getDescription(), item.getQuantity(), item.getUser().getUsername());
orderItems.add(orderItem);
}
newOrder.setItems(orderItems);
newOrder.setOrderName(user.getUsername()+"'s order");
newOrder.setStatus(OrderStatus.SUBMIT);
newOrder.setSubmitedAt();
newOrder.setUsername(username);
orderDao.save(newOrder);
cartService.removeAllUserProducts(username);
LOG.info("[{}]: A new order is created successfully.", username);
return new ResponseEntity<String>("A new order is created successfully.", HttpStatus.CREATED);
}
//...
}
I tried to do one direction relationship for other entities and it really created foreign keys on joined column fields. But I want to find out why my bidirectional way of joining is wrong. Maybe someone who really knows can explain.
The Order class should be like this:
#Entity
public class Orders {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#NotEmpty
#DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime submitedAt;
#NotEmpty
private String orderName;
#NotEmpty
#Column(name="`User`")
private String username;
#Enumerated(EnumType.STRING)
#Column
private OrderStatus status;
#OneToMany(cascade = { CascadeType.ALL}, fetch = FetchType.LAZY, orphanRemoval = true)
#JoinColumn(name="ORDERS_ID")
private Set<Item> items;
And Item class without Orders class and its ManyToOne relationship.
Now relationship is unidirectional. Item entity has foreign keys column name ORDERS_ID that has id's of Orders for which Items belong.
I have three tables customer, product and sales. sales is the join table to store the customer's products as shown below:
customer, product and Sales tables
My Entities defined as described below:
Customer.java
#Entity
#Table(name="customer")
public class Customer {
#Id
#Column(name="c_id")
private String customerId;
#Column(name="customer_name")
private String customerName;
#ManyToMany
#JoinTable(
name = "sale",
joinColumns = #JoinColumn(name = "c_id"),
inverseJoinColumns = #JoinColumn(name = "p_id"))
private Set<Product> customerProducts = new HashSet<>();
}
Product.java
#Entity
#Table(name="product")
public class Product {
#Id
#Column(name="p_id")
private String productId;
#Column(name="product_name")
private String productName;
#Column(name="price")
private Double price;
// ... Setters & Getters
}
Sales.java
#Entity
#Table(name="sales")
public class Sales {
#EmbeddedId
private SalesPK salesId;
#Column(name="qty")
private Long qty;
// ... Setters & Getters
}
SalesPK.java
#Embeddable
public class SalesPK implements Serializable {
#Column(name = "c_id")
private String customerId;
#Column(name = "p_id")
private String productId;
public SalesPK() {}
public SalesPK(String customerId, String productId) {
this.customerId = customerId;
this.productId = productId;
}
}
CustomerRepository.java
#Repository
public interface CustomerRepository extends CrudRepository<Customer, String> {
#Query("select customer from Customer customer " +
"left join fetch customer.customerProducts " +
"where customer.customerName = :customerName")
public Customer getCustomerPurchasedProducts(String customerName);
}
My Spring boot application fail to start with following exception:
org.hibernate.MappingException: Foreign key (FK7wwx8x75009xqb1y0tawm8rty:SALES [p_id])) must have same number of
columns as the referenced primary key (SALES [c_id,p_id])
What am I missing here? I have followed the notes as described here in https://www.baeldung.com/jpa-many-to-many
UPDATE:
There is no issue with above solution, I have misspelled "sales" table in #ManyToMany declaration changing from "sale" to "sales" fixed the issue. Strange behavior why it didn't compline about missing table instead it complain about actual composite primary key definition.
Following code Fixed the issue:
#ManyToMany
#JoinTable(
name = "sales",
joinColumns = #JoinColumn(name = "c_id"),
inverseJoinColumns = #JoinColumn(name = "p_id"))
private Set<Product> customerProducts = new HashSet<>();
}
I would map these classes a bit differently:
#Entity
#Table(name="customer")
public class Customer {
#Id
#Column(name="c_id")
private String customerId;
#Column(name="customer_name")
private String customerName;
#OneToMany(mappedBy = "customer")
private Set<Sale> customerSales = new HashSet<>();
}
#Entity
#Table(name="product")
public class Product {
#Id
#Column(name="p_id")
private String productId;
#Column(name="product_name")
private String productName;
#Column(name="price")
private Double price;
}
#Entity
#Table(name="sales")
public class Sales {
#EmbeddedId
private SalesPK salesId;
#MapsId("customerId") // maps customerId attribute of embedded id
#ManyToOne
Customer customer;
#MapsId("productId") // maps productId attribute of embedded id
#ManyToOne
Product product;
#Column(name="qty")
private Long qty;
// ... Setters & Getters
}
#Embeddable
public class SalesPK implements Serializable {
#Column(name = "c_id")
private String customerId;
#Column(name = "p_id")
private String productId;
public SalesPK() {}
public SalesPK(String customerId, String productId) {
this.customerId = customerId;
this.productId = productId;
}
}
I have created two Entities namely Teacher and Detail, the code snippet is shown below
Teacher.java
#Entity
#Table(name = "teacher")
public class Teacher implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private long id;
#Column(name = "name")
private String name;
#Column(name = "age")
private int age;
#OneToOne(mappedBy = "teacher", cascade = CascadeType.ALL)
private Detail detail;
public Teacher() {
}
public Teacher(String name, int age) {
this.name = name;
this.age = age;
}
//getter and setter
}
Detail.java
#Entity
#Table(name = "detail")
public class Detail implements Serializable {
#Id
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "id")
private Teacher teacher;
#Column(name = "subjects")
private String subjects;
public Detail() {
}
public Detail(String subjects) {
this.subjects = subjects;
}
//getter and setter
}
I am trying to achieve one to one mapping with the shared primary key concept
but when i execute the controller, only Teacher table is updating with the value
try {
Teacher teacher=new Teacher("xyz",23);
Detail detail=new Detail("Java,c,c++");
teacher.setDetail(detail);
session.beginTransaction();
session.save(teacher);
session.getTransaction().commit();
model.addAttribute("added", "data inserted");
session.close();
}
After executing only Teacher table is updated with the specified values.Detail table is still showing empty
It does not work exactly like that. You still need the id field in your Detail, so add:
#Id
private long id;
to your Deatail class.
And - as comment suggests - replace the #Id annotation in field Teacher to #MapsId. This way the id of Teacher is mapped to the id of Detail BUT ONLY if you also set the teacher to the detail - you always need to set both sides of relationship - like:
teacher.setDetail(detail);
detail.setTeacher(teacher);
I would like to perform select statement on Northwind database like this bellow.
select distinct b.*, a.CategoryName
from Categories a
inner join Products b on a.CategoryID = b.CategoryID
where b.Discontinued = 'N'
order by b.ProductName;
I have two problems regarding this operation :
I have created POJO for tables categories and products like bellow
Table Products
#Entity
public class Products {
#Id
private Long productid;
private String productname;
private Long supplierid;
#ManyToOne
#JoinColumn(name = "categories", referencedColumnName = "categoryid")
private Categories categoryid;
private String quantityperunit;
private Double unitprice;
private Long unitsinstock;
private Long unitsonorder;
private Long reorderlevel;
private String discontinued;
Table Categories
#Entity
public class Categories {
#Id
private Long categoryid;
private String categoryname;
private String description;
private String picture;
Now I have no idea how to write rowmapper for this tables (please find below ????)
private static final RowMapper<Products> productsRowMapper = (rs, rowNum) ->{
Products products = new Products();
products.setProductid(rs.getLong("ProductID"));
products.setProductname(rs.getString("ProductName"));
products.setSupplierid(rs.getLong("SupplierID"));
products.setCategoryid(rs.?????
products.setQuantityperunit(rs.getString("QuantityPerUnit"));
products.setUnitprice(rs.getDouble("UnitPrice"));
products.setUnitsinstock(rs.getLong("UnitsInStock"));
products.setUnitsonorder(rs.getLong("UnitsOnOrder"));
products.setReorderlevel(rs.getLong("ReorderLevel"));
products.setDiscontinued(rs.getString("Discontinued"));
return products;
};
the second problem is that I don't know if the annotations on the column categoryid in the table products are correct?
After correction
#Repository
public class JdbcProductsDao implements ProductsDao{
private final JdbcTemplate jdbcTemplate;
#Autowired
public JdbcProductsDao(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
private static final RowMapper<Products> productsRowMapper = (rs, rowNum) ->{
Products products = new Products();
products.setProductid(rs.getLong("ProductID"));
products.setProductname(rs.getString("ProductName"));
products.setSupplierid(rs.getLong("SupplierID"));
products.setCategoryid(new Categories(rs.getString("CategoryName")));
products.setQuantityperunit(rs.getString("QuantityPerUnit"));
products.setUnitprice(rs.getDouble("UnitPrice"));
products.setUnitsinstock(rs.getLong("UnitsInStock"));
products.setUnitsonorder(rs.getLong("UnitsOnOrder"));
products.setReorderlevel(rs.getLong("ReorderLevel"));
products.setDiscontinued(rs.getString("Discontinued"));
return products;
};
public Products findByProductName(String productname) {
String sql = "SELECT * FROM products WHERE ProductName = ?";
return jdbcTemplate.queryForObject(sql, productsRowMapper, productname);
}
public List<Products> sortByProductName(){
String sql = "SELECT * FROM products order by ProductName asc";
return jdbcTemplate.query(sql, productsRowMapper);
}
Table Categories
#Entity
public class Categories {
#Id
private Long categoryid;
#Column(name = "CategoryName")
private String categoryname;
private String description;
private String picture;
#OneToMany(mappedBy="categoryid")
private List<Products> products;
Table Products
#Entity
public class Products {
#Id
private Long productid;
#Column(name = "ProductName")
private String productname;
private Long supplierid;
#ManyToOne
private Categories categoryid;
private String quantityperunit;
private Double unitprice;
private Long unitsinstock;
private Long unitsonorder;
private Long reorderlevel;
private String discontinued;
Now I have no idea how to write rowmapper for this tables (please find
below ????)
You have to set an Object like this :
products.setCategoryid(new Categories(rs.getString("a.categoryname"));
I assume you have an constructor Categories(String categoryname)
Note : If you want to get more information, you have to change your query and your constructor as well.
the second problem is that I don't know if the annotations on the
column categoryid in the table products are correct?
I think there are no need to use #JoinColumn(name = "categories", referencedColumnName = "categoryid") just use :
#ManyToOne
private Categories categoryid;
In your Categories entities add this :
#OneToMany(mappedBy="categoryid")
List<Products> products;
I have 2 tables 'orders' and 'orderlines' and used bidirectional OneToMany mapping.When i save the order, record is successfully inserted into table 'orders'.But my 'orderlines' table is empty.No record is inserted.
This is the save operation code in Controller.
#RequestMapping(value = "ordersuccess", method = RequestMethod.POST)
public String processOrder(#ModelAttribute("order") Order order,
#ModelAttribute("cart") Cart cart,
BindingResult result) {
if (!result.hasErrors()) {
Set<OrderLine> orderLines = new HashSet<OrderLine>();
for(CartLine c : cart.getCartLines()) {
OrderLine line = new OrderLine();
line.setOrder(order);
line.setProduct(c.getProduct());
line.setProductPrice(c.getProduct().getPrice());
line.setTotalPrice(c.getPrice());
orderLines.add(line);
order.setOrderLines(orderLines);
}
orderService.save(order);
orderLineService.save(orderLine);
}
return "ordersuccess";
}
Can someone point me what wrong i am doing.
EDIT:
OrderLine.java
public class OrderLine {
#Id
#GeneratedValue
#Column(name="orderline_id")
private int orderline_id;
#ManyToOne
#JoinColumn(name = "order_id")
private Order order;
#ManyToOne(targetEntity = Product.class,
cascade = CascadeType.ALL,
fetch = FetchType.LAZY)
#JoinTable(
name="products",
joinColumns=#JoinColumn(name="product_id")
)
private Product product;
)
Order.java
public class Order {
#Id
#GeneratedValue
#Column(name="id")
private int id;
#OneToMany(mappedBy = "order")
private Set<OrderLine> orderLines;
//getter/setter
The orderLines object is created:
Set<OrderLine> orderLines = new HashSet<OrderLine>();
You then add lines to it:
orderLines.add(line);
But it never attributed to an order or sent to the service layer.
Also the OrderLine.product mapping should be like this
public class OrderLine {
#Id
#GeneratedValue
#Column(name="orderline_id")
private int orderline_id;
#ManyToOne
#JoinColumn(name = "order_id")
private Order order;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "product_id")
private Product product;
}
and Order.orderLines should have a cascade:
public class Order {
#Id
#GeneratedValue
#Column(name="id")
private int id;
#OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private Set<OrderLine> orderLines;
}
You then need to save the orderLines:
order.setOrderLines(orderLines);
and save the order:
orderService.save(order);
When order is saved it will cascade the orderlines and the associated product too.
If you have bidirectional associations don't forget to set both sides.