Por que usar Hibernate é melhor do que DAO + JDBC?

[quote=saoj]Verdade, já que o meu caso está no extremo oposto. Muito campos, muitos índices, muito peso no banco na hora de fazer as queries. Solução encontrada: replicação de banco em bases read-only de 10 em 10 minutos, ou algo assim que o DBA inventou. Não me lembro. (Isso era a busca do ParPerfeito e o cupido, isto é, pega esses 10 milhões de usuários e me diz quem combina comigo…)

Bom, essa discussão não vai ter fim. O Hibernate é soda, mas eu não sei usar direito e não tenho saco para aprender, principalmente se estiver na secura de terminar logo um projeto.

Alguma alma caridosa (vai ser difícil), poderia migrar o mybooks para Hibernate. Só para se ter um comparativo mais prático entre uma coisa e outra. O mybooks é simples mais tem seus inner joins, e espaço para um cachezinho na busca por livros / usuários…

[/quote]

Concordo que Hibernate é chato de aprender, bem mais que JDBC. Se entregar o resultado for o mais importante, por espaço com ele.

Valeu Peleteiro… Já gostei do que vi…

Pena que tu esqueceu do order by.

Me responde também se o Hibernate primeiro ordena para depois limitar (SQLServer) ou primeiro limita para depois ordenar (Oracle).

Isso vai fazer toda a diferença. Isso vai automático de acordo com o banco que está por trás ??? Se for maravilha…

[quote=saoj]Me responde também se o Hibernate primeiro ordena para depois limitar (SQLServer) ou primeiro limita para depois ordenar (Oracle).

Isso vai fazer toda a diferença. Isso vai automático de acordo com o banco que está por trás ??? Se for maravilha…
[/quote]

Isso é automático, cada dialeto de banco de dados define o seu próprio comportamento.

No oracle eu posso fazer:

select id, date from profiles where date >= sysdate - 10 and rownum <= 100 order by date;

Pega os primeiros 100 que aparecer e ordena eles.

select id, date from (select id, date from profiles where date >= sysdate - 10 order by date) where rownum <= 100;

Pega tudo primeiro, ordena, e me passa os primeiros 100 da lista.

Como vc vai ter esse nível de controle usando o Hibernate eu não tenho idéia. Até porque sou leigo em Hibernate. :roll:

Escrevendo o SQL nativo do seu banco e mandando o Hibernate executar :thumbup:

O Hibernate tem n facilidades. É mais fácil fazer session.save do que ficar se matando para colocar as props do seu objeto um statement (ou PreparedStatement).

Também é bem mais fácil trabalhar com relacionamentos no Hibernate.

E a Criteria API é excelente para buscas! Um dos pontos altos do Hibernate.

As vezes, com coisas não tão trivais assim, é chato trabalhar com Hibernate e vc talvez tenha um overhead a mais no seu sistema. Mas os casos são minoria. Acredito que qq overhead que o Hibernate gere é compensado pelo que vc talvez fizesse de errado com SQL na mão.

nossa e td começou com um singelo post meu =D hahaha
pena que ainda não achei solução :frowning:

Well, eu nao entendo quase nada de BD mas lah na empresa tinhamos um problema classico, comecamos a usar EJB e nao fizemos nenhum relacionamento no banco de dados (o relacionamento tenicamente ficava no codigo Java), isso foi uma pessima ideia (ainda bem que nao fui eu a dar ela, eu nem estava no projeto ainda), entao um companheiro foi contratado para ser o arquiteto pq nao tinhamos esse papel definido, o cara eh muito bom, e uma das primeiras coisas que ele fez foi desenvolver um fwzinho de acesso a JDBC (bem interessante) e implementar o padrao DAO pra nos, perfeito pq conseguimos manter a coexistencia entre DAO e EJB relativamente pacifica, mas daih ele quer usar hibernate, mas pelo que entendi nao podemos ainda pois o hibernate nao tem flexibilidade suficiente para uma coexistencia pacifica com EJBs, entao soh usaremos hibernate quando nao tiver mais uso de EJB no sistema (estamos atualizando aos poucos, retirando os EJBs quando mexemos em algo antigo).

Eh exatamente nesse caso que a pergunta do titulo nao se torna retorica, correto?

[quote=saoj]No oracle eu posso fazer:

select id, date from profiles where date >= sysdate - 10 and rownum <= 100 order by date;

Pega os primeiros 100 que aparecer e ordena eles.

select id, date from (select id, date from profiles where date >= sysdate - 10 order by date) where rownum <= 100;

Pega tudo primeiro, ordena, e me passa os primeiros 100 da lista.

