Performance Hibernate

6 respostas
jtsato

Bom pessoal estou com um problema bem grande de performance. Para exemplificar o que acontece, criei umas classes de exemplo. Caso alguém se interesse forneço o projeto todo de exemplo. Vamos lá, tenho uma classe Professor e uma classe aluno. Um professor pode ter vários alunos. Simples não é :D. O que ocorre é o seguinte: Criei 5 Professores, os quais tem 100 alunos cada. Caso eu faça uma consulta de alunos sem fazer a consulta de professores ocorre o seguinte:

*** Um pedaço do ConsultarRegistros ***

        this.setSessionFactory(new Configuration().configure().buildSessionFactory());

        GenericoDAO genericoDAO = new GenericoDAO();
        genericoDAO.setSession(this.getSessionFactory().openSession());
        
        Collection colAlunos = genericoDAO.listarRegistros("from Aluno");
        for (Iterator iter = colAlunos.iterator(); iter.hasNext();) {
            Aluno aluno = (Aluno) iter.next();
            System.out.println(aluno.getId() + " - " + aluno.getNome());
        }


Hibernate: select aluno0_.id as id, aluno0_.nome as nome, aluno0_.professor_id as professo3_ from Alunos aluno0_
Hibernate: select professor0_.id as id0_, professor0_.nome as nome0_ from Professores professor0_ where professor0_.id=?
Hibernate: select professor0_.id as id0_, professor0_.nome as nome0_ from Professores professor0_ where professor0_.id=?
Hibernate: select professor0_.id as id0_, professor0_.nome as nome0_ from Professores professor0_ where professor0_.id=?
Hibernate: select professor0_.id as id0_, professor0_.nome as nome0_ from Professores professor0_ where professor0_.id=?
Hibernate: select professor0_.id as id0_, professor0_.nome as nome0_ from Professores professor0_ where professor0_.id=?

1 - Professor: 1 - Aluno: 1
...
100 - Professor: 1 - Aluno: 100
101 - Professor: 2 - Aluno: 1
...
200 - Professor: 2 - Aluno: 100
201 - Professor: 3 - Aluno: 1
...
300 - Professor: 3 - Aluno: 100
301 - Professor: 4 - Aluno: 1
...
400 - Professor: 4 - Aluno: 100
401 - Professor: 5 - Aluno: 1
...
500 - Professor: 5 - Aluno: 100

Ou seja o Hibernate faz um acesso pra cada professor diferente. Beleza se eu tiver somente 5. E agora se eu tiver 1000 ? Serão muitos acessos ao BD. E o nosso sistema ficará lento pra ***. Caso eu faça um listagem de Professores antes, o hibernate só faz 2 acessos. É isso. Por favor me ajudem :frowning:

*** Um pedaço do ConsultarRegistros ***

        this.setSessionFactory(new Configuration().configure().buildSessionFactory());

        GenericoDAO genericoDAO = new GenericoDAO();
        genericoDAO.setSession(this.getSessionFactory().openSession());
        
        Collection colProfessores = genericoDAO.listarRegistros("from Professor");
        for (Iterator iter = colProfessores.iterator(); iter.hasNext();) {
            Professor professor = (Professor) iter.next();
            System.out.println(professor.getId() + " - " + professor.getNome());
        }
        Collection colAlunos = genericoDAO.listarRegistros("from Aluno");
        for (Iterator iter = colAlunos.iterator(); iter.hasNext();) {
            Aluno aluno = (Aluno) iter.next();
            System.out.println(aluno.getId() + " - " + aluno.getNome());
        }


Hibernate: select professor0_.id as id, professor0_.nome as nome from Professores professor0_
1 - Professor 1
2 - Professor 2
3 - Professor 3
4 - Professor 4
5 - Professor 5

Hibernate: select aluno0_.id as id, aluno0_.nome as nome, aluno0_.professor_id as professo3_ from Alunos aluno0_

