Hibernate e Desempenho

Boa noite meu povo,

Quero levantar uma questao que já foi discutida em partes aqui no forum.

Hibernate é bom do ponto de vista do programador, pois em geral facilita a vida do programador. Por outro lado, do ponto de vista do usuario final, isso nao tem o menor sentido, porque usuario final nao ve codigo. Usuario final quer simplesmente que o programa execute as funcoes que ele deseja. Para ele nao importa em qual linguagem foi feita.

Fiz essa colocacao porque tenho um amigo que é arquiteto e ele trabalha em uma empresa de grande porte. Ele disse que o sistema lá em que eles utilizaram hibernate está todo sendo alterado para sql puro, porque aumentaria o desempenho fazendo as alteracoes.

Entao, hibernate é bom apenas para o programador? Em que ele é bom para o usuario final, que nem ve codigo?

Obrigado,

Cara, por traz o hibernate utiliza SQL. O Hibernate é apenas um framework de persistência, ou seja, ele alinha seu código ao seu banco de dados…
a unica coisa que ele faz é gerenciar seu banco de dados através de classes e anotações…
agora o mau uso do hibernate pode realmente causar este problema… é preciso gerenciar bem todos os recursos disponíveis pelo hibernate…
um bom estudo poderia automatizar muito um trabalho árduo e desnecessário de gerenciar consultas sql’s.
É o que eu penso… utilizei hibernate em um sistema de grande porte e ele se comportou normalmente…
vlw, abs

Vamos por partes :slight_smile: JDBC puro significa fazer ORM na mao. Ou seja, mapear classes Java para tabelas no banco de dados. Este eh o problema basico que o Hibernate resolve, entao, eu diria que em 99% das vezes a troca do Hibernate para “JDBC puro” por questoes de performance eh totalmente sem fundamento. Como “diversao”, olhe esta pagina e veja qual eh o custo estimado do “Hibernate”: http://www.ohloh.net/p/hibernate : $ 14,304,021 . Isso significa que se o Hibernate fosse desenvolvido dentro de uma empresa, ele custaria isso.

Implementar JDBC na mao, significa que vc tem que resolver estes problemas (que ja foram resolvidos pelo Hibernate):

  • O famoso problema N+1 (faco uma query que retorna muitos dados, ou vou diversas vezes ao banco de dados?). Em outras palavras, Lazy Loading.
  • Cache
  • Versionamento (e auditoria)
  • Quer usar outro banco de dados? Voce precisa de um esquema para abstrair, como os “Dialects”.
  • JDBC Batching.
  • Mapeamento de colecoes.
  • Custo de aprendizado para um novo desenvolvedor na equipe (usar um “padrao” significa que vc pode anunciar uma vaga pedindo JPA, e sabe que todos sabem como usar)
  • Testes. Ja olhou quantos testes existem no “test suite” do Hibernate?
  • Integracao com outros produtos (Seam, Spring, …)
  • Outras coisas que nem consigo pensar agora :slight_smile:

Se voce tem um problema de perfomance, eu diria com 90% de chances de acertar que o problema nao eh com o Hibernate. E mesmo que seja, voce pode sempre cair no “fall back” e rodar a query de forma manual (como Workers, talves). Ou ainda, pode implementar apenas a parte problematica usando JDBC direto.

Enfim, a decisao de se abrir mao do Hibernate (ou de qualquer outra implementacao JPA) tem que ser seguida de dados reais com testes reais de performance, onde se evidencia o problema no produto. E diria que se vc realmente achou um problema de performance, pode abrir um JIRA que alguem vai dar uma olhada (prometo!).

Olá pessoal,
me desculpem comentar um post antigo, mas estou enfrentando um problema de performance usando hibernate.
É muito provável que seja a falta de experiência com o framework.

Recursos da aplicação:
Hibernate, partes com Spring, e JSF e Primefaces, banco postgresql

Gostaria de citar meu problema:

Estou montando uma aplicação onde registro algumas entrevistas internas, então tenho dados do funcionário, entrevistador e das questões da entrevista.
De imediato percebe-se que podemos ter 3 tabelas, entretanto o funcionário e o entrevistador fazem parte da mesma tabela funcionário. Então temos 2 tabelas no banco - Funcionário, Entrevistas.

Só tem um detalhe, teremos ainda as tabelas de Datas Salariais, Cargo, Função e Área todas essas tabelas possuem relações com o funcionário.

Tenho uma página de consulta onde será listada todas as entrevistas, hoje temos 400 entrevistas como carga inicial e é nessa página de consulta que há problema de performance.
Eu uso nessa página um datatable. É exatamente o mesmo que está nas demonstrações do primefaces:
http://www.primefaces.org/showcase/ui/datatableComplex.jsf

Eu também adicionei um <p:ajax event=“rowSelect” />, pois as informações mais detalhadas será mostrada em um dialog. Por isso preciso de todos os dados.

Abaixo segue as classes que montei, sei que temos um recurso chamado LAZY e EAGER para consultas @OneToMany, estou usando EAGER pois terei todas as informações de uma só vez, se eu utilizasse o LAZY provavelmente durante a renderização dos dados ele faria todas as outras consultas do mesmo jeito.

