Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Hibernate Efetivo - Erros Comuns e Soluções

Avatar for Rafael Ponte Rafael Ponte
December 28, 2017

Hibernate Efetivo - Erros Comuns e Soluções

Mesmo anos após o lançamento do Hibernate ainda é fácil encontrar projetos utilizando o framework de maneira ineficiente, podendo leva-lo a problemas sérios de performance ou até inviabilizar a aplicação.

O uso não efetivo do Hibernate está intimamente ligado a erros comuns e más práticas em sua utilização, que vão desde o pool de conexões, select n+1, configuração de cache, batch-size até o uso indevido do cache level 1 em processamentos batch e o tratamento de LazyInitializationException.

Nesta palestra você vai aprender dicas importantes de como melhorar a performance da sua camada de persistência e melhorar a escalabilidade da sua aplicação.

(Apesar dos slides estarem atualizados para 2017, essa talk foi criada e apresentada primeiramente no evento QCONSP 2012)

Avatar for Rafael Ponte

Rafael Ponte

December 28, 2017
Tweet

More Decks by Rafael Ponte

Other Decks in Technology

Transcript

  1. Eu estava me perguntando quando de fato o Hibernate foi

    criado... Luca Bastos O “Boom” foi em 2003!
  2. um SGDB mal configurado pode ser o problema... MAS na

    maioria das vezes o problema está na SUA APLICAÇÃO
  3. ???

  4. mas nem todos dão a devida atenção... até perceberem a

    app engasgando ou até receberem um org.hibernate.exception.Generic JDBCException: Cannot open connection
  5. daí percebem que não configuraram o o pool do Hibernate

    
 hibernate.connection.driver_class=org.postgresql.Driver hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect hibernate.connection.url=jdbc:postgresql://localhost:5432/myapp hibernate.connection.username=postgres hibernate.connection.password=1234 hibernate.properties
  6. daí percebem que não configuraram o o pool do Hibernate

    
 hibernate.connection.driver_class=org.postgresql.Driver hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect hibernate.connection.url=jdbc:postgresql://localhost:5432/myapp hibernate.connection.username=postgres hibernate.connection.password=1234
 hibernate.connection.pool_size=30 hibernate.properties
  7. daí percebem que não configuraram o o pool do Hibernate

    
 hibernate.connection.driver_class=org.postgresql.Driver hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect hibernate.connection.url=jdbc:postgresql://localhost:5432/myapp hibernate.connection.username=postgres hibernate.connection.password=1234
 hibernate.connection.pool_size=30 hibernate.properties
  8. INFO DriverManagerConnectionProvider:64 - Using Hibernate built-in connection pool (not for

    production use!) INFO DriverManagerConnectionProvider:65 - Hibernate connection pool size: 20
  9. INFO DriverManagerConnectionProvider:64 - Using Hibernate built-in connection pool (not for

    production use!) INFO DriverManagerConnectionProvider:65 - Hibernate connection pool size: 20 not for production use!
  10. <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <!-- access configuration --> <property name="driverClass"

    value="${jdbc.driverclass}" /> <property name="jdbcUrl" value="${jdbc.url}" /> <property name="user" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> <!-- pool sizing --> <property name="initialPoolSize" value="3" /> <property name="minPoolSize" value="6" /> <property name="maxPoolSize" value="25" /> <property name="acquireIncrement" value="3" /> <property name="maxStatements" value="0" /> <!-- retries --> <property name="acquireRetryAttempts" value="30" /> <property name="acquireRetryDelay" value="1000" /> <!-- 1s --> <property name="breakAfterAcquireFailure" value="false" /> <!-- refreshing connections --> <property name="maxIdleTime" value="180" /> <!-- 3min --> <property name="maxConnectionAge" value="10" /> <!-- 1h --> <!-- timeouts e testing --> <property name="checkoutTimeout" value="5000" /> <!-- 5s --> <property name="idleConnectionTestPeriod" value="60" /> <!-- 60 --> <property name="testConnectionOnCheckout" value="true" /> <property name="preferredTestQuery" value="SELECT 1+1" /> </bean> podemos obter as conexões de um DataSource
  11. select nf.* from NotaFiscal nf where nf.id=42 select i.* from

    Item i where i.nota_fiscal_id=42 Hibernate executa 2 selects NotaFiscal nf = (NotaFiscal) session.load(NotaFiscal.class, 42); List<Item> itens = nf.getItens();
  12. Session session = sessionFactory.openSession(); NotaFiscal nf = (NotaFiscal) session.load(NotaFiscal.class, 42);

    
 session.close(); List<Item> itens = nf.getItens();
 System.out.println("numero de pedidos:" + itens.size()); a session do Hibernate foi fechada
  13. Session session = sessionFactory.openSession(); NotaFiscal nf = (NotaFiscal) session.load(NotaFiscal.class, 42);

    
 session.close(); List<Item> itens = nf.getItens();
 System.out.println("numero de pedidos:" + itens.size()); mas ao ler os itens da nota org.hibernate.LazyInitializationException: failed to lazily initialize a collection -
 no session or session was closed.
  14. Session session = sessionFactory.openSession(); NotaFiscal nf = (NotaFiscal) session.load(NotaFiscal.class, 42);

    List<Item> itens = nf.getItens();
 System.out.println("numero de pedidos:" + itens.size());
 
 session.close(); fechar a session ao término do trabalho
  15. @Get("/notas/{id}") public void view(Long id) {
 NotaFiscal nf = notaFiscalDao.carrega(id);

    result.include("nf", nf);
 result.forwardTo("/notas/view.jsp"); } view.jsp NotaFiscalController.java <c:forEach var="item" items="${nf.itens}"> ${item.produto.descricao}<br/> </c:forEach>
  16. @Get("/notas/{id}") public void view(Long id) {
 NotaFiscal nf = notaFiscalDao.carrega(id);

    result.include("nf", nf);
 result.forwardTo("/notas/view.jsp"); } view.jsp NotaFiscalController.java <c:forEach var="item" items="${nf.itens}"> ${item.produto.descricao}<br/> </c:forEach> session foi fechada!
  17. @Get("/notas/{id}") public void view(Long id) {
 NotaFiscal nf = notaFiscalDao.carrega(id);

    result.include("nf", nf);
 result.forwardTo("/notas/view.jsp"); } view.jsp NotaFiscalController.java <c:forEach var="item" items="${nf.itens}"> ${item.produto.descricao}<br/> </c:forEach> LazyInitializationException
  18. select nf.*, i.* from NotaFiscal nf left outer join Item

    i on nf.id = i.nota_fiscal_id
 where nf.id=42 Hibernate executa 1 select NotaFiscal nf = (NotaFiscal) session.load(NotaFiscal.class, 42);
  19. mas isso poderia gerar uma sobrecarga... pois os itens da

    nota não são necessários em muitos lugares
  20. @WebFilter(urlPatterns="/*") public class OpenSessionInViewFilter implements Filter { SessionFactory sessionFactory; @Override

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) { Transaction transaction = null; try { Session session = sessionFactory.getCurrentSession(); transaction = session.beginTransaction(); chain.doFilter(req, res); transaction.commit(); } finally { if (transaction != null && transaction.isActive()) { transaction.rollback(); } session.close(); } } } Servlet Filter
  21. @Entity @Cache(usage=CacheConcurrencyStrategy.READ_WRITE) class Bug { @Id private Long id; private

    String descricao; private String status; @ManyToOne private Projeto projeto; @OneToMany private List<Comentario> comentarios; } #2 configuramos as entidades
  22. 2nd Level Cache não faz cache das instancias das entidades

    somente dos valores das propriedades
  23. @Entity @Cache(usage=CacheConcurrencyStrategy.READ_WRITE) class Bug { @Id private Long id; private

    String descricao; private String status; @ManyToOne private Projeto projeto; @OneToMany private List<Comentario> comentarios; }
  24. modelo conceitual do cache Bug Data Cache 17 -> [

    “Bug #1”, “ABERTA” , 1 ] 18 -> [ “Bug #2”, “FECHADA”, 2 ] 19 -> [ “Bug #3”, “ABERTA” , 1 ]
  25. modelo conceitual do cache Bug Data Cache 17 -> [

    “Bug #1”, “ABERTA” , 1 ] 18 -> [ “Bug #2”, “FECHADA”, 2 ] 19 -> [ “Bug #3”, “ABERTA” , 1 ] id descricao status id do projeto
  26. modelo conceitual do cache Bug Data Cache 17 -> [

    “Bug #1”, “ABERTA” , 1 ] 18 -> [ “Bug #2”, “FECHADA”, 2 ] 19 -> [ “Bug #3”, “ABERTA” , 1 ] não é uma árvore de objetos, mas sim um Map de Arrays
  27. @Entity @Cache(usage=CacheConcurrencyStrategy.READ_WRITE) class Bug { @Id private Long id; private

    String descricao; private String status; @ManyToOne private Projeto projeto; @OneToMany private List<Comentario> comentarios; }
  28. modelo conceitual do cache Bug Data Cache 17 -> [

    “Bug #1”, “ABERTA” , 1, ??? ] 18 -> [ “Bug #2”, “FECHADA”, 2, ??? ] 19 -> [ “Bug #3”, “ABERTA” , 1, ??? ] comentarios??
  29. @Entity @Cache(usage=CacheConcurrencyStrategy.READ_WRITE) class Bug { @Id private Long id; private

    String descricao; private String status; @ManyToOne private Projeto projeto; @OneToMany @Cache(usage=CacheConcurrencyStrategy.READ_WRITE) private List<Comentario> comentarios; }
  30. modelo conceitual do cache Bug Data Cache 17 -> [

    “Bug #1”, “ABERTA” , 1, [1,2] ] 18 -> [ “Bug #2”, “FECHADA”, 2, [] ] 19 -> [ “Bug #3”, “ABERTA” , 1, [3] ]
  31. modelo conceitual do cache Bug Data Cache 17 -> [

    “Bug #1”, “ABERTA” , 1, [1,2] ] 18 -> [ “Bug #2”, “FECHADA”, 2, [] ] 19 -> [ “Bug #3”, “ABERTA” , 1, [3] ] ids dos comentarios
  32. E o que o Hibernate faz com todos estes IDs?

    vai no banco de novo! a não ser que você…
  33. Com 2nd Level Cache tudo funciona bem enquanto buscamos por

    ID... ...mas e quando precisamos de uma consulta um pouco diferente? session.load(Bug.class, 17); session
 .createQuery("from Bug where status = ?") .setString(0,"ABERTO")
 .list();
  34. modelo conceitual do query cache Query Cache [“from Bug where

    status = ?”, [“ABERTO”]] -> [17, 19]
  35. modelo conceitual do query cache Query Cache [“from Bug where

    status = ?”, [“ABERTO”]] -> [17, 19] Query + Parâmetros IDs
  36. select nf.* from NotaFiscal nf where nf.id=42 select i.* from

    Item i where i.nota_fiscal_id=42 Hibernate executa 2 selects NotaFiscal nf = (NotaFiscal) session.load(NotaFiscal.class, 42); processaItensDaNota(nf);
  37. List<NotaFiscal> notas = dao.listaTudo(); for (NotaFiscal nf : notas) {

    processaItensDaNota(nf); } Processando os itens de varias notas
  38. List<NotaFiscal> notas = dao.listaTudo(); for (NotaFiscal nf : notas) {

    processaItensDaNota(nf); } Processando os itens de varias notas
  39. select nf.* from NotaFiscal nf select i.* from Item i

    where i.nota_fiscal_id=? select i.* from Item i where i.nota_fiscal_id=? select i.* from Item i where i.nota_fiscal_id=? select i.* from Item i where i.nota_fiscal_id=? select i.* from Item i where i.nota_fiscal_id=? ... Hibernate executa n+1 selects List<NotaFiscal> notas = dao.listaTudo(); for (NotaFiscal nf : notas) { processaItensDaNota(nf); }
  40. select nf.*, i.* from NotaFiscal nf left outer join Item

    i on nf.id = i.nota_fiscal_id
 Hibernate executa 1 select List<NotaFiscal> notas = dao.listaTudo();
  41. select nf.* from NotaFiscal nf select i.* from Item i

    where i.nota_fiscal_id in (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) select i.* from Item i where i.nota_fiscal_id in (?, ?, ?, ?, ?) Hibernate executa n/10+1 selects List<NotaFiscal> notas = dao.listaTudo(); for (NotaFiscal nf : notas) { processaItensDaNota(nf); }
  42. select nf.* from NotaFiscal nf select i.* from Item i

    where i.nota_fiscal_id in (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) select i.* from Item i where i.nota_fiscal_id in (?, ?, ?, ?, ?) Hibernate executa n/10+1 selects List<NotaFiscal> notas = dao.listaTudo(); for (NotaFiscal nf : notas) { processaItensDaNota(nf); }
  43. select nf.* from NotaFiscal nf select i.* from Item i

    where i.nota_fiscal_id in (
 select nf.id from NotaFiscal nf ) Hibernate executa 2 selects List<NotaFiscal> notas = dao.listaTudo(); for (NotaFiscal nf : notas) { processaItensDaNota(nf); }
  44. select nf.* from NotaFiscal nf select i.* from Item i

    where i.nota_fiscal_id in (
 select nf.id from NotaFiscal nf ) Hibernate executa 2 selects List<NotaFiscal> notas = dao.listaTudo(); for (NotaFiscal nf : notas) { processaItensDaNota(nf); }
  45. Session session = sf.openSession(); Transaction tx = session.beginTransaction(); for (

    int i=0; i < 100000; i++ ) { Produto produto = new Produto(...); session.save(produto); } tx.commit(); session.close();
  46. Session session = sf.openSession(); Transaction tx = session.beginTransaction(); for (

    int i=0; i < 100000; i++ ) { Produto produto = new Produto(...); session.save(produto); if (i % 100 == 0) { session.flush(); session.clear(); } } tx.commit(); session.close();
  47. StatelessSession sem 1st Level Cache sem 2nd Level Cache sem

    dirty-checking sem cascade Collections são ignorados sem modelo de eventos sem interceptors próxima ao jdbc API mais baixo nível mapeamento básico
  48. StatelessSession session = sf.openStatelessSession(); Transaction tx = session.beginTransaction(); for (

    int i=0; i < 100000; i++ ) { Produto produto = new Produto(...); session.insert(produto); } tx.commit(); session.close();