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

Transação com o banco está fechada como resolver?

Bom, vou explicar o que está acontecendo. Estou criando um crud simples pra treinar a conexão com o banco de dados utilizando JPA e aprendendo como criptografar a senha para salvar no banco. Estou fazendo esse crud utilizando Servlet para a parte web. O problema se dar quando rodo o servidor e realizo o primeiro cadastro ele funciona normalmente só que quando vou fazer um novo cadastro ele da um erro dizendo que a conexão está fechada, alguém tem como me explicar como resolver isso? Vou deixar todo codigo a abaixo.

CODIGO:

@Entity
@Table(name = "clientes")
public class Cliente {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "Id")
    private Long id;
    @Column(name = "Nome")
    private String nome;
    @Column(name = "Email")
    private String email;
    @Column(name = "Senha")
    private String senha;
    
    public Cliente(String nome, String email, String senha) {
        super();
        this.nome = nome;
        this.email = email;
        this.senha = senha;
    }

    public Long getId() {
        return id;
    }

    public String getNome() {
        return nome;
    }

    public String getEmail() {
        return email;
    }

    public String getSenha() {
        return senha;
    }
        
}
public class ClienteRepositorio {

    public void cadastrar(Cliente cliente) throws SQLException {
        EntityManager manager = JPAUtil.getEntityManager();
        EntityTransaction transaction = manager.getTransaction();

        try {
            transaction.begin();
            manager.persist(cliente);
            transaction.commit();
        } catch (Exception e) {

            if (transaction.isActive()) {
                transaction.rollback();
            }

            throw new SQLException(e);
        } finally {
            JPAUtil.closeEntityManager();
        }
    }
}
public class ClienteServico {
    
    private ClienteRepositorio repository = new ClienteRepositorio();

    public void cadatrarCliente(Cliente cliente) throws SQLException {

        String nome = cliente.getNome();
        String email = cliente.getEmail();
        String senha = cliente.getSenha();

        String senhaHash = BCrypt.hashpw(senha, BCrypt.gensalt(12));
        
        Cliente clientCadastrado = new Cliente(nome,email,senhaHash);
        
        repository.cadastrar(clientCadastrado);
        
    }
}
public class CadastroClienteBean implements TipoAcao {

    @Override
    public String execute(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String nome = req.getParameter("nome");
        String email = req.getParameter("email");
        String senha = req.getParameter("senha");

        Cliente cliente = new Cliente(nome, email, senha);

        ClienteServico servico = new ClienteServico();

        try {
            servico.cadatrarCliente(cliente);
        } catch (SQLException e) {
            e.printStackTrace();
        }

        return "forward:listar-clientes.jsp";
    }
}
public class JPAUtil {
    
    private static final EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("cliente");

    public static EntityManager getEntityManager() {
        return entityManagerFactory.createEntityManager();
    }

    public static void closeEntityManager() {
        entityManagerFactory.close();
    }
}
@WebServlet(urlPatterns = "/cliente")
public class ClienteControlador extends HttpServlet{
    
    private static final long serialVersionUID = 1L;

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        String action = req.getParameter("acao");

        String fqn = "br.com.cliente.bean." + Character.toUpperCase(action.charAt(0)) + action.substring(1);

        String nome;

        try {
            Class<?> classe = Class.forName(fqn);
            @SuppressWarnings("deprecation")
            TipoAcao acao = (TipoAcao) classe.newInstance();
            nome = acao.execute(req, resp);
        } catch (ClassNotFoundException | SecurityException | InstantiationException
                | IllegalAccessException | IllegalArgumentException e) {
            throw new ServletException(e);
        }

        String[] tipoEndereco = nome.split(":");

        if (tipoEndereco[0].equals("forward")) {
            RequestDispatcher dispatcher = req.getRequestDispatcher(tipoEndereco[1]);
            dispatcher.forward(req, resp);
        } else {
            resp.sendRedirect(tipoEndereco[1]);
        }
    }
}

O ERRO:

Insira aqui a descrição dessa imagem para ajudar na acessibilidade

2 respostas
solução!

Fala Bruno!

Imagino que o seu erro pode estar aqui:

public class ClienteRepositorio {

    public void cadastrar(Cliente cliente) throws SQLException {
        EntityManager manager = JPAUtil.getEntityManager();
        EntityTransaction transaction = manager.getTransaction();

        try {
            transaction.begin();
            manager.persist(cliente);
            transaction.commit();
        } catch (Exception e) {

            if (transaction.isActive()) {
                transaction.rollback();
            }

            throw new SQLException(e);
        } finally {
            JPAUtil.closeEntityManager();
        }
    }
}

No final da execução, dando certo ou não, o método sempre fecha a fábrica EntityManagerFactory. Mas veja bem... Vamos analisar um pouco o código do seu JPAUtil:

public class JPAUtil {
    
    private static final EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("cliente");

    public static EntityManager getEntityManager() {
        return entityManagerFactory.createEntityManager();
    }

    public static void closeEntityManager() {
        entityManagerFactory.close();
    }
}

A sua EntityManagerFactory é uma constante, você usou nela os modificadores de acesso static final, quando o atributo é declarado como static final ele pertence à classe e não a uma instância específica, como ele é um final quer dizer que o seu valor é constante, ou seja, é imutável, ele não pode receber outra instância.

Mas o que isso quer dizer?

Quer dizer que a sua EntityManagerFactory é imutável, que o atributo entityManagerFactory da classe JPAUtil só recebe a referencia do Persistence.createEntityManagerFactory("cliente") uma única vez, então quando você fecha essa entityManagerFactory ela não pode mais ser aberta, por essa variável ser constante ela não irá mais receber novas referências de Persistence.createEntityManagerFactory("cliente") .

Por isso que quando o método cadastrar() roda uma segunda vez você recebe o erro "java.lang.IllegalStateException: EntityManagerFactory está fechado". Ele tenta rodar o método getEntityManager(), esse método está usando a sua entityManagerFactory para criar uma instância do EntityManager, mas a fabrica já está fechada.

Acredito que se em vez de você fechar a fabrica que cria os seus EntityManager, fechar apenas a instância do EntityManager (referência manager) deve resolver.

Aqui vai um exemplo:

public class ClienteRepositorio {

    public void cadastrar(Cliente cliente) throws SQLException {
        EntityManager manager = JPAUtil.getEntityManager();
        EntityTransaction transaction = manager.getTransaction();

        try {
            transaction.begin();
            manager.persist(cliente);
            transaction.commit();
        } catch (Exception e) {

            if (transaction.isActive()) {
                transaction.rollback();
            }

            throw new SQLException(e);
        } finally {
            manager.close();
        }
    }
}

Eu espero que isso resolva pra você!

Percebi a semelhança do seu projeto com aquele projeto criado nos cursos de Java Web: crie aplicações com Servlets e MVC. Eu também estive praticando o mesmo que você, usei o projeto final dessa formação como base para praticar. Dá uma olhada nele no meu Github, toda a evolução do projeto está lá, acredito que pode te dar umas ideias. Apliquei Maven, JPA com Hibernate, Lombok, persistência com MySQL, criptografia de senhas com Bcrypt, estilo CSS, JS para Front e requisições assincronas AJAX e, até mesmo Docker... Criei novas funções, melhorei o código do projeto e criei um visual mais agradável. Se você tiver ideias pra contribuir também são muito bem-vindas.

https://github.com/mateuspontess/gerenciador.git

Vou da uma olhada sim! Valeu pelas dicas