Hibernate, o assunto era Reinventar a roda, mas isso não tem mais importância

Esse termo tão utilizado aqui e nos diversos fóruns e blogs do mundo não tá um pouco ultrapassado? Estou iniciando uma discussão pra tentar entender pq se fala tanto nisso. Temos zilhões de projetos voltados para o mesmo objetivo, o de abstrair partes repetitivas e a complexidade de determinados pontos de uma aplicação, e oque eles geram? Mais partes repetitivas, e mais complexidade. É comum ver pessoas que usam JDBC por acharem Hibernate complexo demais, assim como vão dizer “-mas o Hibernate não é complexo…”. Será que tornar algo genérico demais não torna as coisas mais complexas? Vejo projetos como o próprio JForum desenvolvido sem a utilização de frameworks (tudo bem ele em sim tem uma camada de abstração) mas é um projeto tão bem feito e ao mesmo tempo tão simples. Sinceramente, quando vejo um projeto que tem struts.xml (ou vários), applicationContext.xml (ou vários), XXX.hbm.xml (vários e vários), log4.properties, entre dezenas de outros possíveis… frameworks me boto a pensar, isso é java?? E eu pergunto JDBC+cache não é mais rápido que Hibernate? Pq aquelas pessoas que trabalham em uma empresa que usa Oracle vai querer usar Hibernate em um sistema que jamais vai mudar de banco? E cada vez mais as pessoas abarrotam os frameworks de funcionalidades deixando-os cada vez mais complexos, quem viu as configurações do ww1 e vê as do struts2 entende oque eu estou falando…

Ahh sei lá, acordei com isso na cabeça, acho que estou meio EMO hj… auhdaudha

Abstrair não é re-inventar a roda. Veja o exemplo abaixo que eu descrevo em http://book.mentaframework.org/posts/list/5.page

Contando caracteres com Ruby

file = File.open(ARGV[0])

count = 0;

file.each { |line| count += line.chomp.length }

puts "Number of chars: #{count}"

Contando caracteres com Java

import java.io.*;

public class CountChars {

    public static void main(String[] args) {
 
        try {       
            
            FileInputStream fos = new FileInputStream(args[0]);
            
            BufferedReader in = new BufferedReader(new InputStreamReader(fos));
            
            String line;
            
            int count = 0;
            
            while((line = in.readLine()) != null) {
                
                count += line.length();
                
            }
            
            System.out.println("Number of chars: " + count);
            
        } catch(IOException e) {
            
            System.out.println("An error happened: " + e.getMessage());
            
        }
    }
}

Se contar caracteres for uma operação que vc executa sempre e a cada novo projeto, então merece uma bela abstração:

import java.io.*;

public class CountChars {

    public static void main(String[] args) {
 
        int count = FileUtils.countChars(args[0]);
        
        System.out.println("Number of chars: " + count);
 
    }
}

Isso se chama abstrair para facilitar a vida. Se vc quiser fazer tudo na unha para não ter que aprender o framework e o seu FileUtils.count, então tudo bem, mas aí sim vc vai perder tempo reinventando a roda.

Outro exemplo gritante. Todo projeto precisa de um pool de conexões. Então veja como vc configura um pool de conexões no tomcat:

<Context path="/dbcp" docBase="dbcp" debug="5"
reloadable="true" crossContext="true">

<Resource name="jdbc/TestDB" auth="Container"
   type="javax.sql.DataSource" removeAbandoned="true"
   removeAbandonedTimeout="30" maxActive="100"
   maxIdle="30" maxWait="10000" username="kunal"
   password="java_facier"
   driverClassName="com.mysql.jdbc.Driver"
   url="jdbc:mysql://localhost/dbcptest"/>

</Context>

<listener>
        <listener-class> com.onjava.dbcp.DBCPoolingListener</listener-class>
</listener>

<!-- This component has a dependency on an external resource-->
 <resource-ref>
      <description> DB Connection Pooling</description>
      <res-ref-name> jdbc/TestDB</res-ref-name>
      <res-type> javax.sql.DataSource</res-type>
      <res-auth> Container</res-auth>
  </resource-ref>