Como vc vai ter esse nível de controle usando o Hibernate eu não tenho idéia. Até porque sou leigo em Hibernate. :roll: [/quote]

Sergio, as duas DMLs são possível com Hibernate/HSQL
E para mim, a grande vantagem de usar HSQL é a possibilidade de trocar de banco a hora que eu quiser, sem mudar nada, mas nada mesmo, na aplicação, e minha aplicação hoje roda em vários clientes diferentes, com Oracle, SQL-Server, MySQL e Postgres.

Outra vantagem que gosto no hibernate é o SchemaExport para gerar a base de dados à partir dos arquivos de mapeamento do hibernate. Eu adoro esta funcionalidade! :smiley:

Têm uma coisa que têm que ser levada em conta no uso do Hibernate, e que é responsável por pelo menos 50% do problemas na utilização do Hibernate que encontro por ai. O fluxo de utilização dele é completamente diferente do fluxo utilizado quando se têm JDBC.

Em JDBC você cria um DAO com create() save() e delete(), por que esse é o fluxo, você cria um object, você atualiza um objeto e você deleta um objeto.

Mas no Hibernate, 2.1 pra cima, você torna um objeto persistente e torna um objeto transitório (trasient). Você não atualiza um objeto explicitamente.

O que existe é o contexto da sessão (do hibernate). Todo objeto que estiver nesse contexto terá seu estado sincronizado com o seu registro no banco, sem que você tenha que dizer ao HB para fazê-lo.

Exêmplo: O código abaixo vai atualizar o campo nome, na tabela cliente.

Transaction t = session.beginTransation(); Cliente c = session.get(Cliente.class, 1); c.setNome("Novo valor"); t.commit(); s.close();

Eu não preciso, e nem devo, chamar o session.update(). O update() não serve para atualizar o objeto no banco. Ele serve para colocar o objeto devolva ao contexto da session, e como nesse caso ele nunca saiu da sessão não deveria nem estar utilizando esse método.

** Todo objeto carregado pelo Hibernate, por default (você pode alterar o comportamente em alguns casos), está no contexto da sessao do Hibernate. **

Vamos supor que você queira fazer um wizard, colocando o objeto na HTTPSession, e editando aos poucos? Como você já deve ter percebido isso não vai funcionar por que se o indivíduo deixar o processo no meio babau, as alterações feitas nas telas anteriores foram salvas né? Segue abaixo um pseudo-código executado nesse processo:

[code]// 1º Tela
Session s = openSession();
Cliente c = httpSession.get(Cliente.class, 1);
httpSession.setAttribute(“cliente”, c);
c.setNome(valorNovo);
s.close(); // <-- Auto-commit, o nome foi gravado no banco
// e a instância “c” foi retirada do contexto.

// 2º tela
Cliente c = (Cliente) httpSession.getAttribute(“cliente”);
c.setIdade(12); // Você nem usou hibernate aqui, então tudo bem.

// 3º Tela
Cliente c = (Cliente) httpSession.getAttribute(“cliente”);
c.setEndereco(valorNovo);
Session s = openSession();
s.update©; // Ele está colocando o objeto no contexto e atualizando o
// registro no banco. Na verdade, seria melhor utilizar “merge” aqui.
s.close();[/code]

Nesse código a primeira tela é salva e não deveria ser salva. Como resolver isso?

  1. Carregue o objeto e feche a sessão logo em seguida.

Session s = openSession(); Cliente c = session.get(Cliente.class, 1); s.close(); httpSession.setAttribute("cliente", c); c.setNome(valorNovo);

A solução (1) não resolve o problema se você quiser reutilizar essa session e/ou você estiver utilizando um PicoContainer ou Spring da vida que gerêncie a session para você.

  1. Carregue e retira o objeto do contexto logo em seguida.

Session s = openSession(); Cliente c = httpSession.get(Cliente.class, 1); s.evict(c); httpSession.setAttribute("cliente", c); c.setNome(valorNovo); s.close();

A solução (2) resolve, porém bye bye LazyLoading.

  1. Utilize session-per-conversation

Basicamente um session por processo, (no caso haveria uma mesma Session para todo o conversa do wirzard).

[small]* Processo “conversa” (conversation), um processo que é maior que apenas um request, porém não é longo.[/small]

Antes que me atirem pedras, eu não disse um conexão com o banco ativa durante a toda a conversa, eu disse uma sessão.
Esse é inclusive o padrão utilizado pelo Steam, jBMP… (BTW, Estou tentando utilizar isso no Spring de forma mais automática, alguem sabe como? Spring Web Flow?)

