.NET: Cinco dicas sobre o NHibernate

Publicado por Marcos Dell Antonio em 08/01/2007 | .NET

Depois de um ano batendo a cabeça contra o teclado para aprender a usar o NHibernate, sinto-me na obrigação de alertar/ajudar aos usuários sobre alguns detalhes que às vezes te deixam de cabelo em pé (ou sem cabelo, tanto faz, o chefe enchendo o saco será o mesmo).

1. Nunca, em hipótese alguma, use chaves primárias compostas

Eu fui obrigado a utilizar o NHibernate com tabelas que possuíam chaves primárias (PKs) compostas. Primeiramente, essa é uma péssima opção de design. Não acha? Então me responda: qual é o papel das PKs? No meu ponto de vista, e de outros “zilhões” de desenvolvedores do século 21, a PK serve unicamente para identificar uma linha de uma determinada tabela. É isso! Ponto final. Nada de querer facilitar a visualização de relacionamentos (cliente com seus contatos, por exemplo).

Enfie isso na sua cabeça e faça de conta que aprendeu com o tempo:

Chave primária = um e somente um campo chamado ID do tipo inteiro

O NHibernate teoricamente suporta o mapeamento de chaves compostas. Claro, na teoria é lindo. Na prática é um desespero. A sintaxe muda, a documentação é precária e até mesmo os próprios autores do best-seller no assunto, Hibernate In Action (é uma ótima referência ao NH, apesar de ser escrito baseado no irmão mais velho), não recomendam as PKs compostas.

2. Não use campos do tipo TIME

Por algum motivo cientificamente explicável, o NH não implementa suporte nativo aos campos TIME. Em outras palavras, isso significa que se você tiver um campo deste tipo na base de dados, terá que fazer uma implementação extra/proprietária para tratá-lo.

A solução é um tanto quanto simples, porém obscura. A documentação sobre este assunto é precária e o melhor de tudo: eu tinha uma implementação pra resolver esse problema… assim que fiz um update para a última versão do NH ela parou de funcionar! Maravilha, heim? Removeram umas classes que eu utilizava. Tudo bem que eram classes beta, mas estavam lá para qualquer abelhudo desesperado sair usando.

3. Curva de aprendizado gigante

Se você pensa que vai sair mapeando tabelas através de arquivos XML e classes C# e logo depois criar seu sistema e distribuí-lo, esqueça. Talvez esse não seja um ponto tão negativo quanto aparenta, pois a complexidade do NH faz com que os desenvolvedores tirem a bunda da cadeira e comecem a estudar padrões de projeto, gerenciamento de sessão e tudo o que há de melhores práticas para projetar e desenvolver um software.

Com o NH fui forçado a aprender técnicas de mapeamento objeto/relacional (O/R), alguns patterns do GoF e vários recursos do .NET. Tudo isso, sem dúvida alguma, refletiu em um crescimento intelectual/financeiro no que diz respeito à minha profissão.

4. Query By Example ignora a chave primária

Query By Example (QBE) é um recurso muito interessante dos frameworks de persistência de objetos. Basicamente funciona assim: você instancia um objeto, carrega algumas propriedades e pede para o framework retornar todos os registros de acordo com o informado. O SQL gerado trará na sua clausula WHERE as restrições de acordo as propriedades carregadas no objeto.

Ponto negativo: o NH não considera os campos da chave primária. Mais uma vez a composite-id, ou seja, chave primária composta, ferra com a nossa vida. Sem ela,  não é preciso usar QBE com campos da PK, afinal se a PK só tem um campo e você conhece ele, não tem motivo para informar outros. Com ela a coisa complica. Imagine o seguinte: cadastro de Clientes e Contatos, sendo que a tabela de Contatos possui sua chave primária formada pelos campos CodigoCliente e CodigoContato. Se eu quiser saber quais contatos de um determinado cliente são de Santa Catarina? O que fazer?

1 Contato c = new Contato(); 2 c.CodigoCliente = 1; 3 c.Estado = "SC";

Uma QBE usando o objeto do exemplo acima não iria considerar o valor da propriedade CodigoCliente. E aí, como fico? Não dá pra buscar todos os contatos de um cliente que moram em SC?

Para resolver esse “problema” (na realidade, como comentado anteriormente, é um problema de design) usa-se HQL, onde a query será montada manualmente incluindo as propriedades necessárias.

5. Os hermanos têm as respostas

É a realidade: o NHibernate não possui uma documentação sequer boa. A única solução para garantir a sobrevivência é pegando algumas idéias da comunidade Java, afinal eles estão anos luz à nossa frente quando o tema é persistência objeto/relacional.

O site JEEBrasil é uma excelente fonte de artigos sobre o Hibernate. E já que os dois frameworks são irmãozinhos (dizem que o NH nasceu superior, pois já conhece os erros do Hibernate e por isso não comete os mesmos - isso não reflete a minha opinião), por que não utilizar a documentação do outro? Vale a pena.