1 - Professor: 1 - Aluno: 1
...
100 - Professor: 1 - Aluno: 100
101 - Professor: 2 - Aluno: 1
...
200 - Professor: 2 - Aluno: 100
201 - Professor: 3 - Aluno: 1
...
300 - Professor: 3 - Aluno: 100
301 - Professor: 4 - Aluno: 1
...
400 - Professor: 4 - Aluno: 100
401 - Professor: 5 - Aluno: 1
...
500 - Professor: 5 - Aluno: 100

	*** Um pedaço do GenericoDAO ***

	/**
	 * Método responsável em listar registros
	 * @param hSQL
	 * @return Iterator
	 * @throws Exception
	 */
	public Collection listarRegistros(String hSQL) throws Exception {
        try {
    	    List listaRetorno = this.session.find(hSQL);
    	    if (listaRetorno.isEmpty()){ return new ArrayList(); }
            return listaRetorno; 
        } catch (ObjectNotFoundException e) {
            return new ArrayList();
        }
	}

Mapeamento do Professor

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 2.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping package="bean">
    <class name="bean.Professor" table="Professores" dynamic-update="true">
        <id name="id" column="id" type="int" unsaved-value="0">
            <generator class="increment"/>
        </id>
        <property name="nome" column="nome" type="java.lang.String"/>
    </class>
</hibernate-mapping>

Mapeamento do Aluno

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 2.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping package="bean">
    <class name="bean.Aluno" table="Alunos" dynamic-update="true">
        <id name="id" column="id" type="int" unsaved-value="0">
            <generator class="increment"/>
        </id>
        <property name="nome" column="nome" type="java.lang.String"/>
        <many-to-one name="professor" column="professor_id" class="bean.Professor"/>
    </class>
</hibernate-mapping>

Hibernate.properties

hibernate.connection.username=root
hibernate.connection.password=
hibernate.connection.url=jdbc:mysql://localhost:3306/alunos
hibernate.connection.driver_class=com.mysql.jdbc.Driver
hibernate.connection.pool_size=1
hibernate.dialect=net.sf.hibernate.dialect.MySQLDialect
hibernate.use_outer_join=true
Hibernate.cfg.xml

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 2.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-2.0.dtd">
<hibernate-configuration>

    <session-factory>
        <!-- properties -->
        <property name="show_sql">true</property>
        <!-- mapping files -->
        <mapping resource="mapeamento/bean/Aluno.hbm.xml"/>
        <mapping resource="mapeamento/bean/Professor.hbm.xml"/>
        <mapping resource="mapeamento/bean/Materia.hbm.xml"/>
        <mapping resource="mapeamento/bean/AlunoMateria.hbm.xml"/>
    </session-factory>

</hibernate-configuration>

6 Respostas

ranophoenix

Pq vc não faz um relacionamento bidirecional!? Vc insere um one-to-many no mapeamento da classe Professor e na classe Professor vc adiciona um método getAlunos()

louds

Algumas coisas. Não crie a SessionFactory toda vez, é um processo MUITO lento que deve ser feito somente uma vez.

De uma estudada no manual do hibernate, lá fala sobre lazy loading e eager loading, o segundo deve resolver seu problema de performance.

jtsato

Certo. Se eu fiz um mapeamento assim:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 2.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping package="bean">
    <class name="bean.Professor" table="Professores" dynamic-update="true">
        <id name="id" column="id" type="int" unsaved-value="0">
            <generator class="increment"/>
        </id>
        <property name="nome" column="nome" type="java.lang.String"/>
		<set name="professor_alunos" lazy="true" inverse="true" order-by="id">
			<key column="professor_id"/>
			<one-to-many class="bean.Professor"/>
		</set>        
    </class>
</hibernate-mapping>

Meu Código assim:

Collection colProfessores = genericoDAO.listarRegistros("from Professor");
        for (Iterator iter = colProfessores.iterator(); iter.hasNext();) {
            Professor professor = (Professor) iter.next();
            for (Iterator iterator = professor.getProfessor_alunos().iterator(); iterator.hasNext();) {
                Aluno aluno = (Aluno) iterator.next();
                System.out.println(aluno.getId() + " - " + aluno.getNome());
            }
        }