<servlet>
        <servlet-name> EnrolledStudents</servlet-name>
        <servlet-class> com.onjava.dbcp.CourseEnrollmentServlet</servlet-class>
        <load-on-startup> 1</load-on-startup>
</servlet>

<servlet-mapping>
        <servlet-name> EnrolledStudents</servlet-name>
        <url-pattern> /enrollment.do</url-pattern>
</servlet-mapping>

public class DBCPoolingListener implements
ServletContextListener{
 public void contextInitialized
  (ServletContextEvent sce){

  try {
    // Obtain our environment naming context
    Context envCtx = (Context) new InitialContext().
    lookup("java:comp/env");

    // Look up our data source
    DataSource  ds = (DataSource) envCtx.lookup
       ("jdbc/TestDB");

    sce.getServletContext().setAttribute
      ("DBCPool", ds);
   } catch(NamingException e){ e.printStackTrace();
  }
 }
 public void contextDestroyed(ServletContextEvent
 sce){
 }
}

public void init() throws ServletException {
    try {
 //Create a datasource for pooled connections.
 datasource = (DataSource) getServletContext().
 getAttribute("DBCPool");

  //Register the driver for non-pooled connections.
  Class.forName("com.mysql.jdbc.Driver").
      newInstance();
    }
private synchronized Connection getConnection
    (boolean pooledConnection)
    throws SQLException {
  if (pooledConnection) {
     pooledCount++;

    // Allocate and use a connection from the pool
    return datasource.getConnection();
  }
  else {

    nonPooledCount++;
    Connection con = DriverManager.getConnection(
      "jdbc:mysql://localhost/dbcptest","kunal",
      "java_facier");
    return con;   //return a newly created object
    }
  } catch (Exception e) {
      throw new ServletException(e.getMessage());
    }
  }

Fonte: http://www.onjava.com/pub/a/onjava/2006/04/19/database-connection-pooling-with-tomcat.html

Isso é sem dúvida uma aberração. Uma Rube Goldberg machine.

Veja como vc faz um pool de conexões com o Mentawai:

public class ApplicationManager extends org.mentawai.core.ApplicationManager {
    
    private ConnectionHandler connHandler = null;
    
    public void init(Context application) {
    
        // assuming you have a mysql database with a "lohis" database with username "lohis" and password "lohis"
        this.connHandler = new DBCPConnectionHandler("com.mysql.jdbc.Driver", 
                                                     "jdbc:mysql://localhost/lohis?autoReconnect=true", 
                                                     "lohis", 
                                                     "lohis");

        filter(new ConnectionFilter("conn", connHandler));
    }
}       

public String execute() throws Exception {
        
    Connection conn = (Connection) input.getValue("conn");

    // do what you have to do with the connection... no need to return the connection to pool...

    return SUCCESS;
}

Então. Vc acha que isso é reinventar a roda?

Não Saoj, eu sou a favor de tudo isso, oque eu sou contra é gerar trezentas configurações para tentar abstrair uma coisa e torná-la mais complexa, um exemplo é o Hibernate ou o Spring, diga-se de passagem, são excelentes e fazem bem oque propõem, mas depende de muita configuração e não seguem nenhuma especificação entende?

Assim como no mtw, o guice (juice) do google faz praticamente o mesmo que o spring faz (estou falando apenas de DI) e é extremamente mais simples…

Concordo plenamente com vc. Ninguém duvida do poder e da qualidade desses frameworks, mas vc precisa saber tantos detalhes na hora de usar e configurar que intimida um pouco.