É isso aí. Apesar dessas considerações, continuo usando ele em projetos pessoais e profissionais. Depois que entra no ritmo, a produtividade cresce muito. Portanto, se você tem trauma dos DataSets que a Microsoft criou para resolver seus problemas (uh mau gosto heim), eis uma possível alternativa.

Espero que isso ajude alguém que esteja pensando em usar o NHibernate.

Até +.

16 comentários

  1. 1
    Charles Tenorio // March 23rd, 2007 at 3:47 pm

    Valeu pelas dicas mas me diga uma coisa se eu compra o livro Hibernate em ação vou pd usar o NHibernate sem dor de cabeça ?

    valeu!

  2. 2
    Marcos Dell Antonio // March 24th, 2007 at 10:51 am

    Olá Charles.

    Sem dúvida alguma ele vai te ajudar. É um excelente investimento.

    Até +.

    Ps: “não esqueça de esquecer” chaves compostas. Essas sim te dariam dor de cabeça. ;)

  3. 3
    jaqueline malman nunes // May 11th, 2007 at 5:33 pm

    Oi,
    preciso armazenar a hora de entrada de um equipamento, mas se ele não tiver hora pré determinada tenho q indicar atravez do cógido “99:99″, utilizei o campo DateTime para fazer o mapeamento, de um campo tipo Time no PostgreSQL, a principio deu certo, persistiu coisa mais linda hehehe..
    mas qndo fui setar o valor “99:99″ parou tudo, pq DateTime só trabalha com horas validas, gostaria de uma ajuda qnto a qual tipo de dados posso utlizar? e como mapear?
    Obrigada…

  4. 4
    Marcos Dell Antonio // May 12th, 2007 at 5:53 pm

    Oi Jaqueline.

    Por que você não tenta armazenar os Ticks da hora, já que o Postgre não aceita horas inválidas?

    Talvez os minutos ao invés de Ticks seria uma outra ótima opção. Esta eu até já usei.

    Aí no caso do 99:99, vc guardaria 6039 minutos (acho que é isso).

    Até +

  5. 5
    jaqueline malman nunes // May 14th, 2007 at 12:05 pm

    a opção de Ticks é boa e vou precisar em outras partes do sistema, pra resolver este problema, coloquei no formato hh:mm:ss, por exemplo, se a hora não é indicada, o sistema armazena no banco 01:01:01, como o usuário só indica hh:mm, mesmo q tenha q indique essa hora, a marcação no segundo faz a diferença… ;)
    obrigada..
    t++

  6. 6
    Marcos // May 14th, 2007 at 12:21 pm

    Legal saber que funcionou o mapeamento dos campos Time usando DateTime.

    Passei alguns dias revirando a web pra criar um tipo específico (MyTime) para essa situação.

    Provavelmente o “problema” está com o banco, que no meu caso é o MySQL.

    Até +.

  7. 7
    Renato Bertizini // October 17th, 2007 at 12:43 pm

    Boa tarde Marcos.

    Bom antes de mais nada, parabéns pelo texto, está ótimo.
    Estou com o problemas de todos, o famoso campo Time do MySQL.
    No meu servidor, está funcionando sem problemas quando é exibido em um Label, mas no meu Host é exbido como 00:00:00.
    Já tentei utilizar variáveis do tipo TimeSpan e conversões para o Tipo String, mas nenhum destes apresentou solução.
    Você teria alguma dica sem ser a conversão e armazenamento dos minutos? :)
    Não posso mexer na estrutura do banco de dados!!!

    Valeu
    Inté

  8. 8
    Humberto // November 29th, 2007 at 12:36 pm

    Ola Marcos,

    Segue alguns comentários sobre seu post:

    1. Nunca, em hipótese alguma, use chaves primárias compostas

    Chaves naturais ( tipo usar coluna CPF para tabela Pessoa ) sejam compostas ou não já não são consideradas boas práticas de modelagem de dados há bastante tempo mesmo antes do NHibernate.

    As chaves tem que ser apenas um contador mesmo ou um número sem significado. Se alguém reclamar com vc sobre falta de integridade de dados, utilize uma alternate key para garantir a unicidade de um determinado conjunto de colunas da tabela sem ter que alterar a sua chave primária.

    Ainda assim, ja tive que usar tabelas com chaves compostas aqui devido a a uma base-de-dados legada e realmente foi complicado fazer funcionar porque a técnica de utilização é muito mal documentada pelo tutorial do NH. Se quiser saber como funcionar me mande um email.

    2. Não use campos do tipo TIME

    Nunca tentei utilizar esse tipo de campo, entretanto consegui fazer o campo DateTime funcionar tranquilamente por aqui.

    3. Curva de aprendizado gigante

    Pelo que eu entendi so seu post a sua curva de aprendizado se deve mais devido a falta de experiência e/ou conhecimento da tecnologia de objetos. Se vc aprendeu isso após utilizar o NH muito bom para vc.

    O que particularmente percebi foi que, independente da persistência utilizada, a medida que vc emprega conceitos mais eficientes de orientação a objetos, mais produtividade vc ganha na implementação do seu sistema.

    4. Query By Example ignora a chave primária

    Infelizmente, eu não tenho conhecimento sobre essa classe visto que eu utilizo o método find em ISession para trazer os objetos do banco através de consulta em HQL.
    Posso te dizer mesmo com classes de chave composta não tive nenhum problema até agora.

  9. 9
    Laranjo // April 8th, 2008 at 11:11 am

    Eu vou continuar usando os meus TableAdapters que eu sou mais feliz viu…

  10. 10
    Matheus Veras // May 6th, 2008 at 9:55 am

    Mas da para usar DataSets tipados sem usar TableAdapters para substituir os arrays e os vetores, que é uma boa.

  11. 11
    Eric // May 21st, 2008 at 5:42 pm

    Eu tambem não concordo quanto a NÃO utilizar chave primaria composta.

    Imagine voce utilizando cadastros que necessitam utilizar mais de um campo para a entidade realmente ser unica? porque realmente utilizar um Numero Inteiro que nem se quer faz parte dos atributos da entidade?

    Na empresa que estou trabalhando, utilizamos cadastros de empresas com chave primaria em cnpj e filial e outros campos, continuei essa pratica em meus projetos pessoais e nunca me deu problema algum quanto a modelagem do bando de dados.

    Afinal chave primaria não é para identificar um “registro” no banco e sim garantir que uma “entidade” seja unica.

    se eu não tivesse utilizado chaves compostas nos meus projetos, teria que utilizar varias checks e até triggers (dependendo do caso) para validar registros.

    lembrando tambem que uma chave primaria utiliza o melhor indice do bando de cados, um tipo de unique index organizado, ou seja, colocando um campo id_registro na sua tabela/entidade como chave primaria voce vai estar desobedecendo as propriedades que tornam uma entidade unica e ao mesmo tempo terá que criar outros index para garantir a velocidade da tabela quanto a joins.
    E utilizando as propriedades que tornam a entidade unica no banco, voce terá uma documentação rapida de como essa entidade se relaciona com outras tabelas

    por ex no sql server- digita nome da tabela; F1; veja que campos compoe a PK.
    assim voce tem uma noção maior de como sua entidade funciona e é relacionada.

    quanto ao NHibernate, sou novato e vou pesquisar melhor, se realmente for uma limitação dele o uso de chave primaria composta então acho que não vou poder utiliza-lo.

  12. 12
    Vinícius // July 3rd, 2008 at 5:43 pm

    Eric,

    O mapeamento O/R encara cada tupla como um objeto. A forma mais prática para IDentificá-los como únicos é a criação de um campo numérico e de autoincremento.

    Para gerir a integridade dos dados de forma que os mesmos não se dupliquem, por exemplo, crie Alternate Keys.

    Embora sua solução funcione, para mapeamento O/R ela não é indicada.

    [ ]s

  13. 13
    Vinícius // July 3rd, 2008 at 5:44 pm

    Marcos Dell Antonio,

    A propósito, ótimos pontos que vc levantou! Para quem vai começar com o NHibernate e ler as suas dicas vão poupar muitas horas (ou dias) de trabalho!

  14. 14
    Emerson // September 3rd, 2008 at 10:12 am

    O livro hibernate em ação ajuda mas não evita as dores de cabeça de maneira nenhuma.

  15. 15
    Vinicius Pacheco // January 20th, 2009 at 1:52 pm

    Parabéns pelo artigo, Marcos.

    Só peca em uma coisa: assumir que um design especificamente bom para uso com o Hibernate (ou nHibernate) é sempre bom.

    Ao colocar que “PK serve unicamente para identificar uma linha de uma determinada tabela.” você tem razão. No entanto, criar uma coluna adicional para tratar essa situação pode ser muito bom em sistemas onde a quantidade de dados não é tão significativas assim.

    Em muitas situações a geração de PK através de FKs de outras tabelas é completamente racional. Trazer um modelo de dados para a sua forma normal é uma das prerrogativas de um bom modelo de dados, sendo as desnormalizações e redundâncias toleradas em casos muito específicos.

    Um bom caso específico seria o fato de se usar o Hibernate onde as chaves compostas são um problema. Ou seja a solução é essa mesmo. Mas isso não quer dizer que o uso de PKs compostas seja um erro. Só será se houver a determinação/intenção de uso do Hibernate.

    Como dizia uma mensagem bem humorada a respeito de regras de escrita: “A generalização é sempre um erro”…

    Abraço e mais uma vez parabéns.

  16. 16
    Rafael // February 11th, 2009 at 9:00 pm

    Eu acho a documentação do hibernate e nhibernate um cú.. falta um botão pesquisar naquela porra

Deixe o seu comentário

Anúncios

Anúncio provido pelo BuscaPé