Acessando pela internet levou ~1 minuto e 30 segundos para apresentar as 400 entrevistas, pense no usuário clicando no menu de consulta e a página fica ali carregando, eu mesmo não aguentei o tempo que está levando. Já usando a rede local levou ~10 segundos. Mas esses números (400 conforme mencionado acima) tendem a dobrar a cada 3 ou 4 meses.

Minhas questões:

1- Poderiam sugerir um forma melhor de performance ?
2- Quanto ao uso do LAZY, o básico dele eu sei como funciona, mas eu costumo fechar a conexão logo após a query usando session.getTransaction().commit(); e session.close(); então recebo um lazy initialization exception, mas não entendi direito como usar o Open Session in View.
3- Para essa aplicação, o hibernate realmente pode me ajudar, não é ?

Se ficou alguma dúvida na minha explicação é só comentar, desde já agradeço.

Montei as associações mais ou menos assim (possuem pequenas alterações das originais, pois ainda há outros relacionamento e mais colunas):


// FUNCIONARIO

@Service
@Entity
@Table
@SequenceGenerator(name = "pk_sequence_funcionario", sequenceName = "funcionario_id_seq")
public class Funcionario implements Serializable {

	private static final long serialVersionUID = 253282986994912255L;

	@Id
	@Column
	@GeneratedValue(strategy = GenerationType.AUTO, generator = "pk_sequence_funcionario")
	private Integer id;

	@Column
	@OrderBy(clause = "nome")
	private String nome;

        @Column
	private String email;

        @OneToOne
	@JoinColumn(name = "id_cargo")
	private Cargo cargo;

	@OneToOne
	@JoinColumn(name = "id_area")
	private Area area;

	@OneToMany(mappedBy = "funcionario", targetEntity = Salario.class, fetch = FetchType.EAGER)
	@OrderBy(clause = "dataSalario")
	private List<Salario> listaSalarios;

	@OneToMany(mappedBy = "funcionario", targetEntity = Entrevista.class, fetch = FetchType.EAGER)
	@OrderBy(clause = "dataEntrevista ASC")
	private Set<Entrevista> listaEntrevista;

        //  temos ainda mais 4 colunas aqui, omiti para facilitar a leitura

        // getters setters
}

// SALARIO

@Service
@Entity
@Table
@SequenceGenerator(name = "pk_sequence_salario", sequenceName = "salario_id_seq")
public class Salario implements Serializable{

	private static final long serialVersionUID = -660215567623424277L;

	@Id
	@Column
	@GeneratedValue(strategy = GenerationType.AUTO, generator = "pk_sequence_salario")
	private Integer id;
	
	@Column(name="data_entrada")
	private Date dataSalario;
	
	@ManyToOne
	@JoinColumn(name="id_funcionario")
	private Funcionario funcionario;

        // getters setters

}

// AREA

@Service
@Entity
@Table
@SequenceGenerator(name = "pk_sequence_area", sequenceName = "area_id_seq")
public class Area implements Serializable {

	private static final long serialVersionUID = -172491528235032442L;

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO, generator = "pk_sequence_area")
	@Column
	private Integer id;

	@Column
	private String nome;

        // getters setters

}

// CARGO

@Service
@Entity
@Table
@SequenceGenerator(name = "pk_sequence_area", sequenceName = "area_id_seq")
public class Area implements Serializable {

	private static final long serialVersionUID = -172491528235032442L;

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO, generator = "pk_sequence_area")
	@Column
	private Integer id;

	@Column
	private String nome;

        // getters setters

}

// ENTREVISTA

@Service
@Entity
@Table
@SequenceGenerator(name = "pk_sequence_entrevista", sequenceName = "entrevista_id_seq")
public class Entrevista implements Serializable {

	private static final long serialVersionUID = -4555908627091964524L;

	@Id
	@Column
	@GeneratedValue(strategy = GenerationType.AUTO, generator = "pk_sequence_entrevista")
	private Integer id;

	@ManyToOne(cascade = CascadeType.ALL)
	@JoinColumn(name = "id_funcionario")
	private Funcionario funcionario;

	@ManyToOne
	@JoinColumn(name = "id_entrevistador")
	private Funcionario entrevistador;

	@Column(name = "data_entrevista")
	private Date dataEntrevista;

        @Column(name = "tecnologia_plataforma")
	private String tecnologiaPlataforma;

	@Column(name = "sistema_conhecimento")
	private String sistemaConhecidos;

	@Column(name = "dados_pessoais")
	private String dadosPessoais;

        // temos ainda mais 10 colunas aqui, omiti para facilitar a leitura

        // getters setters

}

No geral sobre performance do Hibernate, consultas cabeludas sempre uso SQL nativo (named SQL). Consultas simples ou simples tendendo à média uso Criteria sobrescrevendo programaticamente os lazys para eager, além de fazer os joins, afim de gerar um único SQL para atender todas as informações que necessito para a consulta ou relatório específico.

Interessante para você ler: http://blog.caelum.com.br/os-7-habitos-dos-desenvolvedores-hibernate-e-jpa-altamente-eficazes/

Obrigado javaflex,

vou dar uma olhada no link.
Como precisei terminar logo a aplicação resolvi misturar hibernate com spring jdbc. Assim que sobrar um tempo pretendo voltar a realizar testes de performance (ou melhor estudar) e logo que possível eu volto perguntar ou até mesmo mostrar algum tipo de solução.

Abraços