O que mais tem por aí é gente usando Spring para fazer uma simples injection. Por isso que surgiu o Guice (e agora o Miocc). Repare que o Guice utiliza configuração programática para configurar os componentes. Está bastante claro pra mim que [color=blue]se a configuração virou uma complexidade a mais, ela tem que ser feita programaticamente para poder ser abstraída e simplificada[/color]. E algumas pessoas continuam pensando erradamente que as configurações podem ser resolvidas com conventions. Conventions amenizam o problema mas não resolvem porque para certas coisas não há como escapar da configuração. Dá para escapar da configuração do pool de conexões ??? Dá para escapar da configuração de IoC e DI ??? Dá para escapar da configuração de ORM ??? E não estou contando os casos que vc quer escapar da convenção, como por exemplo na camada view. Ficar limitado o tempo todo a /User.add.mtw vai para /User/add.jsp é extremamente impraticável num projeto sério.

Eu até hoje uso JDBC com uma ferramenta simples que abstrai as queries mais simples de CRUD pra mim. Um dia irei usar/aprender Hibernate, mas infelizmente pra mim esse momento ainda não chegou, por falta de necessidade e vontade mesmo.

Exemplo extraído do site do Hibernate.

private List listEvents() { Session session = HibernateUtil.getSessionFactory().getCurrentSession(); session.beginTransaction(); List result = session.createQuery("from Event").list(); session.getTransaction().commit(); return result; }

Exemplo de como ficaria a mesma coisa em JDBC puro.

private List listEvents() { Connection con = getConnection(); Statement stmt = con.createStatement(); ResultSet rs = s.executeQuery("select * from event"); try { return getAsList(rs); } finally { rs.close(); stmt.close(); } }

Além é claro de não ter que aprender uma HQL por exemplo, é muito mais legível. Oque precisa de abstração aqui?? Na minha opinoão o método getAsList que pode usar um mapper para fazer o bind, mas só isso! Não precisa reescrever o mundo inteiro e ainda deixar a mesma coisa.

Tá tá tá, o Hibernate não é só isso, mas como dizia meu pai, um erro não justifica o outro.

Saoj sou completamente a favor da opinião de as configurações devem ser programáticas, mas tambem não sou contra os xmls, sou contra o mal uso deles.

é muito mais fácil vc fazer um

bind(MyObj.class).in(Scope.singleton)

do quê:

mas será que ninguem tá vendo isso??

Quando vc vai a um restaurante e já na entrada a comida está estragada, então eu prefiro nem ficar para o prato principal. O “Getting Started” do Hibernate, isso mesmo, o HelloHibernate, utiliza transação para um select. Veja o exemplo que vc postou:

private List listEvents() {
    Session session = HibernateUtil.getSessionFactory().getCurrentSession();
    session.beginTransaction();
    List result = session.createQuery("from Event").list();
    session.getTransaction().commit();
    return result;
}

Não faz qualquer sentido ter um commit para um select, mas se a própria documentação do Hibernate incentiva isso então temos um problema.

E vc não colocou a parte mais importante que é a configuração disso tudo via annotations ou xml. (Ok, ele suporta configuração programática, mas ninguém usa, não é recomendado pelos autores e não há documentação clara, então é próximo de inexistente)

Algumas pessoas vão falar que somos malucos, porque elas dominam o Hibernate e sabem que ele pode fazer coisas maravilhosas, desde do mais simples até o mais complexo.

Entretanto para carregar uma lista de beans, eu vou continuar fazendo assim:

List<Carro> carros = session.loadList(Carro.class);

Para carregar uma lista de todos os carros do RJ:


Carro carro = new Carro();

carro.setEstado("RJ");

List<Carro> carros = session.loadList(carro);

Para fazer um insert:

Carro carro = new Carro();
carro.setEstado("RJ");
// outros setters...

session.insert(carro);

E para configurar, eu vou continuar fazendo assim:

// Mentabean configuration
BeanConfig carro = bean(Carro.class, "Carros")
                .pk("id", DBTypes.AUTOINCREMENT)
                .field("name", DBTypes.STRING)
                .field("estado", DBTypes.STRING)
                .field("year", DBTypes.DATE)
                .field("motorId", "motor_id", DBTypes.INTEGER);

E para as queries mais complexas eu faço um SQL na mão mesmo.