Segue um pseudo-codigo que como seria isso:

[code]// 1º Tela
Session s = openSession();
s.setFlushMode(FlushMode.NEVER);
httpSession.setAttribute(“s”, s);
Cliente c = session.get(Cliente.class, 1);
httpSession.setAttribute(“cliente”, c);
c.setNome(valorNovo);
s.disconect(); // Não é close.

// 2º tela
Cliente c = (Cliente) httpSession.getAttribute(“cliente”);
c.setIdade(12); // Você nem usou hibernate aqui, então tudo bem.

// 3º Tela
Cliente c = (Cliente) httpSession.getAttribute(“cliente”);
c.setEndereco(valorNovo);
Session s = (Session) httpSession.getAttribute(“s”);
s.setFlushMode(FlushMode.AUTO);
s.reconnect();
s.close();[/code]

Pronto, você acaba de utilizar o Hibernate (e no futuro EJB3, que funciona da mesma maneira) de forma correta.

Poxa, comecei a escrever e não parei mais… hehehe… desculpem aew a menssagem um pouco grande. :smiley:

Os código acima foram feitos sem ajuda de uma IDE e escondido do meu chefe, e como sou meio depedênde de IDE, podem haver erros.

Apenas umas ultimas dicas:

  1. Leia com atenção o javadoc, especiamente do Session, e tenha certeza que você sabe para que serve cada método daquele. Os nomes não são os melhores, então não deduza, leia.

  2. Você pode utilizar Session.lock para retorna um objeto ao contexto também.

// Re-associa apenas. sess.lock(obj, LockMode.NONE); // Verifica a versão do objeto, então re-associa. sess.lock(obj, LockMode.READ); // Verifica a versão do objeto usando SELECT ... FOR UPDATE, então re-associa. sess.lock(obj, LockMode.UPGRADE);

  1. Leia todos os método da classe Criteria, em uma query você dizer ao HB para retorna todos os objeto fora do contexto.

  2. ATENÇÂO! ATENÇÂO! ATENÇÂO! ATENÇÂO! ATENÇÂO! ATENÇÂO!
    O método merge()/update()/save()/saveOrUpdate() do Session serve para fazer update de um objeto que não esteja no contexto da session. De fato, ele apenas coloca o objeto denovo no contexto. Por favor, leia a documentação atenciosamente.

  1. Leia
    http://www.hibernate.org/hib_docs/v3/reference/en/html_single/#objectstate-transitive
    http://www.hibernate.org/hib_docs/v3/reference/en/html/objectstate.html

juzepeleteiro , Parabéns por seu post 5 estrelas pra ele, estou iniciando em hibernate e suas informações foram muito úteis.
Abraço

[quote=Alexandre Possebom]juzepeleteiro , Parabéns por seu post 5 estrelas pra ele, estou iniciando em hibernate e suas informações foram muito úteis.
Abraço[/quote]

Obrigado :wink:

Parabéns mesmo. Meio complicado esse hibernate, mas com essa sua explicação bem-detalhada e clara, vai facilitar a vida de quem está começando agora com ele e/ou tentando migrar de JDBC para Hibernate (meu caso).

Bom, aproveitando o assunto Hibernate + DAO.

Dada as classes:


    public class Pessoa{

        public Collection getDependentes();
        public void setDependentes(Collection col);
 
        //Outros gets and sets

    }

    public class Dependente{

         //gets and sets

    }

Imaginem a seguinte situação. Implemento um DAO com o hibernate. Imaginem que seja a seguinte interface.

interface PessoaDAO
{
     Pessoa getPessoaById(Long id);
}

Dai minha logica de negócio usa da seguinte maneira:

Pessoa pessoa = pessoaDAO.getPessoaById(1);
Collection dependentes = pessoa.getDependentes();
//Faz algo com cada dependente, alterando os atributos do dependente

Ok… Tudo funciona legal com hibernate, no fim da sessão tudo é atualizado ok…

Agora ESQUECAM O HIBERNATE! Alguma alma infeliz implementa SÓ a camada de persistência (i.e DAO´s) usando JDBC puro mas no método PessoaDAO.getPessoaById ele não pré-carrega os dependentes (porque deveria?) e nem modifica a classe Pessoa nem Dependente que são POJO´s. Resultado: getDependetes retorna null e minha lógica de negócio se quebra. Uma alteração na camada inferior resulta em problemas na superior.

Então minha dúvida e observação é essa. Ao utilizar hibernate, EJB ou outra coisa que têm esse mecanismo de persistência “automático”, ficamos amarrados a outro semelhante mesmo ou eu desconheço algo??? Mas a interface de DAO não devria supostamente abstrair o mecanismo de persistência??? Ou talvez minha interface também deva especificar eager-loading?