E saiu isso:

Hibernate: select professor0_.id as id, professor0_.nome as nome from Professores professor0_
Hibernate: select professor_0_.professor_id as professo3___, professor_0_.id as id__, professor_0_.id as id0_, professor_0_.nome as nome0_, professor_0_.professor_id as professo3_0_ from Alunos professor_0_ where professor_0_.professor_id=? order by professor_0_.id

1 - Professor: 1 - Aluno: 1
...
100 - Professor: 1 - Aluno: 100

Hibernate: select professor_0_.professor_id as professo3___, professor_0_.id as id__, professor_0_.id as id0_, professor_0_.nome as nome0_, professor_0_.professor_id as professo3_0_ from Alunos professor_0_ where professor_0_.professor_id=? order by professor_0_.id

101 - Professor: 2 - Aluno: 1
...
200 - Professor: 2 - Aluno: 100

Hibernate: select professor_0_.professor_id as professo3___, professor_0_.id as id__, professor_0_.id as id0_, professor_0_.nome as nome0_, professor_0_.professor_id as professo3_0_ from Alunos professor_0_ where professor_0_.professor_id=? order by professor_0_.id
201 - Professor: 3 - Aluno: 1
...
300 - Professor: 3 - Aluno: 100

Hibernate: select professor_0_.professor_id as professo3___, professor_0_.id as id__, professor_0_.id as id0_, professor_0_.nome as nome0_, professor_0_.professor_id as professo3_0_ from Alunos professor_0_ where professor_0_.professor_id=? order by professor_0_.id
301 - Professor: 4 - Aluno: 1
...
400 - Professor: 4 - Aluno: 100
Hibernate: select professor_0_.professor_id as professo3___, professor_0_.id as id__, professor_0_.id as id0_, professor_0_.nome as nome0_, professor_0_.professor_id as professo3_0_ from Alunos professor_0_ where professor_0_.professor_id=? order by professor_0_.id
401 - Professor: 5 - Aluno: 1
...
500 - Professor: 5 - Aluno: 100

Ou seja ele continuar fazendo todos os SQLs. Eu gostaria que ele fizesse apenas um. Tipo:

select * from alunos a left join professores p on (a.professor_id = p.id)

Eu posso fazer isso fazendo minha querie como acima. Porém o retorno do meu List não será uma collection de “Alunos” e sim um Array de Objetos que contém Alunos + Profesores. E terei que gastar mais processamento tirando os Alunos do Array. Estou em busca de algo no hibernate que faça isso pra mim. Tipow tentei usar outer-join=“true” e alterar o hibernate.max_fetch_depth=?. Mas não conheço bem essa parte de configurações avançadas.

Obrigado pelas Respostas.
:smiley:

jtsato

louds:
Algumas coisas. Não crie a SessionFactory toda vez, é um processo MUITO lento que deve ser feito somente uma vez.

De uma estudada no manual do hibernate, lá fala sobre lazy loading e eager loading, o segundo deve resolver seu problema de performance.

Valeu pela dica. Mas esse é só um código de exemplo. Na verdade o meu projeto roda no TOMCAT, e tenho um Listener para Carregar o SessionFactory somente quando o Tomcat é Carregado. :smiley:

louds

Olhe as opções de fetch da interface Query. Algo +/- assim:

Query q = session.createQuery("select Aluno join Professor");
q.setFetchMode("professor", ???.EAGER_LOAD);
jtsato

Pessoal encontrei a solução :smiley:

http://www.hibernate.org/118.html#A24

How can I avoid n+1 SQL SELECT queries when running a Hibernate query?
Follow the best practices guide! Ensure that all and mappings specify lazy=“true” in Hibernate2 (this is the new default in Hibernate3). Use HQL LEFT JOIN FETCH to specify which associations you need to be retrieved in the initial SQL SELECT.

Obrigado para quem tentou ajudar :stuck_out_tongue:

Criado 6 de maio de 2005
Ultima resposta 9 de mai. de 2005
Respostas 6
Participantes 3