Bem no meu ver o hibernate e um gigantesco framework com diversas configurações… mais ai vai a questão… se vc quiser usar o hibernate precisa usar todo ele??? para fazer insert na mão com jdbc e um saco… imagine um bean com 100 atributos que vc tenque fazer um insert desse bean na hora de gravar em 5 tabelas… pra fazer isto e extremamente desgastante e trabalhozo… usando o hibernate ai vc ganha bem mais vantagem pois pode usar um dos diversos scripts de mapeamento porai pra ele fazer o mapeamento do bean com as tabelas e depois e so chamar o metodo de insert do hibernate… agora não significa que pq vc use o hibernate vc tera que usar so o hibernate em todo o projeto… se vc achar mais simples fazer consultas por jdbc do que com criteria ou hql vc pode usar perfeitamente jdbc para isto… não se restringe apenas ao hibernate… varios frameworks tem milhares de configurações mas em grande parte vc precisara usar apenas 20% delas…

Veja o meu exemplo no post anterior. Usar JDBC totalmente puro não dá. Vai ser trabalhoso. O negócio é usar JDBC + uma ferramenta que te ajuda na construção das queries e que faça as queries básicas de CRUD pra vc. Acho que o iBatis vai mais por esse lado. O Mentabean , que já vem com o Mentawai, foi feito exatamente para esse propósito. Mas ninguém aqui está falando que quem usa o Hibernate está errado.

[quote=saoj]Quando vc entra na casa de alguém pela primeira vez e encontra uma imensa bosta de cachorro no meio da sala, vc se assusta um pouco. O “Getting Started” do Hibernate, isso mesmo, o HelloHibernate, utiliza transação para um select. Veja o exemplo que vc postou:

private List listEvents() {
    Session session = HibernateUtil.getSessionFactory().getCurrentSession();
    session.beginTransaction();
    List result = session.createQuery("from Event").list();
    session.getTransaction().commit();
    return result;
}

Não faz qualquer sentido ter um commit para um select, mas se a própria documentação do Hibernate incentiva isso então temos um problema.
[/quote]

-E-X-A-T-A-M-E-N-T-E-

Imagino pq a configuração programática é desaconselhada.

Mas era exatamente esse o ponto que eu queria que essa discussão chegasse… NÃO HÁ ABSTRAÇÃO DE COMPLEXIDADE NISSO!!

Reinventar a roda é uma expressão preconceituosa. Se o ser humano não tivesse reinventado a roda os automóveis não teriam pneus e as rodas de automóvel , trem e avião seria iguais e rodas dentadas não existiriam.

Reinventar a roda é importante se for necessário. Mas só se for necessário.

O uso de vários frameworks sem integração/orquestração pode gerar demasiados artefactos e tornar o sistema complexo de manter (não, obviamente, para quem o construiu). Por isso que é bom usar padrões de mercado ( e me refiro a padrões como EJB e não a “frameworks que o povo usa” )

A configuração por codigo é extreamamente importante porque é a forma que o framework fornece para que seja integrado num framework maior. E tem outras vantagens como a possibilidade de escreve essa configuração em alguma linguagem de script de forma que possa funcionar como um properties inteligente.
A configuração com xml ou anotações deve ser apenas um simplificador ou um extensor. Por exemplo, um framework de desenho de telas pode se aproveitar de xml para gerar as telas com ferramentas.
A configuração por convenção é apenas dizer que existe um default para cada opção e que esse default deve ser escolhido conforme o uso mais comum do framework em causa. ( por exemplo, a configuração por convenção de um ORM deveria simplesmente não precisar de nenhuma configuração , anotação ou xml: simplesmente assume que os campos tem os nomes das colunas e classes das tabelas e converte os tipos primitivos sozinho)

