4
respostas

EntityManager do Transaction não executa de forma correta

Boa noite,

A classe que gerencia as transações não está executando o EntityManager de forma correta, parece que o CDI não está injetando o EntityManager que deveria e no final nada acontece.

@Transacional
@Interceptor
public class GerenciadorDeTransacao implements Serializable {

    private static final long serialVersionUID = 1L;

    @Inject
    EntityManager manager;

    @AroundInvoke
    public Object executaTX(InvocationContext contexto) throws Exception {
        manager.getTransaction().begin();

        Object resultado = contexto.proceed();

        manager.getTransaction().commit();

        return resultado;
    }
}

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 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;
import br.com.caelum.livraria.tx.Transacional;

@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
    private LivroDao livroDao;

    @Inject
    private AutorDao autorDao;

    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());
    }

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

        if (livro.getAutores().isEmpty()) {
            FacesContext.getCurrentInstance().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();
    }

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

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

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

    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"));
        }

    }

    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();
    }
}
4 respostas

O que não está acontecendo de forma correta?

Parece que o EntityManager que está sendo injetado não representa a mesma sessão, o que ocorre que as informações no final não são alteradas, removidas ou inseridas no banco de dados.

Descobri o problema, eu pensei que o escopo default do CDI era @RequestScoped e aí deixei a factory do EntityManager sem anotação, quando eu coloquei @RequestScoped o problema foi resolvido mas pode me dizer porque o EntityManager não funciona como deveria com o escopo padrão na Factory?

Opa Rafael, eu acho que sei.. veremos se faz sentido :). Se vc deixa o produto de EntityManager no escopo default(Dependent), no momento que esse EntityManager for injetado em alguém, ele vai assumir o escopo desse alguém. Caso você tenha injetado no Dao, que deve ser Dependent também, ele vai assumir o escopo do local que o Dao foi injetado.. Aí chegamos no seu ManagedBean... Ele ta ViewScoped, o que significa que o EntityManager ta ficando lá enquanto vc ta mesma view...

Existe uma boa chance do Interceptor ser instanciado para toda nova requisiçao, causando uma nova instanciação do EntityManager que, por sua vez, é diferente do que está utilizado no seu ManagedBean.

Caso isso faça sentido, justifica o motivo de nada ser gravado e tal.. Vc estaria comitando com um EntityManager diferente do que está sendo utilizado no ManagedBean.

Definindo o escopo como Request ajusta tudo.. pq aí, todo mundo, durante o request, vai receber o mesmo entityManager.