I want to make an orderform with spring boot where I can Save the order with more order items.
I dont't know how to implement the Service, Class and even the thymeleaf page for this.
Any hint would be great!
Here's a picture what I want to make
An here's my two entity class(no getters and setters, and customer for brevity)
#Entity
#Table(name = "order_item")
public class OrderItem {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "order_id")
private Order order;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "product_id")
private Product product;
private int qty;
private double amount;
public OrderItem() {}
public OrderItem(int id, Order order, Product product, int qty, double amount) {
super();
this.id = id;
this.order = order;
this.product = product;
this.qty = qty;
this.amount = amount;
}
#Entity
#Table(name="order")
public class Order {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
private Date dateTime;
private double total;
private int paidStatus;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="customer_id")
private Customers customer;
#OneToMany(mappedBy="customOrder")
private List<OrderItem> orderItems;
You simply need to create a repository, service and controller.
1. First, let's create repositories for our models.
public interface CustomerRepository extends JpaRepository<Customer, Long> {}
public interface ProductRepository extends JpaRepository<Product, Long> {}
public interface OrderRepository extends JpaRepository<Order, Long> {}
2. Second, let's create our service layer.
(Note: I gathered all the functionality here for an example.You can distribute it to different layers.)
public interface OrderService {
List<Customer> findAllCustomers();
List<Product> findAllProducts();
List<Order> findAllOrders();
}
#Service
public class OrderServiceImpl implements OrderService {
private final CustomerRepository customerRepository;
private final ProductRepository productRepository;
private final OrderRepository orderRepository;
public OrderServiceImpl(CustomerRepository customerRepository,
ProductRepository productRepository,
OrderRepository orderRepository) {
this.customerRepository = customerRepository;
this.productRepository = productRepository;
this.orderRepository = orderRepository;
}
#Override
public List<Customer> findAllCustomers() {
return customerRepository.findAll();
}
#Override
public List<Product> findAllProducts() {
return productRepository.findAll();
}
#Override
public List<Order> findAllOrders() {
return orderRepository.findAll();
}
}
3. Now add a controller layer, this will reply to your urls. (Note: here are simple examples just to help you understand the operation. You can come up with many different solutions.)
#Controller
#RequestMapping("/order")
public class OrderController {
private final OrderService orderService;
public OrderController(OrderService orderService) {
this.orderService = orderService;
}
#GetMapping("/create")
public String createOrder(Model model) {
model.addAttribute("customers", orderService.findAllCustomers());
model.addAttribute("products", orderService.findAllProducts());
model.addAttribute("order", new Order());
return "order-form";
}
#PostMapping("/insert")
public String insertOrder(Model model, Order order) {
// Save operations ..
return "order-view";
}
}
4. Here, customers and products come from your database.
The 'Submit Form' button will be sending the entity id's of the selections here to the insertOrder method. (You can duplicate your other fields in a similar way and I recommend you to examine the example in this link to dynamically duplicate this product selection area.)
<!DOCTYPE HTML>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<body>
<div>
<form action="/order/insert" method="post" th:object="${order}">
<p>
<label>Select Customer</label>
</p>
<p>
<select name="customer.id">
<option th:each="customer : ${customers}"
th:value="${customer.id}"
th:text="${customer.name}">Customer Name</option>
</select>
</p>
<p>
<label>Select Product</label>
</p>
<p>
<select name="orderItems[0].product.id">
<option th:each="product : ${products}"
th:value="${product.id}"
th:text="${product.name}">Product Name</option>
</select>
<input type="text" name="orderItems[0].quantity" />
</p>
<button type="submit">Submit Form</button>
</form>
</div>
</body>
</html>
I recommend you to read this example, which has scope for necessary library and spring settings.
Related
I have a registration form page index.html for users for a car rental app I'm building
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Register</title>
</head>
<body>
<form action="#" th:action="#{/register}" th:object="${user}"
method="post">
<p>User Name <input type="text" name="name"></p>
<p>Password <input type="password" name="password"></p>
<button type="submit">Register</button>
</form>
</body>
</html>
And a corresponding controller class where I want to invoke a new user using the factory class then save it to MySQL DB
Controller Class:
#Controller
public class IndexController {
#Autowired
private UserService userService;
private CustomerFactory userFactory;
private UserController controller;
#GetMapping("/")
public String registerForm(Model model){
return "index";
}
#GetMapping("/car-list")
public String carList(){
return "index";
}
#PostMapping("/register")
public String registerUser(#ModelAttribute User user){
User u = userFactory.createUser(user.getName(), user.getPassword());
//userService.saveUser(user);
return "car-list";
}
}
Here is the User class:
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "userID", nullable = false)
private int userID;
private String name;
private String password;
public User() {
}
public User(String name, String password) {
this.name = name;
this.password = password;
}
//getters setters and toString omitted
}
Here is my factory class:
public class CustomerFactory implements com.project.CS4125.service.UserFactory {
#Override
public User createUser(String name, String password) {
return new User(name, password);
}
}
My problem is I need to create the new User through the factory class then save it but get this error
: Cannot invoke "com.project.CS4125.service.CustomerFactory.createUser(String, String)" because "this.userFactory" is null
Any help appreciated.
Take a look at this section of your code. How many autowired fields does it show?
#Autowired
private UserService userService;
private CustomerFactory userFactory;
private UserController controller;
The answer is one, the UserService.
Annotations such as #Autowired apply only to the element that immediately follows them. This section of code shows one field with an #Autowired annotation and two fields without this annotation. These other two fields are thus left at their default value of null, hence the NullPointerException attempting to call a method of the CustomerFactory.
If you want values for all three of these fields to be autowired, ensure all three fields have an #Autowired annotation:
#Autowired
private UserService userService;
#Autowired
private CustomerFactory userFactory;
#Autowired
private UserController controller;
I'm learning spring boot from a project https://github.com/rstyro/admin
In page/role/list.html,there is code:
<button th:if="${QX.add == '1' && QX.query == '1'}" class="btn btn-success
btn-sm" id="addRole"><i class="fa fa-plus"></i> add role</button>
I want to check what is this QX entity, So I go to RoleController.java
#RequestMapping(value="/list",method=RequestMethod.GET)
public Object list(Model model){
model.addAttribute("roles", roleService.list());
return "page/role/list";
}
then RoleService.java
#Service
public class RoleService implements IRoleService{
#Autowired
private RoleDao roleDao;
#Autowired
private MenuService menuService;
private Logger log = Logger.getLogger(this.getClass());
#Override
public List<ParameterMap> list() {
return roleDao.list();
}
then RoleDao.java
public interface RoleDao {
public List<ParameterMap> list();
public List<ParameterMap> getRoleByuId(ParameterMap pm);
public ParameterMap getRoleById(ParameterMap pm);
public void updateRoleQX(ParameterMap pm);
public void addRole(ParameterMap pm);
public void delRole(String roleId);
public void delUserRole(String roleId);
}
and RoleMapper.xml
<mapper namespace="com.lrs.admin.dao.RoleDao" >
<select id="list" resultType="pm">
SELECT
role_id,
role_name,
role_desc,
rights,
add_qx,
del_qx,
edit_qx,
query_qx
from
sys_role
</select>
and Role.java
public class Role {
private long roleId;
private String roleName;
private String roleDesc;
private String rights;
private String addQX;
private String delQX;
private String editQX;
private String queryQX;
public long getRoleId() {
return roleId;
}
But nothing is there. Am I missing something? Thx.
He is calling
return roleDao.list();
But there is no implementation of that statement, RoleDao interface is not implemented anywhere. Which means there is no bean called roleDao, which means, you cannot run this code. It will give you error.
The author probably did not share RoleDao implementation.
I Have simple JSP page whichs should display 5 Products with id, description, amount. Can you help me to find a problem ?
I want to Connect this page with Database, manualy listing products will be not professional and problematic when you want to add more Products.
When i request page /storage i have an exception :
[ERROR] Servlet.service() for servlet [dispatcherServlet] in context with path []
threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause
java.lang.NullPointerException: null
at (...)StoragePageController.openAllProductsPage(StoragePageController.java:27)
Storage.JSP :
<table width="1000" border="0" cellpadding="6" cellspacing="2">
<c:forEach var="u" items="${productList }">
<c:set var="licznik" value="${licznik+1}" />
<tr onmouseover="changeTrBg(this)" onmouseout="defaultTrBg(this)">
<td align="right"><c:out value="${licznik }" /></td>
<td align="right"><c:out value="${u.id }" /></td>
<td align="left"><c:out value="${u.description }" /></td>
<td align="left"><c:out value="${u.amount }" /></td>
</tr>
</c:forEach>
</table>
StoragePageController :
#Controller
public class StoragePageController {
private StorageService storageService;
#GET
#RequestMapping(value = "/storage")
public String openAllProductsPage(Model model) {
List<Product> productList = storageService.findAll();
model.addAttribute("productList", productList);
return "storage";
}
}
StorageService :
public interface StorageService {
public Product findById(int id);
List<Product> findAll();
void updateProduct(int id, int amount);
}
StorageServiceImpl :
#Service("storageService")
#Transactional
public class StorageServiceImpl implements StorageService {
#Autowired
private ProductRepository productRepository;
#Override
public Product findById(int id) {
Product product = productRepository.findById(id);
return product;
}
#Override
public List<Product> findAll() {
List<Product> productList = productRepository.findAll();
return productList;
}
#Override
public void updateProduct(int id, int amount) {
productRepository.updateProductAmount(amount, id);
}
}
ProductRepository :
#Repository("productRepository")
public interface ProductRepository extends JpaRepository<Product, Integer>{
#Modifying
#Query(value = "UPDATE Products p SET p.amount = :amount WHERE p.product_id= :id", nativeQuery = true)
void updateProductAmount(#Param("amount") int amount, #Param("id") int id);
public Product findById(int id);
}
and **Product: **
#Entity
#Table(name = "products")
public class Product {
#Id
#Column(name = "product_id")
private int id;
#Column(name = "description")
#NotNull
private String description;
#Column(name = "amount")
#NotNull
private int amount;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public int getAmount() {
return amount;
}
public void setAmount(int amount) {
this.amount = amount;
}
}
SQL if needed :
DROP TABLE IF EXISTS `products`;
CREATE TABLE `products` (
`product_id` int(11) NOT NULL AUTO_INCREMENT,
`description` varchar(255) NOT NULL,
`amount` INT(64) NOT NULL,
PRIMARY KEY (`product_id`)
) ENGINE=InnoDB DEFAULT CHARSET=UTF8;
It seems like you are missing #Autowired annotation in the StoreController
#Controller
public class StoragePageController {
#Autowired
private StorageService storageService;
#GET
#RequestMapping(value = "/storage")
public String openAllProductsPage(Model model) {
List<Product> productList = storageService.findAll();
model.addAttribute("productList", productList);
return "storage";
}
}
OR
The most recommended way to the dependency injection is to create a constructor with all dependencies.
#Controller
public class StoragePageController {
private final StorageService storageService;
public StoragePageController(StorageService storageService) {
this.storageService = storageService;
}
#GET
#RequestMapping(value = "/storage")
public String openAllProductsPage(Model model) {
List<Product> productList = storageService.findAll();
model.addAttribute("productList", productList);
return "storage";
}
}
I have this model:
Order.java
#Entity
#Table(name = "`order`")
public class Order {
private Long id;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "order")
private List<OrderProduct> orderProducts;
#Override
public int hashCode() {
return new Long(id).hashCode();
}
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (!(obj instanceof Order)) {
return false;
}
return this.id == ((Order) obj).getId();
}
/* getters & setters */
}
OrderProduct.java
#Entity
#Table(name = "order_product")
public class OrderProduct {
private Long id;
private String name;
private Long quantity;
#ManyToOne
#JoinColumn(name = "order_id")
private Order order;
/* getters & setters */
}
And this controller:
OrderController.java
#Controller
#SessionAttributes({"products"})
public class OrderController {
#Autowired
private OrderService orderService;
#Autowired
private ProductService productService;
#Autowired
private SecurityService securityService;
#Autowired
private UserService userService;
#RequestMapping(value = "/order/add", method = RequestMethod.GET)
public String addOrder(Model model) {
Order order = new Order();
order.setOrderProducts(new AutoPopulatingList<>(OrderProduct.class));
model.addAttribute("orderForm", order);
model.addAttribute("products", productService.findAll());
return "order/add";
}
#RequestMapping(value = "/order/add", method = RequestMethod.POST)
public String addOrder(#ModelAttribute("orderForm") Order orderForm, BindingResult bindingResult, Model model) {
orderForm.setUser(userService.findByUsername(securityService.findLoggedInUsername()));
for (OrderProduct orderProduct : orderForm.getOrderProducts()) {
orderProduct.setOrder(orderForm);
}
orderService.save(orderForm);
return "redirect:/order/view";
}
}
And I want to create a form where a user can choose few products, give a quantity of them and submit his order. I was trying to write JSP code like that:
<form:form method="POST" modelAttribute="orderForm">
<spring:bind path="orderProducts">
<tr>
<td>
<c:forEach items="${products}" var="product">
<form:checkbox path="orderProducts[${product.id}].name"
value="${product.name}"
label="${product.name}"/>
<form:input type="text" path="orderProducts[${product.id}].quantity" placeholder="Quantity"/>
</c:forEach>
</td>
</tr>
</spring:bind>
<button class="btn btn-lg btn-primary btn-block" type="submit">Submit</button>
</form:form>
...but unfortunately it's probably trying to create as many orderProducts as I have in products list and as a result the orderProducts, which a service try to save, doesn't have properly setted values and at the end there is created an order without the orderProducts. So my question is how can I properly send the orderProducts via JSP code to the controller which finally will create the right order with its items?
I am suspecting this ${product.id}. As this is an index value, it should be like
orderProducts[0].name orderProducts[0].quantity
orderProducts[1].name orderProducts[1].quantity
orderProducts[2].name orderProducts[2].quantity
I solved it by removing an orderProducts from an orderForm which don't have a quantity properly setted in my OrderController but probably it isn't the best solution.
I try to display a part of gallery's photo=> this is result when i did the search action. This result have avatar like a picture and infomation like username or email.
I create Photo.java like child of Users.java in relationship #ManytoOne
Here is my code :
Photo.java----
#Entity
public class Photo extends Model{
#Id
public Long id;
public String path;
#ManyToOne
#JoinColumn(name = "user_id")
public Users user;
}
Users.java-----
#Entity
public class Users extends Model{
#Id
public Long id;
#Constraints.Required
public String username;
#Constraints.Required
public String email;
#Constraints.Required
public String password;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "user")
public List<Photo> photo = new ArrayList<Photo>();
public Users(){}
public Users(String username,String email,String password){
this.username=username;
this.email=email;
this.password=password;
}
}
Search.java -----
public static Result search(){
DynamicForm form = form().bindFromRequest();
String name = form.get("name");
Finder<Long, Users> find = new Finder<Long, Users>(Long.class, Users.class);
List<Users> users = find.where().like("username", '%'+ name +'%').findList();
if (form.get("name")=="" || users.isEmpty() || users==null){
return ok(search_again.render());
}
else{
return ok (search_result.render(users));
}
}
search_result.scala.html----
#(users : List[Users])
#main(nav= "search"){
<h3>Result</h3>
<input class="button" type="button" value="Back to Search">
<input class="button" type="button" value="Back to Home">
<p>Found #users.size() result(s) : </p>
<div class="sresult">
#for(user <- users){
<div id="sresult">
<div id="haha"><img src="#routes.Assets.at("upload/"+user.photo.path)"></div>
//Error here. Why "user.photo.path" not working ?
<p>
#user.username</a></br>
#user.password</a></br>
#user.email</a>
</p>
</div>
}
</div>
}
Why "user.photo.path" not working ? any ideal in my case ?