MySQL, Foreign Key e DAO + Elegância! É possível?

26 respostas
Thiago_Senna

Olá GUJ’s…

Estou criando um DAO chamado ServicoPrestadoDAO, e me surgiu uma dúvida. Na base de dados a tabela ServicoPrestado tem um relacionamento com a tabela empresa à partir do CNPJ. Por isso criei o FK usando o ALTER TABLE. A minha dúvida inicial é a seguinte:

Quando eu for criar um novo serviço prestado e passar um cnpj de uma empresa que não existe no meu banco será lançado uma exceção? Ou seja, o banco de dados deve barrar a entrada deste registro ou não? Será que eu terei que implementar esta consistência no próprio código do DAO?

Bom, eu fiz alguns testes, e parece que vou ter que fazer esta consistência no DAO mesmo, mas acho q o banco deveria cuidar disso, e não meu DAO. Fica muito deselegante e vai poluir muito meu código. Será que estou fazendo algo errado, o MySQL não suporte isso ou nenhum banco suporta?

O Script SQL segue abaixo:

DROP TABLE IF EXISTS `sabesbd`.`empresa`;
CREATE TABLE `empresa` (
  `cnpj` varchar(14) NOT NULL default '',
  `nome` varchar(30) NOT NULL default '',
  `endereco` varchar(80) default NULL,
  `complemento` varchar(20) default NULL,
  `cidade` varchar(25) default NULL,
  `estado` varchar(20) default NULL,
  `bairro` varchar(20) default NULL,
  `cep` varchar(9) default NULL,
  `telefone` varchar(14) default NULL,
  PRIMARY KEY  (`cnpj`),
  UNIQUE KEY `Unique` (`nome`)
) TYPE=InnoDB COMMENT='Tabela para armazenas empresas';

DROP TABLE IF EXISTS `sabesbd`.`servicoprestado`;
CREATE TABLE `servicoprestado` (
  `idPK` bigint(20) unsigned NOT NULL auto_increment,
  `cnpj_clienteFK` varchar(14) NOT NULL default '',
  `titulo` varchar(25) NOT NULL default '',
  `descricao` varchar(25) default NULL,
  `dataInicio` DATETIME,
  `dataTermino` DATETIME,
  `comentario` VARCHAR(255),
  `estado` VARCHAR(20),
  `cidade` VARCHAR(25),
  `bairro` VARCHAR(25),
  PRIMARY KEY  (`idPK`)
) TYPE=InnoDB;

ALTER TABLE `sabesbd`.`servicoprestado` ADD CONSTRAINT 
`FK_servicoprestado_1` FOREIGN KEY `FK_servicoprestado_1` (`cnpj_clienteFK`)
    REFERENCES `empresa` (`cnpj`)
    ON DELETE RESTRICT
    ON UPDATE RESTRICT;

Desde já agradeço!
Thiago Senna

26 Respostas

Mauricio_Linhares

O MySQL normalmente não faz esse tipo de testes (porque as tabelas são, por default, MyISAM, que ainda não suporta isso), mas usando tabelas InnoDB (as que você está usando) e criando os índices manualmente você consegue fazer o banco lidar com isso normalmente. Use uma ferramenta, como o DbDesigner ou vá lá na documentação ver como declarar a criação de índices.

Mas porque você não pode fazer isso no seu Dao? Vai ser só uma linha, pra ver se a propriedade empresa está null, não?

Thiago_Senna

Olá Maurício

Humm… pelo jeito naum venho mechendo com isso… Vou seguir seu conselho! Nem sei o que é índice! Mas já fico contente em saber que o banco pode cuidar disso pra mim!

Só verificar se está nulo? Bom, o que pretendo garantir é que a empresa exista no banco, entaum eu precisaria de algo do tipo

if (empresaDAO.load(empresa.getCnpj()) == null )
     throw new DAOException("Empresa não existe!");

É, até que é pequeno, até que posso colocar isso no dao numa boa… mas particularmente prefiro que o banco faça isso.

Valeu Maurício… Com essas suas colocações já deu pra ajudar bastante!
Thiago Senna

Mauricio_Linhares

Índice

Foreign Keys no MySQL

Referência do InnoDB