Por favor, discutam sobre esse assunto, acredito que é algo interessante e que pode acrescentar.

Valew!

Quem implementar o DAO vai ter que garantir o contrato, que é “trazer” (seja com Lazy loading ou não) as dependências, problema de quem vai implementar com JDBC.

Ninguém disse que ia ser fácil :stuck_out_tongue:

[quote=saoj]
Empresa Oi tem 3 bilhões de ligação.

Nem vc nem o Hibernate vão poder carregar todas essas ligações numa lista. Seja Lazy Loading, ou seja o que for.

Isso é um problema de tunning de banco de dados, já que terá que haver um belo índice que te permita pegar subsets dessa mundo sem distruir a máquina e/ou parar o banco.[/quote]

Não senhor. Eu não falei que queria ter todas estas ligações, muito pelo contrário.

Para dar contexto eu já trabalhei numa aplicação com o seguinte cenário:

Ligação*—1 Assinante *—1 Grupo

Com casos de uso do tipo:

Listar todos os assinantes do grupo, depois o usuário seleciona um destes para ver suas ligações.

Sem lazy loading:

1 - Traz a lista de assinantes do banco de dados
2 - Exibe a lista
3 - Faz outra query pedindo todas as ligações do usuário
4 - Exibe a lista

Com lazy loading:
1 - grupoEscolhido.listarAssinantes();
2 - Exibe a lista
3 - usuarioEscolhido.listarLigacoes();
4 - Exibe a lista

Isso parece besteira, mas não é. Lazy loading te permite parcialmente esquecer que existe um banco de dados envolvido.

Quando você possui métodos que fazem a mesma coisa o bicho pega.

Imagine que no exemplo temos um método no DAO que faz uma consulta recuperando apenas os assinantes, sem suas ligações, do grupo:

class Dao{

  public Set&lt;Assinante&gt; listarAssinantes(Grupo grupo){...}
}

Agora imagine que você tem outro caso de uso que exige que voce traga do grupo os assinantes e suas ligações.

class Dao{

  public Set&lt;Assinante&gt; listarApenasAssinantes(Grupo grupo){...}

  public Set&lt;Assinante&gt; listarAssinantesComLigacoes(Grupo grupo){...}

}

Baseado no seu caso de uso você escolhe um ou outro e eu poderia afirmar que 70% destes métodos é código repetido.

Falando em Caso, agora eu quero saber quanto de credito um usuario tem. O credito é dado por:

Crédito = Limite do Grupo - Gasto pelo grupo
Gasto pelo grupo = SOMA(Gasto assinantes)

Então eu tenho mais um método:

class Dao{

  public Set&lt;Assinante&gt; listarApenasAssinantes(Grupo grupo){...}

  public Set&lt;Assinante&gt; listarAssinantesComLigacoes(Grupo grupo){...}

  public Grupo getGrupoDo(Usuario u){...}

}

Isso pode até estar desnormalizado no banco de dados para performance, mas estamos lidando com objetos e não falando sobre manipulação de dados em si 9que eu também posso fazer com Hibernate, aliás), fora que eu posso estar utilizando um banco de dados sem stored procedures (como antigos MySQL).

O que acontece quando nosso sistema precisa informar que o usuário gastou algo? Teria no DAO algo assim:

class Dao{

  public Set&lt;Assinante&gt; listarApenasAssinantes(Grupo grupo){...}

  public Set&lt;Assinante&gt; listarAssinantesComLigacoes(Grupo grupo){...}

  public Grupo getGrupoDo(Usuario u){...}

  public void adicionarLigacao(Usuario u, Ligaçao l){...}

}

A tendência é ter muitos métodos repetidos, uns trazem uma relação, outros não. COmo dito, muitos deles têm mais de 70% de código exatamente igual.

Utilizando Hibernate você só precisa de um método que retorne a lista de assinantes.

Quer saber todos os assinantes?


Set&lt;assinante&gt; assinantes = dao.listarAssinantes();

for(Assinante a:assinantes) System.out.println(a.getNome());

Precisa das ligações de um assinante da lista?

Set&lt;assinante&gt; assinantes = dao.listarAssinantes();

//NOTA: eu sei que set não tem get, mas ignore isso para facilitar
Assinante a = assinantes.get(0);
System.out.println(a.getLigações());

Crédito dele?

Set&lt;assinante&gt; assinantes = dao.listarAssinantes();

//NOTA: eu sei que set não tem get, mas ignore isso para facilitar
Assinante a = assinantes.get(0);

