Hibernate + Mysql = Extremamente Lento

Olá,

estou encerrando o desenvolvimento da aplicação para o meu TCC, são duas aplicações, uma delas é praticamente um CRUD Java + Hibernate + Mysql, o problema é que quando eu populo o banco de dados dessa aplicação com por exemplo 100.00 registros, a mesma fica extremamente lenta, até mesmo em tabelas que contenham por exemplo apenas 2 tuplas, chegando a demorar minutos só para realizar um login, mesmo que eu tenha apenas um usuário cadastrado na tabela de usuários.

Também tenho uma outra aplicação aqui que já deve ter uns 3.000 registros e estou notando que ela já está começando a ficar consideravelmente lenta.

Quando faço uma consulta qualquer através do netbeans, mesmo no bd com 100.00 registros o resultado é instantâneo, mas na minha aplicação demora muito tempo.

Vou deixar aqui os arquivos de configuração do hibernate e a minha classe DAO abstrata para que vocês possam me ajudar a encontrar o erro, porém, caso alguém esteja interessado em ver alguma outra parte do código para poder detectar o erro eu posso postar com prazer.

Conto com a ajuda de vocês, obrigado.


HIBERNATE.CFG.XML

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
  <session-factory>
    <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
    <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
    <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/smid_server</property>
    <property name="hibernate.connection.username">root</property>
    <property name="hibernate.connection.password">admin</property>
    <property name="hibernate.show_sql">false</property>
    <mapping resource="persistencia/hibernate/mapeamentos/Municipio.hbm.xml"/>
    <mapping resource="persistencia/hibernate/mapeamentos/Imovel.hbm.xml"/>
    <mapping resource="persistencia/hibernate/mapeamentos/Visitapesquisa.hbm.xml"/>
    <mapping resource="persistencia/hibernate/mapeamentos/Quadra.hbm.xml"/>
    <mapping resource="persistencia/hibernate/mapeamentos/Agente.hbm.xml"/>
    <mapping resource="persistencia/hibernate/mapeamentos/Boletimtratamento.hbm.xml"/>
    <mapping resource="persistencia/hibernate/mapeamentos/Logradouro.hbm.xml"/>
    <mapping resource="persistencia/hibernate/mapeamentos/Permissao.hbm.xml"/>
    <mapping resource="persistencia/hibernate/mapeamentos/Visitatratamento.hbm.xml"/>
    <mapping resource="persistencia/hibernate/mapeamentos/Boletimpesquisa.hbm.xml"/>
    <mapping resource="persistencia/hibernate/mapeamentos/Localidade.hbm.xml"/>
    <mapping resource="persistencia/hibernate/mapeamentos/Transacaoagente.hbm.xml"/>
    <mapping resource="persistencia/hibernate/mapeamentos/Acessos.hbm.xml"/>
    <mapping resource="persistencia/hibernate/mapeamentos/Quadralogradouro.hbm.xml"/>
    <mapping resource="persistencia/hibernate/mapeamentos/Configuracao.hbm.xml"/>
  </session-factory>
</hibernate-configuration>
ABSTRACTDAO.JAVA

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package persistencia.dao;

import java.util.ArrayList;
import persistencia.session.hibernateSessionFactory;
import java.util.List;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JOptionPane;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.criterion.Projections;

/**
 *
 * @author Maycon Fernando Silva Brito @email mayconfsbrito@gmail.com
 */
public class AbstractDAO {

    protected static Session sessao;
    protected static Transaction transacao = null;

    /**
     * Inicializa a sessao para efetuar as transacoes no banco de dados
     */
    public static final void inicializaSessao() {
        sessao = hibernateSessionFactory.getSession();
    }

    /**
     * Fecha a sessao caso ela esteja aberta
     */
    public static boolean fechaSessao() {
        if (sessao.isOpen()) {
            sessao.flush();
            sessao.close();

            return true;
        }

        return false;
    }

    /**
     * Cadastra um unico objeto no banco de dados
     *
     * @param obj - Objetto a ser cadastrado
     * @return - @true se o objeto foi cadastrado, @false se o objeto nao foi
     * cadastrado
     */
    public static boolean cadastrar(Object obj) {
        try {

            inicializaSessao();
            transacao = sessao.beginTransaction();
            sessao.merge(obj);
            transacao.commit();

            return true;

        } catch (HibernateException er) {

            er.printStackTrace();
            transacao.rollback();

            return false;

        } finally {

            fechaSessao();

        }
    }