No caso da validação do Dao, eu acho que o “ServiçoPrestado” nem deveria chegar no Dao se já não tivesse uma empresa nele, ele já deveria ter sido validado antes disso, o seu Dao deve receber tudo limpinho e bonitinho. Pelo que você falou, ter uma empresa em um “ServiçoPrestado” parece ser uma regra de negócio, se é regra de negócio, deveria ter sido validado na camada de negócio, não no Dao ou no banco de dados.

Thiago_Senna

Olá!

Realmente Maurício, é uma lógica de negócio. No meu modelo nem sei se devo chamar esta relação entre empresa e serviço prestado como relacionamento.

Na verdade eu tenho um ServiçoPrestado que possui uma empresa cliente ( a qual contratou o serviço). O relacionamento no meu modelo acontece entre a empresa e o serviço prestado através da empresa que forneceu o serviço.

Bom… deixe-me ser mais direto e encher menos linguiça!
A classe empresa possui uma coleção de serviços prestados, e dentro da classe serviços prestados possuir uma referência para a empresa que contratou o serviço.

O que eu quero garantir é que serviço prestado não seja persistido caso a empresa que contratou o serviço não exista na base de dados, e não o caso desta empresa que cadastrou o serviço estar nula. É quanto a este tipo de responsabilidade é que quero saber se o BD garante pra mim com a Foreign Key.

Maurício,

Já baixei o DBDesigner, e já vi que vai valer a pena. Também vou estudar os links que você me indicou. Com a ajuda que q você deu já deu para me orientar e por isso vou continuar com o MySQL mesmo no meu projeto de final de curso.

Observação:

Abraços!
Thiago Senna

pcalcado

Thiago,

Em que hipotese voce teria um CNPJ invalido? Voce esta pegando o que o usuario digitou e colocando como foreign key?

Se não, não teria, num caso normal, você ter um CNPJ invalido, ja que ele viria de uma lsita

Se sim, voce tem que fazer essa validação quando o usuario inserir o dado, se nao estiver cadastrado rejeite, ou cadastre, sei la

[]s

Shoes, que esta de 10 ate as 19:40 de um sabado, mesmo nas ferias que começaram ontem, corrigindo problemas em MALDITOS DAOS

Mauricio_Linhares

pcalcado:

Shoes, que esta de 10 ate as 19:40 de um sabado, mesmo nas ferias que começaram ontem, corrigindo problemas em MALDITOS DAOS

Na boa Shoes, eu tava lutando pra fazer o Commons Validator funcionar no Spring, mas vou pegar um cineminha pra descansar, ninguém merece…

Vai dar um passeio também, eu espero que funcione pra mim :mrgreen:

Thiago_Senna

Olá!

Mudando de assunto…

Humm… :roll: Verdade… Essa é aquela maldita mania que assombra todo programdor, que é ficar evitando possibilidades que nunca acontecerão!

Abraços!
Thiago Senna

renatosilva

Maurício,

Obrigado pela informação sobre o InnoDB, temos uma aplicação cujo banco já estávamos pensando em mudar para PostgreSQL por que não conseguíamos criar chaves estrangeiras no MySQL. Só uma pergunta: que impactos eu teria se mudar o bd de MyISAM para InnoDB? Resumidamente você saberia as diferenças? Os perigos? Meu colega tá tentando já converter o banco, mas não tá funfando…

Mauricio_Linhares

A única diferença é que o InnoDB garante mesmo a integridade referêncial por chaves estrangeiras, o que pode estar causando problemas no seu sistema, pode ter alguma coisa que está “quebrando” a integridade dele.

Se você já tem um sistema que roda normalmente com MyISAM, continue usando ele, porque ele é bem mais rápido que o InnoDB, se ainda tá começando, faça tudo em InnoDB.

renatosilva

O sistema tá funfando, mas temos que escolher o banco. Meu colega acabou de informar que conseguiu converter. Não dá pra usar MyISAM porque acho muito necessário que o banco possua integrigade referencial, tornando-o muito mais consistente. Valeu! Obrigado!

renatosilva

Aliás, foi tu que escreveu o tutorial do Hibernate 3? Li uma parte em casa, tá legalzinho, parabéns!

Mauricio_Linhares

Valeu :mrgreen:

Thiago_Senna

Olá Renato,

Quando mudei minha base para InnoDB, uma coisa que me deu uma dor de cabeça e tanto é que nos meus testes unitários haviam um comando truncat nas minhas tabelas.

tipo

truncate table minhaTabela

