Ainda não tem acesso? Estude com a gente! Matricule-se
Ainda não tem acesso? Estude com a gente! Matricule-se

Solucionado (ver solução)

Livraria EJB - Alterar livro com erro

Boa noite pessoal, Importei a aplicação da livraria utilizando EJB (Wildfly) sem realizar nenhuma alteração nela e notei um erro ao tentar alterar um livro. Me parece ser um erro por não ter carregado a lista de autores. Podem me auxiliar como resolver este problema de uma forma "Nico" (boa). Segue trechos dos codigos: Livro.xhtml:

                    <p:dataTable value="#{livroBean.autoresDoLivro}" var="autor" id="tabelaAutores" emptyMessage="Nenhum autor">
                        <p:column>
                            <h:outputText value="#{autor.nome}" />
                        </p:column>
                        <p:column>
                            <p:commandLink value="X" action="#{livroBean.removerAutorDoLivro(autor)}" update="tabelaAutores" process="@this"/>
                        </p:column>
                    </p:dataTable>
                </p:fieldset>
.
.
.    
                <p:column headerText="Alterar">
                    <p:commandButton update=":formLivro" process="@this" value="alterar" actionListener="#{livroBean.carregar(livro)}" icon="fa fa-fw fa-edit" />
                </p:column>
6 respostas

LivroBean.java

package br.com.caelum.livraria.bean;

import java.io.Serializable;
import java.util.List;

import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.ValidatorException;
import javax.faces.view.ViewScoped;
import javax.inject.Inject;
import javax.inject.Named;
import javax.transaction.Transactional;

import br.com.caelum.livraria.dao.AutorDao;
import br.com.caelum.livraria.dao.LivroDao;
import br.com.caelum.livraria.modelo.Autor;
import br.com.caelum.livraria.modelo.Livro;

@Named
@ViewScoped
public class LivroBean implements Serializable {

    private static final long serialVersionUID = 1L;

    private Livro livro = new Livro();

    private Integer autorId;

    private List<Livro> livros;

    @Inject
    LivroDao livroDao;

    @Inject
    AutorDao autorDao;

    @Inject
    FacesContext context;

    public void setAutorId(Integer autorId) {
        this.autorId = autorId;
    }

    public Integer getAutorId() {
        return autorId;
    }

    public Livro getLivro() {
        return livro;
    }

    public List<Livro> getLivros() {
        if (this.livros == null) {
            this.livros = livroDao.listaTodos();
        }
        return livros;
    }

    public List<Autor> getAutores() {
        return autorDao.listaTodos();
    }

    public List<Autor> getAutoresDoLivro() {
        return this.livro.getAutores();
    }

    public void carregarLivroPelaId() {
        this.livro = livroDao.buscaPorId(this.livro.getId());
    }

    public void gravarAutor() {
        Autor autor = autorDao.buscaPorId(this.autorId);
        this.livro.adicionaAutor(autor);
        System.out.println("Escrito por: " + autor.getNome());
    }

    // begin
    @Transactional
    public void gravar() {
        System.out.println("Gravando livro " + this.livro.getTitulo());

        if (livro.getAutores().isEmpty()) {
            context.addMessage("autor", new FacesMessage(
                    "Livro deve ter pelo menos um Autor."));
            return;
        }

        if (this.livro.getId() == null) {
            livroDao.adiciona(this.livro);
            this.livros = livroDao.listaTodos();
        } else {
            livroDao.atualiza(this.livro);
        }

        this.livro = new Livro();
    }

    // commit

    @Transactional
    public void remover(Livro livro) {
        System.out.println("Removendo livro");
        livroDao.remove(livro);
        this.livros = livroDao.listaTodos();
    }

    public void removerAutorDoLivro(Autor autor) {
        this.livro.removeAutor(autor);
    }

    public void carregar(Livro livro) {
        System.out.println("Carregando livro");
        this.livro = this.livroDao.buscaPorId(livro.getId());
    }

    public String formAutor() {
        System.out.println("Chamanda do formulário do Autor.");
        return "autor?faces-redirect=true";
    }

    public void comecaComDigitoUm(FacesContext fc, UIComponent component,
            Object value) throws ValidatorException {

        String valor = value.toString();
        if (!valor.startsWith("1")) {
            throw new ValidatorException(new FacesMessage(
                    "ISBN deveria começar com 1"));
        }

    }
}

Livro.java

package br.com.caelum.livraria.modelo;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

@Entity
public class Livro implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String titulo;
    private String isbn;
    private double preco;
    @Temporal(TemporalType.DATE)
    private Calendar dataLancamento = Calendar.getInstance();

    @ManyToMany
    private List<Autor> autores = new ArrayList<Autor>();

    public List<Autor> getAutores() {
        return autores;
    }

    public void adicionaAutor(Autor autor) {
        this.autores.add(autor);
    }

    public Livro() {
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getTitulo() {
        return titulo;
    }

    public void setTitulo(String titulo) {
        this.titulo = titulo;
    }

    public String getIsbn() {
        return isbn;
    }

    public void setIsbn(String isbn) {
        this.isbn = isbn;
    }

    public double getPreco() {
        return preco;
    }

    public void setPreco(double preco) {
        this.preco = preco;
    }

    public Calendar getDataLancamento() {
        return dataLancamento;
    }

    public void setDataLancamento(Calendar dataLancamento) {
        this.dataLancamento = dataLancamento;
    }

    public void removeAutor(Autor autor) {
        this.autores.remove(autor);
    }

}

Erro:

21:23:33,795 SEVERE [javax.enterprise.resource.webcontainer.jsf.application] (default task-34) Error Rendering View[/livro.xhtml]: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: br.com.caelum.livraria.modelo.Livro.autores, could not initialize proxy - no Session
    at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:587)
    at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:204)
    at org.hibernate.collection.internal.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:148)
    at org.hibernate.collection.internal.PersistentBag.size(PersistentBag.java:261)
    at javax.faces.model.ListDataModel.isRowAvailable(ListDataModel.java:109)
    at javax.faces.model.ListDataModel.setRowIndex(ListDataModel.java:184)
    at javax.faces.model.ListDataModel.setWrappedData(ListDataModel.java:219)
    at javax.faces.model.ListDataModel.<init>(ListDataModel.java:78)
    at org.primefaces.component.api.UIData.getDataModel(UIData.java:767)
solução

Bom dia Fabrício, por padrão relacionamentos ToMany como:

@ManyToMany
    private List<Autor> autores = new ArrayList<Autor>();

São lazy, então em tela, quando tentamos acessar algum atributo do autor a entityManager está fechada! Temos duas maneiras de resolver, fazendo uma query planejada EAGER ou usar o padrão "open session in view" e abrir a conexão na tela e fazer durar por uma requisição completa

Obrigado pela ajuda Guilherme. O orientador do curso (Nico) utilizava EAGER nas primeiras aulas, porém ele retira esse configuração pois diz ser boas praticas, alegando que muitas vezes carregamos informações sem necessidade (nesse caso a lista de autores). Pode me auxiliar com a segunda forma de resolver?Eu pensei por exemplo: ao clicar no botão alterar, fazer com que no LivroBean ele carregue "forçadamente" a lista de autores no método carregar. Isso seria uma boa pratica?

Ficaria da seguinte forma: LivroBean.java

    public void carregar(Livro livro) {
        System.out.println("Carregando livro");
        this.livro = this.livroDao.buscaPorId(livro.getId());
        List<Autor> listaAutores = this.autorDao.buscaPorIdLivro(this.livro.getId());
        this.livro.setAutores(listaAutores );
    }