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?
- 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ê.
- 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.
- 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. 
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:
-
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.
-
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);
-
Leia todos os método da classe Criteria, em uma query você dizer ao HB para retorna todos os objeto fora do contexto.
-
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.