Sobre Id's de banco de dados em classes Java

Oi pessoal,

Eu estou começando a modelar meu Projeto Final da faculdade, e tenho algumas dúvidas sobre a utilização de Id’s.

A primeira delas, é porque todas as tabelas de banco geralmente tem um Id, mesmo que a própria entidade possua um atributo que serviria muito bem como chave primária. Por exemplo uma tabela unidade_de_medida com um atributo “codigo” (que serviria muito bem como uma primary key) tem também um id_unidade_de_medida. Faço essa pergunta, pois dois três projetos que eu trabalhei, dois usavam esse padrão de criar um id para tudo. Isso tem algo a ver com a criação de índices, está ligado ao uso de frameworks ORM ou coisa assim?

A segunda dúvida é se esses Id’s devem aparecer nas classes de domínio, alguns já me disseram que o uso dos id’s facilita no Equals(), outros (inclusive essa era a visão do meu professor na faculdade) diziam que classes não deveriam ter id’s, pois fere o principio de mapeamento objeto-relacional.

Eu queria saber a opinião de vocês sobre esses assuntos, vcs costumam criar sempre os id’s no banco? E esses id’s refletem nas classes java? E qual o ganho disso. Como eu disse, eu vi esse padrão em dois dos meus três projetos, mas nunca sobe o ganho real disso.

Esse é meu primeiro post no forum, eu dei uma pesquisada sobre o assunto e não encontrei nada, se de repente já tiver um post sobre isso peço desculpas.

Muito obrigado!

quem disse q todas as tabelas geram ID´s? uso mysql e quem gera os atributos sou eu…

Não não, não foi isso que eu quis dizer. O que eu disse foi que os projetistas desses sistemas que eu trabalhei criavam id’s (int’s sequenciais) para todas as tabelas, mesmo para entidades que possuiam atributos que serviriam como PK’s.
Minha dúvida é em relação ao ganho dessa prática. Como eu disse, eu só passei por três projetos, e em dois deles era feito assim. Eu queria entender melhor o porquê.

Dê uma pesquisada no google sobre surrogate keys. O que eu penso é que às vezes alguns projetistas usam isso, mas em alguns projetos em que vi foi meio que generalizadamente - o que na minha opinião descaracteriza um pouco o princípio verdadeiro por trás da solução. Mas fazer o que né… às vezes quando você tem um martelo na mão tudo o que vê pela frente parece prego…

Olá,

Ainda existe uma certa “polêmica” sobre a melhor forma de definir chaves para tabelas de um banco.
Alguns defendem o uso de chaves naturais (ou seja, um identificador que faz parte dos dados) e outros preferem chaves artificiais, os IDs a que você se refere.

A vantagem de cada um é basicamente a seguinte:

  • Chaves naturais modelam melhor a realidade do negócio, e garantem dados mais consistentes (por exemplo, se a chave em uma tabela de clientes é o CPF, uma mesma pessoa não pode ser incluída duas vezes. Se essa chave for um ID, será possível incluir dois clientes com o mesmo CPF).
    Por outro lado, tendem a tornar o modelo mais complexo, pois muitas vezes torna-se necessário o uso de chaves compostas.

  • Chaves artificiais (ID) deixam a modelagem e a codificação das aplicações mais simples, devido a ausência de chaves compostas. Geralmente os frameworks ORM precisam que a base esteja dessa forma para que funcionem corretamente.

[quote=gomesrod]Olá,

Se essa chave for um ID, será possível incluir dois clientes com o mesmo CPF).
[/quote]

Pra isso servem as restrições de unicidade :wink:

Já vi projetos utilizando as duas abordagens e acredito que as chaves artificiais causam menos dor de cabeça.

Um problema que vejo com chave natural é que é o próprio usuário que a define.
E se ele escolhe, eventualmente ele vai querer alterá-la (nem que seja por ter digitado errado).
Daí você começa a criar tabelas com opção de Update cascade ou simplesmente impede o usuário de alterar aquilo.
(O que geralmente gera chamado no suporte técnico com essa solicitação)

Outra coisa é que chaves naturais promovem a criação de chaves compostas.
Escrever joins vira uma chateação (sim, estou reclamando da quantidade de código mesmo).
Sem falar que, alguém que não conheça a modelagem tem que ficar consultando o ddl da tabela para descobrir qual é a pk.
(Outra fonte comum de erros, as pessoas tendem a “chutar” qual é a chave).

Geralmente eu tendo a usar ids em todas tabelas.
Se precisar evitar duplicações (como no caso co CPF), crio a coluna com constraint unique.
Ainda não tive problemas com essa abordagem, então continuo seguindo.

Algo que procuro fazer é nunca mostrar o id para o usuário do sistema.

E um último conselho, não é aconselhável utilizar o id como base no equals.
Novos objetos tem seus ids alterados quando persistidos e isso pode te gerar problemas.

??

??[/quote]

Acabei deixando esse trecho no ar pois achei que já tinha escrito muito…

Quando eu digo que os objetos tem id alterado, quis dizer que têm ids zerados antes de serem persistidos e depois ganham um id de verdade.

Se utiliza o id no equals (e por consequência no hashcode), você pode ter problemas para encontrar objetos em mapas e sets por exemplo.

Mas como,se a partir do momento q é persistido o objeto passa a ter um ID válido?

Mas como,se a partir do momento q é persistido o objeto passa a ter um ID válido?[/quote]

Imagine a situação abaixo:

        MinhaClasse meuObjeto = new MinhaClasse("outras");
        
        HashSet<MinhaClasse> meuSet = new HashSet<MinhaClasse>();
        
        meuSet.add(meuObjeto);                
        
        System.out.println( meuSet.contains(meuObjeto) );
        persiste(meuObjeto);
        System.out.println( meuSet.contains(meuObjeto) );

//método persiste
    static void persiste(MinhaClasse objeto) {
        objeto.setId(1);
    }

No caso, implementando o método equals e hashcode utilizando apenas o campo id da classe.

Estou falando de um caso em que utilize o objeto em sets antes e depois de ser persistido.

Em resumo, utilizar o id no hashcode quebra o contrato do método proposto pela especificação.

Pessoal, muito obrigado pelas respostas, era bem isso que eu queria mesmo, aprender sobre esstática de definição de pk’s.
Procurei saber sobre as chaves substitutas, e junto com o que o pessoal, escreveu aqui, eu decidi que vou usá-las sim, principalmente porque acho que elas vão agilizar as coisas, junto com o hibernate. Eu já estava inclinado em fazê-lo, mas queria saber melhor porque eu estaria fazendo.

Na verdade eu me expressei mal em relação a essa questão do equals(), a vantagem a que citei era poder comparar se dois objetos instanciados em momentos diferentes faziam referência ao mesmo registro, sem ter que comparar um conjunto de atributos, mas sempre com os objetos já recuperados do banco. De qualquer forma, obrigado AbelBueno, entendi bem o que vc quis dizer!

Valeu!