    /**
     * Altera um unico objeto no banco de dados
     *
     * @param obj - Objetto a ser alterado
     * @return - @true se o objeto foi alterado, @false se o objeto nao foi
     * alterado
     */
    public static boolean alterar(Object obj) {
        try {

            inicializaSessao();
            transacao = sessao.beginTransaction();
            sessao.merge(obj);
            transacao.commit();

            return true;

        } catch (HibernateException er) {

            er.printStackTrace();
            transacao.rollback();

            return false;

        } finally {
            fechaSessao();

        }
    }

    /**
     * Exclui um unico objeto no banco de dados
     *
     * @param obj - Objeto a ser excluido
     * @return - @true se o objeto foi excluido, @false se o objeto nao foi
     * excluido
     */
    public static boolean excluir(Object obj) {
        try {

            inicializaSessao();
            transacao = sessao.beginTransaction();
            sessao.delete(obj);
            transacao.commit();

            return true;

        } catch (HibernateException er) {

            er.printStackTrace();
            transacao.rollback();
            return false;

        } finally {
            fechaSessao();
        }
    }

    /**
     * Esclui um determinado objeto a partir unicamente do seu id
     *
     * @param nomeObjeto
     * @param nomeId
     * @param id
     * @return
     */
    public static boolean excluirPorId(String nomeObjeto, String nomeId, Integer id) {

        try {

            List obj = consultar(nomeObjeto, "" + nomeId + "=" + id);
            Object objeto = obj.get(0);
            if (!excluir(objeto)) {
                System.out.println("Não foi possível excluir o objeto " + nomeObjeto + " de Id " + id);
            }

            return true;

        } catch (IndexOutOfBoundsException er) {

            return true;

        } catch (Exception er) {

            er.printStackTrace();
            return false;

        }

    }

    /**
     * Consulta todos as instancias de um determinado objeto
     *
     * @param nomeObjeto - Nome dos objetos a serem consultados no banco de
     * dados
     * @return - Lista preenchida com as instancias do objeto no banco de dados,
     * @null caso exista alguma excecao
     */
    public static List listar(String nomeObjeto) {
        try {

            inicializaSessao();
            transacao = sessao.beginTransaction();
            List list = sessao.createQuery("from " + nomeObjeto).list();

            return list;

        } catch (HibernateException er) {

            er.printStackTrace();
            return null;

        } finally {

            fechaSessao();
        }
    }

    /**
     * Consulta um tipo de objetos do banco de dados a partir de uma determinada
     * condicao
     *
     * @param nomeObjeto - Nome dos objetos a serem consultados no banco de
     * dados
     * @param condicao - Condicao a ser satisfeita para realizar a consulta
     * @return - Lista preenchida com os objetos que satisfazem a condicao
     */
    public static List consultar(String nomeObjeto, String condicao) {

        try {
            inicializaSessao();
            transacao = sessao.beginTransaction();
            List list = sessao.createQuery("from " + nomeObjeto + " where " + condicao).list();

            return list;

        } catch (Exception er) {

            er.printStackTrace();
            return null;

        } finally {
            fechaSessao();
        }
    }

    /**
     * Realiza uma consulta puramente através do HQL
     *
     * @return
     */
    public static List consultar(String hql) {

        try {
            inicializaSessao();
            transacao = sessao.beginTransaction();
            List list = sessao.createQuery(hql).list();

            return list;

        } catch (Exception er) {

            er.printStackTrace();
            return null;

        } finally {
            fechaSessao();
        }
    }

    /**
     * Realiza uma listagem sem fechar a sessao
     *
     * @param nomeObjeto
     * @return
     */
    public static List listarSemFecharSessao(String nomeObjeto) {

        try {

            inicializaSessao();
            transacao = sessao.beginTransaction();
            List list = sessao.createQuery("from " + nomeObjeto).list();

            return list;

        } catch (Exception er) {

            er.printStackTrace();
            return null;

        }
    }

    /**
     * Realiza uma consulta sem fechar a sessao
     *
     * @param nomeObjeto
     * @param condicao
     * @return
     */
    public static List consultarSemFecharSessao(String nomeObjeto, String condicao) {

        try {
            
            List list = null;
            inicializaSessao();
            transacao = sessao.beginTransaction();
            if (nomeObjeto != null) {
                list = sessao.createQuery("from " + nomeObjeto + " where " + condicao).list();
            } else {
                list = sessao.createQuery(condicao).list();
            }

            return list;

        } catch (Exception er) {

            er.printStackTrace();
            return null;

        }
    }

