Galera, sinceramente já estou batendo cabeça com esse projeto a algum tempo... infelizmente...
Agora, quando eu tento inserir um determinado livro no carrinho de compras estou obtendo o seguinte erro:
HTTP Status 500 - javax.el.ELException: Error reading 'price' on type br.com.bookstore.model.CartItem
Eu não sei se poderia ter sido apresentado o curso de Spring sem que essa decisão de design das classes do produto ter uma lista de Preço, e este ter um tipo que é uma ENUM de valores.... Gostaria um projeto com o Spring MVC que não tivesse trazido essa complexidade inicialmente pois achei que ficar tentando achar uma forma de relacionar o produto com o seu preço, neste projeto, mais difícil do que aprender o framework em si....
Vou tentar colocar o código aqui em baixo para ver se alguém pode me dar um help dessa vez, grato.
package br.com.bookstore.model;
import java.math.BigDecimal;
import java.util.Calendar;
import java.util.List;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import org.springframework.format.annotation.DateTimeFormat;
@Entity
public class Product {
// let the database manage the id
@Id @GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
private String title;
private String description;
private Integer pages;
private String fileLocation;
@DateTimeFormat
private Calendar published;
@ElementCollection
private List<Price> prices;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Integer getPages() {
return pages;
}
public void setPages(Integer pages) {
this.pages = pages;
}
public Calendar getPublished() {
return published;
}
public void setPublished(Calendar published) {
this.published = published;
}
public List<Price> getPrices() {
return prices;
}
public void setPrices(List<Price> prices) {
this.prices = prices;
}
@Override
public String toString() {
return "Product [title=" + title + ", description=" + description + ", pages=" + pages + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Product other = (Product) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
return true;
}
// iterates through types and filters the subtype until finds the first equal and picks the price of this specification being passed
public BigDecimal priceOf(PriceType priceType) {
return prices.stream().filter(price -> price.getType().equals(priceType)).findFirst().get().getValue();
}
public String getFileLocation() {
return fileLocation;
}
public void setFileLocation(String fileLocation) {
this.fileLocation = fileLocation;
}
}
package br.com.bookstore.model;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.web.context.WebApplicationContext;
@Component
@Scope(value=WebApplicationContext.SCOPE_SESSION)
public class ShoppingCart implements Serializable {
private static final long serialVersionUID = 1L;
private Map<CartItem, Integer> items = new LinkedHashMap<CartItem, Integer>();
public Collection<CartItem> getItems() {
return items.keySet();
}
public void addItemInCart(CartItem item) {
items.put(item, getQuantityOfAnItemInACart(item) + 1);
}
public Integer getQuantityOfAnItemInACart(CartItem item) {
if (!items.containsKey(item)) {
items.put(item, 0);
}
return items.get(item);
}
public Integer getQuantity() {
return items.values().stream().reduce(0, (next, acumulator) -> (next + acumulator));
}
public BigDecimal getTotalPriceOfAnItemInCart(CartItem item) {
return item.getTotal(getQuantityOfAnItemInACart(item));
}
public BigDecimal getTotal() {
BigDecimal total = BigDecimal.ZERO;
for(CartItem item : items.keySet()) {
//total = total.add(getTotal(item));
total = total.add(getTotalPriceOfAnItemInCart(item));
}
return total;
}
}
package br.com.bookstore.model;
import java.math.BigDecimal;
import javax.persistence.Embeddable;
@Embeddable
public class Price {
private BigDecimal value;
private PriceType type;
public BigDecimal getValue() {
return value;
}
public void setValue(BigDecimal value) {
this.value = value;
}
public PriceType getType() {
return type;
}
public void setType(PriceType type) {
this.type = type;
}
}
package br.com.bookstore.daos;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import br.com.bookstore.model.Product;
@Repository
@Transactional
public class ProductDAO {
@PersistenceContext
private EntityManager manager;
public void save(Product product) {
manager.persist(product);
}
public List<Product> list() {
return manager.createQuery("select p from Product p", Product.class).getResultList();
}
public Product find(Integer id) {
return manager.createQuery("select distinct(p) from Product p join"
+ " fetch p.prices prices where p.id = :id", Product.class).setParameter("id", id).getSingleResult();
}
}
package br.com.bookstore.model;
import java.math.BigDecimal;
public class CartItem {
private Product product;
private PriceType priceType;
public CartItem(Product product, PriceType priceType) {
this.product = product;
this.priceType = priceType;
}
public Product getProduct() {
return product;
}
public void setProduct(Product product) {
this.product = product;
}
public BigDecimal getPrice() {
return product.priceOf(this.priceType);
}
public BigDecimal getTotal(int quantity) {
return this.getPrice().multiply(new BigDecimal(quantity));
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((product == null) ? 0 : product.hashCode());
result = prime * result + ((priceType == null) ? 0 : priceType.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
CartItem other = (CartItem) obj;
if (product == null) {
if (other.product != null)
return false;
} else if (!product.equals(other.product))
return false;
if (priceType != other.priceType)
return false;
return true;
}
}
package br.com.bookstore.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.ModelAndView;
import br.com.bookstore.daos.ProductDAO;
import br.com.bookstore.model.PriceType;
import br.com.bookstore.model.CartItem;
import br.com.bookstore.model.Product;
import br.com.bookstore.model.ShoppingCart;
@Controller
@RequestMapping("/cart")
@Scope(value=WebApplicationContext.SCOPE_REQUEST)
public class ShoppingCartController {
@Autowired
private ProductDAO productDao;
@Autowired
private ShoppingCart cart;
@RequestMapping("/add")
public ModelAndView add(Integer productId, PriceType type) {
System.out.println(productId);
ModelAndView modelAndView = new ModelAndView("redirect:/cart");
CartItem item = createItem(productId, type);
cart.addItemInCart(item);
return modelAndView;
}
private CartItem createItem(Integer productId, PriceType type) {
Product product = productDao.find(productId);
CartItem cartItem = new CartItem(product, type);
return cartItem;
}
// must create an items method here to take the user directly to the cart detail page.
@RequestMapping(method=RequestMethod.GET)
public ModelAndView items() {
return new ModelAndView("/cart/items");
}
}
<%@ include file="/WEB-INF/includes.jsp"%>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>${product.title }details</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" />
</head>
<body>
<c:import url="/WEB-INF/views/header.jsp"></c:import>
<div class="main">
<div class="container">
<div class="row">
<div class="col-md-6">
<h1>Book Cover</h1>
</div>
<div class="col-md-6">
<h1 class="product-title">${product.title }</h1>
<h1 class="product-title">${product.id }</h1>
<p class="book-description">Description: ${product.description }.</p>
<p>Published in: <fmt:formatDate pattern="dd/MM/yyyy" value="${product.published.time}" />.</p>
<form action="${s:mvcUrl('SCC#add').arg(0,product.id).build()}" method="POST">
<p>Please, select your buying option below:</p>
<ul>
<c:forEach items="${product.prices }" var="price">
<li class="buy-option">
<!-- SECOND PARAMETER BEING SENT TO SHOPPING CART CONTROLLER -->
<!-- <input type="radio" name="type" class="variant-radio" checked="checked" /> -->
<input type="radio" name="type" class="variant-radio" value="${price.type}" checked="checked" />
<label class="variant-label">${price.type} - $ ${price.value }</label>
</li>
</c:forEach>
</ul>
<button type="submit" class="btn btn-primary" title="Compre Agora${product.title}">Buy now</button>
</form>
</div>
</div>
</div>
</div>
<c:import url="/WEB-INF/views/footer.jsp"></c:import>
</body>
<script type="text/javascript" src=""></script>
</html>
<%@ include file="/WEB-INF/includes.jsp"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" />
<title>List</title>
</head>
<body>
<c:import url="/WEB-INF/views/header.jsp"></c:import>
<div class="main">
<div class="container">
<h3>Products list</h3>
<p>${confirmationMessage}</p>
<table class="table table-striped table-hover ">
<tr>
<th>Title</th>
<th>Description</th>
<th>Pages</th>
<th>Published</th>
</tr>
<c:forEach items="${products }" var="product">
<tr>
<td><a href="${s:mvcUrl('PC#detail').arg(0,product.id).build() }">${product.title }</a></td>
<td>${product.description }</td>
<td>${product.pages }</td>
<td><fmt:formatDate pattern="dd/MM/yyyy" value="${product.published.time }" /></td>
</tr>
</c:forEach>
</table>
</div>
</div>
<c:import url="/WEB-INF/views/footer.jsp"></c:import>
</body>
<script type="text/javascript" src=""></script>
</html>
<%@ include file="/WEB-INF/includes.jsp" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Products</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" />
</head>
<body>
<c:import url="/WEB-INF/views/header.jsp"></c:import>
<div class="main">
<div class="container">
<form:form action="${s:mvcUrl('PC#saveProduct').build() }" method="POST" commandName="product" enctype="multipart/form-data">
<div class="form-group">
<label>Title</label>
<form:input path="title" cssClass="form-control"/>
</div>
<div>
<form:errors path="title"></form:errors>
</div>
<div class="form-group">
<label>Description</label>
<form:textarea path="description" rows="5" cols="20" cssClass="form-control"/>
</div>
<div>
<form:errors path="description"></form:errors>
</div>
<div class="form-group">
<label>Pages:</label>
<form:input path="pages" cssClass="form-control" />
</div>
<div>
<form:errors path="pages"></form:errors>
</div>
<div class="form-group">
<label>Published:</label>
<form:input path="published" cssClass="form-control" />
</div>
<div>
<form:errors path="published"></form:errors>
</div>
<c:forEach items="${types}" var="priceType" varStatus="status">
<div class="form-group">
<label>${priceType}</label>
<form:input path="prices[${status.index}].value" cssClass="form-control"/>
<form:hidden path="prices[${status.index}].type" value="${priceType}" />
</div>
</c:forEach>
<br>
<div class="form-group">
<label>Book summary file:</label>
<input type="file" name="file">
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form:form>
</div>
</div>
<c:import url="/WEB-INF/views/footer.jsp"></c:import>
</body>
<script type="text/javascript" src=""></script>
</html>
Eu verifiquei da view details para o controller ShoppingCartController /add o atributo price.type está nulo, muito embora eu esteja passando o mesmo na view e consigo exibi-lo na view através de sysout.
Mas esse atributo não chega para o controlador nem a &#!*$.
Agradeço a quem puder ajudar.....
Adicionando aqui a saída do erro:
type Exception report
message javax.el.ELException: Error reading 'price' on type br.com.bookstore.model.CartItem
description The server encountered an internal error that prevented it from fulfilling this request.
exception
org.apache.jasper.JasperException: javax.el.ELException: Error reading 'price' on type br.com.bookstore.model.CartItem
org.apache.jasper.servlet.JspServletWrapper.handleJspException(JspServletWrapper.java:565)
org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:481)
org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:385)
org.apache.jasper.servlet.JspServlet.service(JspServlet.java:329)
javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java:168)
org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:303)
org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1271)
org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1037)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:980)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
root cause
javax.el.ELException: Error reading 'price' on type br.com.bookstore.model.CartItem
javax.el.BeanELResolver.getValue(BeanELResolver.java:98)
org.apache.jasper.el.JasperELResolver.getValue(JasperELResolver.java:110)
org.apache.el.parser.AstValue.getValue(AstValue.java:169)
org.apache.el.ValueExpressionImpl.getValue(ValueExpressionImpl.java:184)
org.apache.jasper.runtime.PageContextImpl.proprietaryEvaluate(PageContextImpl.java:944)
org.apache.jsp.WEB_002dINF.views.cart.items_jsp._jspx_meth_c_005fforEach_005f0(items_jsp.java:282)
org.apache.jsp.WEB_002dINF.views.cart.items_jsp._jspService(items_jsp.java:168)
org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:443)
org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:385)
org.apache.jasper.servlet.JspServlet.service(JspServlet.java:329)
javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java:168)
org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:303)
org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1271)
org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1037)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:980)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
root cause
java.util.NoSuchElementException: No value present
java.util.Optional.get(Optional.java:135)
br.com.bookstore.model.Product.priceOf(Product.java:96)
br.com.bookstore.model.CartItem.getPrice(CartItem.java:23)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.lang.reflect.Method.invoke(Method.java:498)
javax.el.BeanELResolver.getValue(BeanELResolver.java:94)
org.apache.jasper.el.JasperELResolver.getValue(JasperELResolver.java:110)
org.apache.el.parser.AstValue.getValue(AstValue.java:169)
org.apache.el.ValueExpressionImpl.getValue(ValueExpressionImpl.java:184)
org.apache.jasper.runtime.PageContextImpl.proprietaryEvaluate(PageContextImpl.java:944)
org.apache.jsp.WEB_002dINF.views.cart.items_jsp._jspx_meth_c_005fforEach_005f0(items_jsp.java:282)
org.apache.jsp.WEB_002dINF.views.cart.items_jsp._jspService(items_jsp.java:168)
org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:443)
org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:385)
org.apache.jasper.servlet.JspServlet.service(JspServlet.java:329)
javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java:168)
org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:303)
org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1271)
org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1037)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:980)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
note The full stack trace of the root cause is available in the Apache Tomcat/8.5.11 logs.