Solucionado (ver solução)
Solucionado
(ver solução)
5
respostas

Error reading 'price' on type br.com.bookstore.model.CartItem

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.
5 respostas

Descobri algo através desse tópico https://cursos.alura.com.br/forum/topico-terminei-o-curso-mas-fiquei-com-3-duvidas-29711

Conseguiu resolver? O problema aí é que o produto está sem um dos tipos de preço.. agora para saber qual, vc vai precisar dar uma debugada :(.

O problema foi resolvido... já estou conseguindo chegar aqui na página que exibe o que existe dentro do carrinho de compras.... vou só da um double check em algo aqui e já posto o resultado para ter certeza....

                    <form action="${s:mvcUrl('SCC#add').arg(0,product.id).build()}" method="POST">
<%--                     <form action="<c:url value='/cart/add' />" method="post" class="container"> --%>
<%--                     <input type="hidden" name="productId" value="${product.id}" /> --%>

De fato, fiz um double check aqui comentando as linhas em que eu definia a tag do form e a sua action utilizando mvcUrl e o type passa a chegar null quando da criação de um item.

Verifiquei também a url que ficou http://localhost:8080/book-store/cart/add?productId=1&type, ou seja, creio que ele tenha enviado para o local correto mas com o type null.

Utilizando a tag ele envia para o mesmo endereço, a url no browser ficou assim: http://localhost:8080/book-store/cart com os parâmetros dentro do post...

Alberto Souza, aí eu lhe pergunto, faz ideia do porque disso?

Conforme eu citei já, eu não fui o único a passar por esse problema e seria bem interessante saber o porque disso, já que em outras partes da aplicação conseguimos passar parâmetros via mvcUrl normalmente...

Agradeço demais qualquer ajuda na compreensão deste fato.

O projeto está em: https://github.com/DaviGadelhaLeitao/book-store

solução!

Como o type ta sendo definido pela opção do usuário no formulário, vc não consegue passar o argumento no mvcUrl. Vc precisa ter o input com esse name no form.