    /**
     * Inicializa um jComboBox com todos os objetos de uma tabela ordenados
     */
    public static DefaultComboBoxModel inicializaComboBoxEntidades(String primeiraPosicao, String nomeTabela, String atributoOrdenado) {
        try {

            List<String> list = new ArrayList<String>();

            inicializaSessao();
            list = sessao.createQuery("select nome from " + nomeTabela + " order by " + atributoOrdenado).list();

            DefaultComboBoxModel model = new DefaultComboBoxModel();

            model.addElement(primeiraPosicao);
            for (int i = 0; i < list.size(); i++) {
                model.addElement(list.get(i));
            }

            return model;

        } catch (HibernateException er) {

            JOptionPane.showMessageDialog(null, "Não foi possível carregar o ComboBox.", "Erro", JOptionPane.ERROR_MESSAGE);
            er.printStackTrace();
            return null;

        } finally {
            fechaSessao();
        }
    }

    /**
     * Inicializa um jComboBox com todos os objetos de uma tabela ordenados
     */
    public static DefaultComboBoxModel inicializaComboBoxGenerico(String primeiraPosicao, String select, String nomeTabela, String atributoOrdenado) {
        try {

            List<String> list = new ArrayList<String>();

            inicializaSessao();
            list = sessao.createQuery("select " + select + " from " + nomeTabela + " order by " + atributoOrdenado).list();

            DefaultComboBoxModel model = new DefaultComboBoxModel();

            model.addElement(primeiraPosicao);
            for (int i = 0; i < list.size(); i++) {
                model.addElement(list.get(i));
            }

            return model;

        } catch (HibernateException er) {

            JOptionPane.showMessageDialog(null, "Não foi possível carregar o ComboBox.", "Erro", JOptionPane.ERROR_MESSAGE);
            er.printStackTrace();
            return null;

        } finally {
            fechaSessao();
        }
    }

    /**
     * Realiza a contagem de objetos na tabela
     *
     * @param nomeObjeto
     * @return
     */
    public static int count(Class classe) {
        try {
            inicializaSessao();
            Criteria criteria = sessao.createCriteria(classe);
            criteria.setProjection(Projections.rowCount());

            return ((Integer) criteria.list().get(0)).intValue();

        } catch (Exception er) {
            er.printStackTrace();
            transacao.rollback();
            return -1;

        } finally {

            sessao.flush();
            sessao.close();

        }
    }

    /**
     * Verifica qual o maior valor do atributo da classe no bd
     *
     * @param nomeObjeto
     * @return
     */
    public static int max(Class classe, String atributo) {
        try {
            inicializaSessao();
            Criteria criteria = sessao.createCriteria(classe);
            criteria.setProjection(Projections.max(atributo));

            if (((Integer) criteria.list().get(0)) != null) {
                return ((Integer) criteria.list().get(0)).intValue();
            } else {
                return 1;
            }



        } catch (Exception er) {
            er.printStackTrace();
            transacao.rollback();
            return -1;

        } finally {

            sessao.flush();
            sessao.close();

        }
    }

    public static Session getSession() {
        return sessao;
    }
}

velho, tu faz um list no teu banco e nao fecha a sessao? se tu terminou de fazer o que tinha que fazer pq tu mantém a sessao aberta lá? Por boas práticas, a session deve ser aberta e fechada após ter terminado o trabalho e não deixar aberta e ir para outra.
E outro detalhe se tu vai trazer 100 registros e querer exibir na página, seria bom quebrar e paginar o resultado. Veja tb se seu banco está como Lazy. Enfim, eu trabalho com hibernate há um bom tempo e ele nunca me deixou na mão com a performace, comparando custo x beneficio do framework.

Além do comentário do camarada acima, que está 101% correto, notei algumas coisas:

  • Desde quando é responsabilidade do DAO criar um JComboBox?
  • Está usando hibernate e possui um método só para obter o maior id das classes no bd, por quê?
  • Por que não usa singleton para gerenciar a sessão (se a sessão está aberta e ativa, use a mesma, senão, cria outra)?
  • Como estão feitos os mapeamentos dos relacionamentos entre as tabelas (se é que estes existem)?

Só para contextualizar, trabalho em um projeto onde o sistema que mantemos possui quase 10 anos, usa hibernate 2.alguma coisa e sua performance é incrível, muito mais rápida que muito sistema que usa JDBC por aí.
Lógico, o processo foi atualizado ao longo do tempo, as consultas otimizadas, mas, nunca tive problemas, nem com Oracle (atual BD com o qual trabalho), menos ainda com MySQL, ainda mais em tabelas com tão poucos registros (10, 100 mil).

Olá pessoal, muito obrigado pela ajuda.