System.out.println(a.getCredito());

Informar gasto?

Set&lt;assinante&gt; assinantes = dao.listarAssinantes();

//NOTA: eu sei que set não tem get, mas ignore isso para facilitar
Assinante a = assinantes.get(0);

a.adicionarLigacao(ligação);

Sem repetiçãod e código, o Hibernate cuida das idas ao banco de dados. Com minha experiência em ambos os lados tenho que dizer que tende inclusive a ser mais performático, não porque é tããão otimizado mas sim porque em nome de ‘performance’ os programadores acabam fazendo besteiras incríveis e piorando ainda mais as coisas, como os tais cache caseiros.

JDBC é bom quando o que te importa é o banco de dados. Hibernate é bom quando o que te importa são objetos de negócio. Para o meio de um processo de negócio para chamar o método no DAO e preencher um buraco no objeto é matar a transparência da persistência (lembre-se: objetos de negócio não sabem o que é persistência), e na maioria das vezes suas regras de negócio vão incluir idas ao banco de dados para repopular objetos que estão pela metade.

Todos os exemplos acima mais ou menos fazem parte de um sistema gigantesco que eu participei (e que tinha uma hierarquia de usuarios, grupos e perfis muito mais complexa que a do exemplo) utilizando JDBC puro. Eu só fui entender o quanto lazy loading é maravilhosos depois de repetir muitas vezes os métodos e olhar nome como:

getUsuarioComPerfil
getUsuarioSemPerfil
getUsuarioComPerfilELigacoes
getUsuarioComPerfilEGrupo

Isso proque não entrei nas atualizações, só falei de consultas. Quando você tem uma atualização em um objeto de negócio é complicado. A quantidade de NullPointerExceptions é enorme porque muitas vezes você vai mantêr um objeto em sessão que não está completo e precisa atualizar algo que não foi trazido.

pcalcado, ótimo post. Concordo que é muito bom e prático o lazy loading. Mas refaço minha pergunta então: se você confia num eager ou lazy loading de determinada implementação, o DAO não perdeu o sentido no que se refere à esconder a implemenção do mecanismo de persistência. Repare que a sua listarAssinantes não diz nada sobre ter que carregar outras relações em conjunto.
E se é deste modo então, para que o DAO neste caso? Eu entendo que uma das principais vantagens do DAO é você poder “trocar” a implementação da persistência “SEM” afetar a lógica de negócio que a utiliza.

Por favor me esclareça esse ponto.

[quote=duardor]
pcalcado, ótimo post. Concordo que é muito bom e prático o lazy loading. Mas refaço minha pergunta então: se você confia num eager ou lazy loading de determinada implementação, o DAO não perdeu o sentido no que se refere à esconder a implemenção do mecanismo de persistência. Repare que a sua listarAssinantes não diz nada sobre ter que carregar outras relações em conjunto.
E se é deste modo então, para que o DAO neste caso? Eu entendo que uma das principais vantagens do DAO é você poder “trocar” a implementação da persistência “SEM” afetar a lógica de negócio que a utiliza.

Por favor me esclareça esse ponto.[/quote]

Ah, agora eu entendi sua pergunta :slight_smile:

Utilizando Hibernate+DAO (encapsulado num Repository) você poderia trocar de persistência desde que a nova opção tenha as mesmas funcionalidades, como EJB3 (não conheço outras a ponto de mencionar aqui, mas acredito que existam. TopLink? JDO?).

Eu entendi sua preocupação mas não vejo como, sem um framework por trás, mantêr objetos de negócio que usem as vantagens de persistência transparente e ainda possam funcionar sem problema com persistência intrusiva, como a que se tem com JDBC direto.

O framework cumpre seu papel de tornar tarefas repetitivas e comuns (caching, lazy, eager) fáceis e razoavelmente padronizadas. Assim como qualquer outro framework você não rpecisa dele para isso mas… que trabalho vai dar!

Com a persistência padronizada do EJB 3.0 você pode ter como premissa o uso de EJB para persistência e ainda variar de implementação, mas voltar para JDBC é muito complicado.

Antes eu recomendaria Hibernate/EJB3.0 apenas para domínios complexos, agora eu recomendaria JDBC apenas para projetos muito simples ou para trechos de código que realmente precisam estar vinculados à bancos relacionais. Na maioria das vezes utilizar um framework trás vantagens desde o momento que se aprende a utiliza-lo.

Uma resposta mais concisa: O DAO permite que você isole o framework do seu código de aplicação. Você pdoe substituir o framework, mas o novo deve ter ao menos as funcionalidades que você já está usando.