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

Hibernate Efetivo - Erros Comuns e Soluções

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)

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();