Esse comando não pode ser usando em tabelas do tipo InnoDB, ou pelo menos, naum pode ser usado durante uma transação! :wink:

Abraços!
Thiago Senna

renatosilva

Obrigado Thiago pela dica. Vou ficar de olho pra ver se esse InnoDB vai se comportar bem hehe. Pelo menos até agora tá legalzinho. O que seria esse truncate? É um comando SQL?

Um problema que já verifiquei e que pode ser por causa da migração para InnoDB é que o MS Access dá um erro fatal quando eu tento exportar tabelas para o MySQL via ODBC, o que antes não acontecia.

Thiago_Senna

Bom renato, truncate é um comando SQL mesmo!

Ele faz algo muito similar ao comando

delete from suaTabela

Mas se não me engano, o truncate é mais poderoso, pois ele afeta a estrutura da tabela, e quando se usa InnoDB deu um erro comigo que custou para descobrir!

A fonte deste texto está aqui: http://netmirror.org/mirror/mysql.com/doc/mysql/pt/innodb-restrictions.html

A fonte está esquisita, mas dá para entender, acho!

Abraços!
Thiago Senna

renatosilva

Esse truncate esvazia a tabela de registros, tipo o delete, acho que a diferença é que ele é um comando mais direto, e portanto mais rápido. Eu usei isso via PHPMyAdmin, aparentemente tá tudo ok…

renatosilva

:shock: Opa! O que é isso?

Num intendi, meu colega fez isso hoje, mas não deu problema nenhum!

Thiago_Senna

renato3110:
:shock: Opa! O que é isso?

Num intendi, meu colega fez isso hoje, mas não deu problema nenhum!

Bom… comigo naum tive este tipo de problema. Talvez dependa da versão do mysql q vc está usando!

Abraços!
Thiagoi

Z

É bom você fazer backup dos dados, quando tá brincando com essas besteirinhas. PRINCIPALMENTE, se for dados em produção.

renatosilva

O próximo passo é descobrir como fazer backup no MySQL, principalmente agendado…

Rodrigo_Carvalho_Aul

Ué… é só agendar uma tarefa no cron que executa uma linhazinha de comando…

[]'s

Rodrigo C. A.

Thiago_Senna

aff…

[off topic + desabafo]
Alguém tá de marcação comigo!
Estão arrancando minhas poucas estrelas que suei tanto para conseguir!

Vêde meus dois posts acima!

O que eu fiz de tão errado???
[off topic + desabafo] :twisted:

renatosilva

Rodrigo, deve ter algum utilitário via shell que posso usar no Linux pra mandar fazer o backup né? Vou pesquisar entaum… valeu.

Rodrigo_Carvalho_Aul

Tá na mão

[]'s

Rodrigo

renatosilva

Valeu Rodrigo, vou dar uma olhada…

W

Caros colegas!
Estou gostando da conversa de vcs! Realmente nos faz pensar um pouco…
Estou com um problema em um projeto.
Estou pegando um sistema de terceiros (desenvolvido em Microsoft SQL Server + Delphi + Cristal Reports) e passando esse sistema para PHP + MySQL.
Missão impossível? Nem tanto…rs
Mas agora que já modelei o novo sistema para ser compatível com o antigo (pois o cliente não pode perder os dados atuais) esbarrei num problema:

Tenho uma tabela chamada empresas, que obviamente tem campos do tipo: codigoEmpresa, razaoSocial, endereco, cnpj, inscricaoEstadual, etc…
O campo codigoEmpresa é auto incremento e chave primária.
O campo cnpj é um índice único.
O campo inscricaoEstadual é um índice único, porém podendo ser nulo.

Quando tentei executar o SQL para importar os dados, uma surpresa desagradável. O programador que desenvolveu o sistema antigo não tomou o cuidado de modelar o banco de dados, de maneira que tenho mais de uma empresa com o mesmo cnpj, causando erro de duplicidade.

Analisando os dados duplicados na chave cnpj, me veio a seguinte indagação:

CNPJ deve ser mesmo usado como campo chave única? :?:

Se existir uma empresa com filiais, cada um terá seu cnpj, ou o cnpj é o mesmo para as filiais? E no caso da inscricaoEstadual e inscricaoMunicipal, como ficam? :?:

Espero contar com a ajuda de vcs!
Forte abraço,
Weber

Criado 8 de julho de 2005
Ultima resposta 2 de jul. de 2008
Respostas 26
Participantes 7