1º - Primeiramente, eu tenho um método na classe DAO que lista a consulta sem fechar a sessão porque em alguns casos é necessário a utilização do relacionamento com o “Lazy=false”, como no exemplo dos métodos abaixo aonde eu tenho de listar informações de outros objetos relacionados com o objeto buscado.
No primeiro método eu realizo a consulta sem fechar a sessão e logo após no segundo método após listar os objetos e fecho a sessão. Mas mesmo assim seria uma prática errada? Tem uma prática melhor para fazer isto?


    protected void listarTodosElementosNaTabela() {

        List<Boletimtratamento> list = AbstractDAO.listarSemFecharSessao("Boletimtratamento");
        listarPesquisaNaTabela(list);
    }

 private void listarPesquisaNaTabela(List<Boletimtratamento> list) {

        frame.tabela.getColumnModel().getColumn(0).setMinWidth(30);
        frame.tabela.getColumnModel().getColumn(1).setMinWidth(60);
        frame.tabela.getColumnModel().getColumn(2).setMinWidth(200);
        frame.tabela.getColumnModel().getColumn(3).setMinWidth(300);
        frame.tabela.getColumnModel().getColumn(4).setMinWidth(200);

        DefaultTableModel modelo = (DefaultTableModel) frame.tabela.getModel();
        modelo.setNumRows(0);

        //Inicializa o DefaultTableCellRender modificado para personalizar algumas células
        DefaultTableCellRenderer renderer = new RendererTable(list);

        if (list != null) {
            try {
                for (int i = 0; i < list.size(); i++) {
                    Boletimtratamento boletim = list.get(i);
                    AbstractDAO.inicializaSessao();
                    modelo.addRow(new Object[]{boletim.getIdBoletimTratamento(), Datas.toString(boletim.getData()), boletim.getMunicipio().getNome(),
                                boletim.getLocalidade().getNome(), boletim.getAgente().getNome()});
                    AbstractDAO.fechaSessao();

                    //Altera as propriedades das células desta linha de acordo com o estado de conclusão da transação atual*
                    frame.tabela.getColumnModel().getColumn(0).setCellRenderer(renderer);
                    frame.tabela.getColumnModel().getColumn(1).setCellRenderer(renderer);
                    frame.tabela.getColumnModel().getColumn(2).setCellRenderer(renderer);
                    frame.tabela.getColumnModel().getColumn(3).setCellRenderer(renderer);
                    frame.tabela.getColumnModel().getColumn(4).setCellRenderer(renderer);
                }

            } catch (Exception er) {
                JOptionPane.showMessageDialog(frame, "Erro ao listar os objetos na tabela!\n\n" + er, "Erro na listagem!", JOptionPane.ERROR_MESSAGE);
                er.printStackTrace();
            }
        }

    }

2º - Em relação a paginar o resultado, eu faço com que o usuário realize apenas buscas no sistema (nunca listando tudo), então eu retorno somente o que ele busca, de qualquer forma essa idéia de paginar o resultado é muito boa, aliás, a paginação ocorre somente ao listar os objetos na gui, ou ocorre em relação a busca (buscando por exemplo apenas o primeiro grupo de 10 elementos). Mas o meu problema por enquanto é que a lentidão do sistema ocorre mesmo antes de eu realizar consultas, ou seja, até na tela de login o sistema fica lento (lembrando que isso só ocorre qd o bd está lotado).

3º - Sobre o preenchimento do DAO no ComboBox talvez eu tenha feito uma gambiarra? Mas é que de qualquer forma seria necessário ter algum método para preencher automaticamente os combobox, e como na maior parte este preenchimento é proveniente de alguma consulta no bd, eu resolvi colocar o método no DAO.

4º - Em algumas ocasiões eu preciso saber o último id cadastrado, ou saber o número de objetos cadastrados naquela tabela (count), como são métodos de consulta no BD e são abstratos (ou seja, genéricos, para qualquer objeto) resolvi criá-los no DAO mesmo.

5º - Vou implementar o Singleton para a sessão agora.

6º - Obviamente os relacionamentos entre as tabelas existem, você quer o relacionamento do mapeamento hibernate ou o relacionamento MySQL das tabelas? É só pedir que posto aqui.

Pessoal, vocês estão me ajudando bastante, muito obrigado mesmo. Por enquanto vou procurar por consultas em que não fechei a sessão (apesar de sempre estar fechando depois) e implementar o Singleton para a Session.