Num framework a capacidade dele ser simples para quem não o entende, extensivel para casos particulares e configurável para casos raros é o que o torna uma boa ferramenta. Mas existem casos em que as ferramentas falham, e isso é normal (ler 50000 registros com hibernate…). Por isso que o principio de separação de responsabilidade deve ser seguido À risca e todo framework deve ser encapsulado por um codigo de aplicação.(padrão Façade ou Service)
Desta forma quando o framework não atender ou tiver que ser substituido a aplicação não sofre.

Acredito que o ponto em achar que Hibernate é complexo é diretamente relacionado à não entender o problema que ele soluciona. ORM não é simples e tentar fazer com JDBC caseiro é alo que não funciona. Implemente lazy loading (algo extremamente básico em qualquer sistema OO) no seu exemplo e você vai entender melhor o problema antes de criticar a solução.

Em primeiro lugar, ninguem aqui falou que é contra o hibernate, apenas estamos questinando a forma que ele é configurado e o aumento da complexidade que ele gera.

Segundo, ORM não é simples e muito menos o hibernate.

E por fim… ninguem tá questionando a funcionalidade do hibernate, ele tem lazy load ohhh o ibatis tambem tem, ele tem cache ohhh o ibatis tambem tem e mantem a simplicidade. O problema é que não se justifica criar uma nova linguagem HQL para trabalhar com os dados, a menos que queira deixar seu sistema mais lento além do mais não se troca de banco de dados como se troca de roupa neh??

JDBC não funciona? O Hibernate roda em cima doque?? Do Windows??? Na minha opinião se as pessoas se preocupassem em aprender mais as coisas veriam que JDBC não é tão complexo assim e utilizariam-no mais.

A maioria das pessoas que usam hibernate é que não entende o problema que ele soluciona, simplesmente começam usar o hibernate sem ter conhecimento do JDBC oq temos que convir que é errado e dá na mesma de começar usar struts sem saber oq é servlet.

Mais uma vez reitero que não estamos questionando o hibernate, e sequer a sua funcionalidade já enfatizada neste tópico, mas sim a relação framework vs. api java, quais os ganho em relação à complexidade nos usos de um ou de outro.

Coisas do tipo, eu preciso comitar minhas queries, tá mas com jdbc eu não precisava…

Entendeu??

Uma coisa é consequência da outra.

[quote=volnei]
E por fim… ninguem tá questionando a funcionalidade do hibernate, ele tem lazy load ohhh o ibatis tambem tem, ele tem cache ohhh o ibatis tambem tem e mantem a simplicidade.[/quote]

IBatis possui uma filosofia de ORM comletamente diferente, baseada em DAOs. O Hibernate não diz como seus dados serão acessados. IBatis costuma ser uma boa escolha quando sua base de dadosnão é consequência do seu modelo OO.

Você está partindo de uma premissa errada, como eu já havia alertado. HQL não é para tratar dados, é para tratar objetos. Objetos e relacionamentos são coisas muito diferentes e SQL não serve nos dois meios. Faça uma consulta polimórfica com SQL.

Quem falou que JDBC não funciona?

Ok. Faça uma consulta polimórfica com SQL em todas as estratégias de persistência de herança comuns e implemente lazy loading nela.

[quote=volnei]
Coisas do tipo, eu preciso comitar minhas queries, tá mas com jdbc eu não precisava…

Entendeu??[/quote]

Pelo que entendi seu programa não é transacional…

Como o autor do post falou, ninguém aqui está questionando o Hibernate, só que existe outras alternativas, como sempre dependendo do caso.

Minha opinião é que lazy loading só passa a ser essencial quando temos grafos de objetos muito grande (acima de 3 níveis de composição).

Hibernate tem outras coisas mais complexas como remoção em cascata, suporte a herança, locks e cache.

Se eu carrego um carro, que possui um monte de partes, que por sua vez possuem um monte de peças (composição em tres níveis), usando JDBC, eu terei que fazer esses tres carregamentos distintos na mão quando eu achar que eles são necessários. O Hibernate faz isso automaticamente quando o getter (getPartes, getPecas) são invocados.