Pessoa, implementei o Singleton da maneira abaixo (um pouco diferente pois tive que adaptar) e o sistema deu uma leve melhorada, ou seja, agora ele só garra em partes que exigem consulta (não está garrando mais na tela de login por exemplo), mesmo que seja um consulta pequena que lista por exemplo 10 objetos apenas, e quando garra, não gasta mais minutos, mas apenas cerca de uns 30 segundos.

Vou começar a procurar por sessões que deixei em aberto em listagens e etc.

Mas a mensagem de cima é extremamente importante ainda, por isso se vocês puderem responder minhas dúvidas ficarei extremamente agradecido. Obrigado.

public static synchronized Session getSessao(){
        if(sessao != null && sessao.isOpen()) {
            fechaSessao();
            sessao = hibernateSessionFactory.getSession();
            
        } else {
            sessao = hibernateSessionFactory.getSession();
        }
        return sessao;
    }

/**
     * Inicializa a sessao para efetuar as transacoes no banco de dados
     */
    protected static final void inicializaSessao() {
        sessao = getSessao();
    }

    /**
     * Fecha a sessao caso ela esteja aberta
     */
    public static boolean fechaSessao() {
        if (sessao.isOpen()) {
            sessao.flush();
            sessao.close();

            return true;
        }

        return false;
    }

[quote=mayconfsbrito]Olá pessoal, muito obrigado pela ajuda.

1º - Primeiramente, eu tenho um método na classe DAO que lista a consulta sem fechar a sessão porque em alguns casos é necessário a utilização do relacionamento com o “Lazy=false”, como no exemplo dos métodos abaixo aonde eu tenho de listar informações de outros objetos relacionados com o objeto buscado.
No primeiro método eu realizo a consulta sem fechar a sessão e logo após no segundo método após listar os objetos e fecho a sessão. Mas mesmo assim seria uma prática errada? Tem uma prática melhor para fazer isto?
[/quote]
Talvez seja ideal analisar a possibilidade de implementar no projeto o pattern OpenSessionInView, para este caso.
(1° - Primeiramente, 2° - Segundamente…)

A questão é que você possui métodos de listar, isso nos leva a crer que a consulta pode estar sendo realizada considerando todos os resultados da tabela. Isso pode gerar lentidão.

Remova isso imediatamente. Começa-se com uma pequena gambiarra e quando se vê, está fazendo POGs (programação orientada a gambiarra).

A questão é que o hibernate consegue controlar os ids. Já o count, não tem jeito mesmo.

Talvez seja melhor considerar a sua possibilidade e, se possível, usar alguma ferramenta para pool de conexões (talvez o c3p0).

Até onde você disse, o problema é aplicação. Com os mapeamentos do hibernate, consigo deduzir como estão as tabelas. Do contrário, não.

[quote=mayconfsbrito]
Pessoal, vocês estão me ajudando bastante, muito obrigado mesmo. Por enquanto vou procurar por consultas em que não fechei a sessão (apesar de sempre estar fechando depois) e implementar o Singleton para a Session.[/quote]

Olá drsmachado,

1º - O Open Session In View também serve para aplicações desktop? É que só encontrei menções ao JSP e JSF, e pelo que entendi ele deixa a sessão aberta e só encerra após o envio da transação ao cliente. Além disso minha aplicação é desktop…
Mas talvez nem tenha necessidade de implementar esse padrão, pois após implementação do Singleton para a sessão do hibernate o sistema deu um melhorada considerável, agora ele está “garrando” somente quando eu seleciono algum elemento de algum JComboBox. O desempenho melhorou a ponto de eu preencher um ComboBox com 500 elementos buscados diretamente do bd sem nenhum lag (Eu sei que um comboBox não é o mais indicado para esta quantidade de elementos, mas este é um caso extremo, eu estou realizando testes no sistema para caso extremos, ou seja, com uma quantidade absurda de dados). Agora vou verificar o que está acontecendo quando seleciono algum elemento de JComboBox para estar “garrando” assim…

2º - Retirei a gambiarra do DAO e criei uma classe ComboBoxUtils que guarda este e outros métodos que me ajudam a gerenciar os JComboBox.

3º - Sobre o C3p0 encontrei bastante coisa importante e vi que ele é fundamental para uma boa aplicação com Hibernate, vou configurá-lo em breve, mas eu ainda não consegui entender direito o que um connection pool faz.
Inclusive deixo link da documentação do hibernate ensinando como configurar o C3p0:
https://community.jboss.org/wiki/HowToConfigureTheC3P0ConnectionPool

4º - O Mapeamento das tabelas no MySQL está de boa, o negócio realmente é a minha aplicação.

Mais uma vez muito obrigado.