Há controversias até que ponto lazy loading automático é bom ou ruim. Minha opinião é que vc (desenvolvedor) é que deve saber quando e onde as dependencias serão necessárias e carregá-las na mão, mas concordo que se o grafo é gigantesco isso pode se tornar trabalhoso.

Nada te impede também de programar na mão lazy loading em suas entidades, ou seja, quando o cara chamar o getPecas() vc checa se já foi carregado do banco, se tiver sido retorna, se não tiver sido então vai carregar. Vc não precisa obrigatoriamente de Hibernate para fazer isso.

Há uma discussão extensa sobre lazy loading aqui: http://www.guj.com.br/posts/list/57590.java

Outras:

Lazy Loading: Good or Bad: http://www.matshelander.com/wordpress/?p=45

Lazy Loading: The good, The bad and the Evil: http://weblogs.asp.net/fbouma/archive/2007/04/16/more-on-lazy-loading-vs-pre-loading-in-o-r-mapping-scenarios.aspx

O tópico não é sobre Lazy Loading ou Hibernate ou ORM, mas sim sobre abstração x reinventar a roda.

[quote=pcalcado][quote=volnei]
Em primeiro lugar, ninguem aqui falou que é contra o hibernate, apenas estamos questinando a forma que ele é configurado e o aumento da complexidade que ele gera.
[/quote]

Uma coisa é consequência da outra.
[/quote]
Então???

[quote=pcalcado]

A filosofia é diferente mas o objetivo é o mesmo.

No fim tudo vira SQL se vc consegue fazer com HQL vc consegue fazer com SQL. A abstração da complexidade é que não fica muito clara quando se fala em criar uma linguagem intermediária.

Bom mas uma vez, consultas polimóficas é uma idéia de design no mínimo ridicula, lazy loading se faz utilizando uma biblioteca qualquer feita pra isso, até mesmo a que o hibernate usa.

[quote=pcalcado]

Que programa? Mas uma vez você está se equivocando, eu não to falando que tenho a solução do mundo, estou apenas discutindo oque existe.

[quote=volnei]
Então???[/quote]

Então que ou você sugere algo que traga os mesmos benefícios e seja mais simples ou essa crítica não faz sentido.

Java, C++e C# fazem a mesma coisa. São iguais?

Eu consigo fazer uma aplicação web em assembly. No final tudo vira código de máquina mesmo, porque eu usaria Java?

Eu Não falei que JDBC não funciona, eu falei que fazer lazy loading com JDBC caseiro não funciona.

‘biblioteca que o hibernate usa’? O hibernate é uma implementação de ORM, ele é a biblioteca de lazy loading que usa. Novamente: entenda o problema antes de criticar a solução.

Polimorfismo é uma idéia ridícula então? Acho que é melhor você ler alguns textos sobre orientação a objetos. Você pode até não gostar de usar objetos e suas características (como polimorfismo) mas se o fizer porque está usando java em primeiro lugar?

[quote=volnei]
Que programa? Mas uma vez você está se equivocando, eu não to falando que tenho a solução do mundo, estou apenas discutindo oque existe.[/quote]

E ‘o que existe’? Transações? Se você acha que transações são ruins basta abstrair seu uso. Existem dezenas de formas de fazer isso, caseiras ou não. O idioma do-commit/rollback é padrão para qualquer interação entre dois sistemas, isso não tem nada inventado pelo Hibernate.

[quote=volnei]Esse termo tão utilizado aqui e nos diversos fóruns e blogs do mundo não tá um pouco ultrapassado? Estou iniciando uma discussão…
[/quote]

:XD:

T+

Ao meu ver o hibernate nao e um framework de abstração de complexidade e sim de abstração de trabalho…

Boa Proteu :idea:

[quote=luistiagos]Ao meu ver o hibernate nao e um framework de abstração de complexidade e sim de abstração de trabalho…
[/quote]

OK, mas deixarei claro pela ultima vez, NÃO ESTOU FALANDO DO HIBERNATE, estou falando de abstração de complexidade vs. reiventar a roda… será que é tão dificil se manter no assunto??