SQL Magazine Edição 27
Esse artigo faz parte da revista SQL Magazine Edição 27. Clique aqui para ler todos os artigos desta edição

Criando um Plugin para o Rational Rose

 

Atualmente, uma das ferramentas mais completas para a construção de diagramas UML é o Rational Rose. Neste artigo, mostrarei como é possível estender o Rose criando um plugin que seja capaz de gerar (a partir de um diagrama de classes) um script SQL contendo o mapeamento OO-relacional. No exemplo em questão, o código SQL foi direcionado para o Oracle, mas nada impede que se utilize qualquer outro banco de dados.

Ao longo das etapas, executaremos um exemplo completo, mas enxuto (sem aqueles detalhes desnecessários à compreensão do mecanismo de extensão do Rational Rose). Analisarei apenas as principais procedures do plugin – você poderá fazer o download do código-fonte completo no site referente a este artigo. Para informações adicionais, é aconselhável consultar a documentação do Rose.

NOTA: para obter mais informações sobre mapeamento OO-relacional, leia o artigo de capa da SQL Magazine 5, do mesmo autor.

A ferramenta de modelagem Rational Rose

O Rose é muito mais que um simples editor de diagramas UML. Boa parte da documentação gerada no processo de análise e projeto pode ser centralizada nessa ferramenta, como, por exemplo, descrição de classes, métodos, casos de uso etc.

A Figura 1 exibe a janela principal do Rose. Nela, podemos observar três áreas principais:

 

a) Navegador de diagramas: permite selecionar um diagrama ou elemento específico;

b) Área de documentação: exibe a documentação do elemento selecionado;

c) Janela principal: onde o diagrama é visualizado.

 

Figura 1 – Diagrama de classes do Rational Rose

imagem

 

Funcionamento do plugin

O Rose disponibiliza interfaces de acesso a elementos do diagrama e tratadores (handlers) para seus eventos internos. Esse mecanismo utiliza a arquitetura COM da Microsoft e consiste na estrutura principal de funcionamento de um plugin (Figura 2).

imagem

 

O Rose permite definir a função a ser chamada quando um determinado evento ocorrer. Essas funções, que na Figura 2 chamamos de Tratador de Evento N, residirão em uma DLL, que será registrada no sistema – tudo de acordo com determinadas regras simples de serem seguidas, que detalharei mais adiante. Alguns exemplos de eventos incluem selecionar uma determinada opção do menu, criar uma nova classe em um diagrama, apagar um relacionamento etc.

Nosso plugin será chamado pelo usuário por intermédio de um menu flutuante, exibido quando clicamos com o botão direito do mouse em um elemento do diagrama. O plugin criará dois novos itens nesse menu: um para gerar o script SQL correspondente à classe selecionada e outro para gerar o script para todo o modelo. Observe que a rotina será a mesma para os dois itens. No segundo caso, o procedimento será executado repetidas vezes, uma para cada classe do diagrama.

Criando e registrando o plugin

Neste exemplo, utilizaremos o Delphi 7, porém o processo de criação do plugin pode ser executado de forma bastante parecida em qualquer outro ambiente. O plugin será uma DLL que contém um objeto COM. No Delphi, uma  biblioteca que contenha objetos COM deve ser criada como uma ActiveX Library.

Para iniciar a construção, abra o Delphi e siga estes passos:

 

1)     Crie uma biblioteca ActiveX: Menu File | New | Other | ActiveX | ActiveX Library e salve o projeto com o nome “MapeamentoOOR”.

2)     Crie um Automation Object: Menu File | New | Other | ActiveX | Automation Object. Na caixa de diálogo, digite o nome “MapeamentoOORCom” (Figura 3). Este objeto COM será alocado dentro da biblioteca criada no passo anterior.

imagem
Figura 3 – Criando um Automation Object

Agora, criaremos os tratadores para os eventos que o nosso plugin interceptará. Esses tratadores consistirão em métodos do objeto COM que acabamos de criar. O Rose disponibiliza diversos eventos, todos documentados nos manuais, no help online e no site da Rational (www.rational.com). Os tratadores de evento deverão ter o nome, os parâmetros e o valor de retorno idênticos aos estabelecidos no manual do Rose. Em nosso exemplo, trataremos os seguintes eventos:

OnActivate: ocorre quando o plugin é ativado pelo Rose;

OnDeactivate: ocorre quando o plugin é desativado;

OnSelectedContextMenuItem: ocorre quando o usuário seleciona um item de menu.

 

3)     Crie os métodos para tratar os eventos:

a.      Abra a janela Type Library Editor, por meio do menu View | Type Library;

b.      No painel esquerdo, selecione IMapeamentoOORCom; clique com o botão direito do mouse, selecione New | Method e digite “OnActivate”;

c.      No painel direito, na aba Parameters, crie um parâmetro com o nome “iRoseApp”, tipo “IDispatch*” e modificador “[in]”. Deixe o tipo de retorno (return type) em “HRESULT”;

d.      Repita o procedimento anterior para os eventos OnDeactivate e OnSelectedContextMenuItem;

e.      Para o evento OnSelectedContextMenuItem, será adicionado mais um parâmetro (além de iRoseApp), denominado “InternalName”, tipo “BSTR” e modificador “[in]” (Figura 4).

imagem

 

Todos esses parâmetros devem seguir o padrão especificado na documentação do Rational Rose (Figura 5).

imagem

 


Figura 4 – Tela Type Library Editor com a especificação do método OnSelectedContextMenuItem


Figura 5 – Documentação do Rational Rose com a descrição dos eventos disponíveis.

 

4)     Clique no botão Refresh Implementation; os métodos recém-definidos serão incluídos na unit do objeto COM. Salve a unit como uMapeamentoOORCom.pas.

Para a alegria dos ansiosos, já de início exibiremos mensagens informando quais eventos foram disparados. Selecione o método OnActivate na unit gerada no passo anterior (uMapeamentoOORCom.pas) e digite o código da Listagem 1. Repita o processo para o método OnDeactivate (não codificamos o método OnSelectedContextMenuItem pois ainda não podemos testá-lo).

 

Listagem 1

uses dialogs;

 

procedure TMapeamentoOORCom.OnActivate(const iRoseApp: IDispatch);

begin

  ShowMessage( 'Hello, world! Plugin MapeamentoOOR Ativado!' );

end;

O próximo passo consiste em registrar a biblioteca COM no sistema operacional. Isso pode ser feito de duas formas:

a) Na janela Type Library Editor, clique no botão “Register Type Library”;

b) Utilize o aplicativo de linha de comando do Windows regsvr32.exe – nesse caso, basta passar o nome da dll como parâmetro (por exemplo, \>regsvr32 NOME da DLL).

Finalmente, é necessário informar o Rational Rose da existência do nosso plugin. Isso é feito no registro do Windows, através destes passos:

1)     Abra o aplicativo regedit (Menu Iniciar do Windows| Executar | regedit).

2)     Selecione a chave “HKEY_LOCAL_MACHINE\SOFTWARE\Rational Software\Rose\AddIns\”.

3)     Crie uma subchave. Para fazer isso, clique com o botão direito do mouse e selecione o menu New | Key; nomeie a subchave como “Mapeamento OOR”.

4)     Dentro da nova chave, crie sete valores, de acordo com a Tabela 1. Para criar um valor do tipo string, clique com o botão direito do mouse e selecione o menu New | String Value (figura 6).

 

Tabela 1

Nome

Valor

Tipo

Active

No

String

InstallDir

C:\PluginRose

String

LanguageAddIn

No

String

ToolDisplayName

Mapeamento OO-R

String

ToolName

Mapeamento OO-R

String

Version

1.0

String

OLEServer

MapeamentoOOR.MapeamentoOORCom

String

5) Ainda dentro da chave Mapeamento OOR, crie uma subchave chamada “Events”. Crie dois valores de acordo com a Tabela 2. Observe neste tabela que não é necessário registrar o evento OnSelectedContextMenuItem (verifique na documentação do Rose quais eventos devem ou não ser registrados).

 

Tabela 2

Nome

Valor

Tipo

OnActivate

Interface

String

OnDeactivate

Interface

String

Pronto! Inicie o Rose, selecione o menu Add-Ins | Add-In Manager e role a lista de plugins até que apareça “Mapeamento OOR”. Clique no check box e aperte o botão apply para ativar o plugin. Nesse momento, será exibida a mensagem “Hello, world! Plugin MapeamentoOOR Ativado!”. Após esse passo, os métodos OnActivate/OnDeactivate do plugin serão executados toda vez que o Rose for iniciado ou finalizado.

imagem
Figura 6 – Registro do Windows

 

Criando itens de menu

Agora que já temos o esqueleto do nosso plugin funcionando, vamos escrever o código responsável pelo mapeamento OO-relacional. É importante notar que, para podermos recompilar o plugin, devemos desativá-lo no “Add-In Manager” ou fechar o Rose. Se não fizermos isso, receberemos a seguinte mensagem de erro do Delphi: “[Fatal Error] Could not create output file MapeamentoOOR.dll”.

Uma vez que nossas rotinas farão referência aos tipos de dados internos do Rose, devemos ter a declaração desses tipos em nosso aplicativo. Na arquitetura COM, isso pode ser feito através de type libraries. Uma type library é um arquivo que contém informações sobre tipos, interfaces e métodos disponibilizados por objetos COM.

Para importar a type library do Rose, clique no menu Project | Import Type Library e selecione a biblioteca “RationalRose (Version X)”, onde X é a versão correspondente do Rose. Clique no botão Install para gerar a unit RationalRose_TLB.pas no diretório imports do Delphi e, depois, clique no botão Cancel para fechar o diálogo. Acrescente a unit RationalRose_TLB na cláusula uses da unit uMapeamentoOORCom.pas.

 

NOTA: o Delphi gera automaticamente uma type library para a ActiveX Library que está sendo criada; para visualizá-la, clique no menu View | Unit e selecione MapeamentoOOR_TLB.

 

Agora, criaremos dois itens de menu, que serão exibidos quando o usuário clicar com o botão direito do mouse em uma classe. Codifique o método OnActivate de acordo com a Listagem 2 (aproveite para remover o ShowMessage ‘Hello World’, já que ele não é mais necessário). Vamos analisar o código:

 

Linha 7: devemos sempre tomar o cuidado de colocar o código entre blocos try-except, a fim de evitar a propagação de exceções do Delphi para o Rose;

Linha 8: conversão do parâmetro iRoseApp, passado como IDispatch, no tipo IRoseApplication. Essa linha de código estará presente em praticamente todos os tratadores de eventos, pois iRoseApp é o ponto de entrada para os demais elementos do Rose;

Linha 9: para criar um item de menu amarrado a um plugin, é necessário antes recuperar um objeto que represente o plugin. Essa linha recupera a coleção de plugins (no Rose, os plugins são chamados também de AddIns);

 

Linha 10: usamos os método FindFirst e GetAt para recuperar uma referência do plugin;

 

Linhas 11 e 12: inserimos duas entradas no menu através do método AddContextMenuItem. O primeiro parâmetro indica em qual tipo de elemento o item de menu irá aparecer: classes, casos de uso etc. Em nosso exemplo, os itens de menu aparecerão quando clicarmos em uma classe (rsClass) do diagrama. Os outros dois parâmetros representam os nomes externo e interno do comando. O nome externo indica o texto que será exibido no item de menu.

imagem

 

A Figura 7 exibe os itens de menu do plugin na tela principal do Rose.

 

NOTA: existe um tipo coleção para cada elemento do Rose: coleção de itens, de classes, de diagramas, de add-ins etc. Todas as coleções começam em 1 e disponibilizam os mesmos métodos básicos: FindFirst, FindNext, GetAt, Exists, Remove, Count etc. O help online oferece uma descrição completa desses métodos.

 

 


Figura 7 – Item de menu do plugin

No método OnSelectedContextMenuItem utilizaremos o nome interno do comando para identificar o item de menu escolhido, conforme o código da Listagem 3 (note que apenas os menus ‘amarrados’ ao plugin serão passados como parâmetro para este método).

 

Rotinas auxiliares

Vejamos agora algumas rotinas importantes para o funcionamento do plugin. Primeiro, devemos adequar os nomes usados nos diagramas às regras de nomeação do banco de dados. Por exemplo, é necessário retirar os espaços em branco e limitar o número de caracteres. Isso é feito na rotina ConformaNome, no código da Listagem 4.

A função PadraoChaveEstrangeira, da Listagem 4, apenas concatena ‘_ID’ ao nome da classe passada como parâmetro. Ela é utilizada na função GerarRelacionamentos (Listagem 6).

Para este plugin, é útil definir uma rotina que faça o mapeamento entre os tipos usados no Rational Rose e os tipos que serão gerados no script do banco de dados. Essa rotina deve receber como parâmetro uma string com o nome do tipo e retornar o tipo equivalente no banco de dados: date continua date, string se torna varchar, integer se torna number e assim por diante. No exemplo, a rotina se chama MapeiaTipo.

 

function ConformaNome( st: string ): string;

begin

  Result := StringReplace(UpperCase(st),' ','_',[rfReplaceAll]);

end;

 

function PadraoChaveEstrangeira( RC: IRoseClass ): string;

begin

  Result := Copy(ConformaNome(RC.Name),1,27) + '_ID';

end;

Listagem 4

 

Gerando o script

         A rotina que gera o script para todo o modelo se chama GerarScript e pode ser visualizada na Listagem 5. Ela será executada quando o usuário clicar no menu ‘GerarScript’ (Figura 7). Vamos analisar os pontos principais do código:

 

Linha 10: a rotina do Delphi PromptForFileName abre uma caixa de diálogo padrão, a fim de que o usuário selecione o nome/localização do arquivo a ser criado para receber o script;

Linhas 12 e 13: usaremos duas listas de strings para gerar o script: uma com os atributos da tabela e outra com os relacionamentos. Optei por duas listas porque, como os comandos de relacionamento fazem referência às tabelas, eles deverão ser gerados no final (linha 25);

Linhas 15 e 16: obtemos a coleção de classes e fazemos um loop para percorrer essa coleção (a propriedade CurrentModel representa o diagrama selecionado). Cada elemento da coleção, obtido com o método GetAt (linha 18), é do tipo IroseClass;

Linha 19: criamos uma tabela para cada classe da coleção. Note que nesse exemplo, para fins de simplicidade, optei pelo mapeamento OO-Relacional onde cada classe corresponde a uma tabela. Em um projeto de plugin mais elaborado, a persistência de cada classe deveria ser opcional - para tal, seria necessário definir uma nova propriedade interna, para que o projetista indicasse se a classe se tornaria ou não persistente;

Linhas 20, 21 e 22: chamamos as rotinas que preenchem o script de geração da tabela.

 

Mapeando relacionamentos

Um relacionamento pode ser mapeado de duas formas: se a cardinalidade for 1:1 ou 1:N, basta criarmos uma constraint de chave estrangeira na tabela apropriada. Se for N:N, devemos criar uma nova tabela auxiliar. Basicamente, isso é feito no procedimento GerarRelacionamentos.

Para facilitar a compreensão dessa rotina, vamos conhecer a estrutura interna da representação de uma classe no Rose. Observe que, na Figura 8, todas as classes são do tipo IRoseClass e possuem, entre outras propriedades, duas coleções, uma de atributos e outra de associações (relacionamentos).

 

imagem

Figura 8 – Representação interna de classe

Cada elemento da coleção de associações possui duas propriedades: Role1 e Role2. Essas propriedades constituem as pontas do relacionamento: uma delas aponta para a própria classe e, a outra, para a classe que está sendo relacionada. Contudo, essa ordem não é fixa. Na Figura 8, Role1 aponta para C1, mas poderia ser o contrário, ou seja, Role2 apontar para C1 e Role1 apontar para C2. O que determina a ordem é a forma como o usuário desenhou o relacionamento no diagrama, por exemplo, se clicou primeiro em C1 ou em C2.

Veja o código completo da procedure GerarRelacionamentos na Listagem 6. Vamos analisá-lo:

 

Linha 7: temos um loop para percorrer a coleção de associações da classe;

Linhas 12 e 13: garantimos que a propriedade Role1 conterá sempre a classe que está sendo processada (Role2 será sempre a outra ponta do relacionamento);

Linhas 15, 22 e 29: descobrimos qual a cardinalidade do relacionamento. Note que os relacionamentos 1 para N e 1 para 0..1 são tratados da mesma forma (linhas 15 e 22), pois a ação é igual para ambos (incluir uma chave estrangeira na tabela que representa a outra ponta do relacionamento). O teste da linha 15 aborda os casos onde a chave estrangeira é obrigatória (cardinalidade igual a 1). Já na linha 22, vemos os casos onde a chave estrangeira não é obrigatória (cardinalidade igual a 0 ou 1). O teste R1.Cardinality <> '1' da linha 22 evita que seja colocada uma chave estrangeira em cada tabela de um relacionamento 1 para 0..1 (lembre-se de que a rotina GerarScript percorre todas as classes do modelo);

 

NOTA: para simplificar, esta versão do plugin não trata do relacionamento 1:1 obrigatório (se isso ocorrer, o script colocará uma chave estrangeira nas duas tabelas – veja linha 15). Observe que, em uma associação 1-1 obrigatória, um objeto não pode existir sem o outro; logo, os objetos relacionados precisam ser criados ao mesmo tempo, o que não é muito comum na prática.

 

Linha 32 até a linha 41: criamos uma tabela auxiliar para implementar os relacionamentos N:N. A tabela possui dois atributos (linhas 33 e 34), que são duas chaves estrangeiras para as classes em R1 e R2 (linhas 35 e 37);

Linha 46: temos a implementação da herança através de fragmentação vertical, ou seja, criamos um relacionamento obrigatório entre a chave da tabela que representa a classe filha e a chave da tabela que representa a classe pai.

Exemplo

 

imagem

Figura 9 – Modelo usado como exemplo

A Figura 9 mostra um modelo de classes de exemplo. Os atributos sem tipo foram mapeados para “varchar2(30)”. A Listagem 7 apresenta o script gerado pelo plugin para as classes Veículo e Multa.

Conclusões

Neste artigo, vimos como é possível estender uma ferramenta de modelagem como o Rational Rose para adequá-la às nossas necessidades. O gasto despendido não é muito grande e certamente será diluído ao longo dos outros projetos realizados – afinal, normalmente não se compra uma ferramenta do porte do Rose para utilizá-la em um único projeto.

Em nosso exemplo, criamos um plugin para realizar o mapeamento OO-Relacional. No entanto, podemos criar plugins para realizar qualquer tarefa a partir de um determinado diagrama. Por exemplo, podemos acessar bancos de dados para armazenar ou recuperar partes do modelo (ou dos dados), gerar código para mais de um banco, gerar scripts para a criação de dados de testes, gerar documentação no Word, criar versões, disponibilizar partes do modelo na Web, etc. Ou seja, podemos estender a ferramenta de forma a integrá-la a qualquer outra aplicação.

Finalmente, cabe ressaltar que para estender outras ferramentas, como o Model Maker, utiliza-se um procedimento bastante semelhante ao que foi mostrado aqui.

 

 

1.  procedure TMapeamentoOORCom.OnActivate(const iRoseApp: IDispatch);

2.  var

3.    RA: IRoseApplication;

4.    AddIns: IRoseAddInCollection;

5.    AddIn: IRoseAddIn;

6.  begin

7.    try

8.      RA := iRoseApp as IRoseApplication;

9.      AddIns:= RA.AddInManager.AddIns;   

10.     AddIn := AddIns.GetAt( AddIns.FindFirst( 'Mapeamento OO-R' ) );

11.     AddIn.AddContextMenuItem( rsClass, 'Gerar Tabela', 'GerarTabela' );

12.     AddIn.AddContextMenuItem( rsClass, 'Gerar Script', 'GerarScript' );

13.   except

14.     ShowMessage( 'Exceção...' );

15.   end;

16. end;

Listagem 2

 

procedure TMapeamentoOORCom.OnSelectedContextMenuItem(

  const iRoseApp: IDispatch; const InternalName: WideString);

begin

  try

    if InternalName = 'GerarTabela' then

      GerarTabela( RoseApp as IRoseApplication )

    else if InternalName = 'GerarScript' then

      GerarScript( RoseApp as IRoseApplication );

  except

    on E: Exception do

      ShowMessage( E.Message );

  end;

end;

Listagem 3

 

           

1.      procedure GerarScript( RA: IRoseApplication );

2.      var

3.        Arquivo: string;

4.        Tabela, Relacionamentos: TStrings;

5.        RCC: IRoseClassCollection;

6.        RC: IRoseClass;

7.        i: integer;

8.      begin

9.        Arquivo := 'script.sql';

10.    if PromptForFileName( Arquivo, '', '', 'Salvar como...' ) then

11.    begin

12.      Tabela := TStringList.Create;

13.      Relacionamentos := TStringList.Create;

14.      try

15.        RCC := RA.CurrentModel.GetAllClasses;

16.        for i := 1 to RCC.Count do

17.        begin

18.          RC := RCC.GetAt( i );

19.          Tabela.Add( 'CREATE TABLE ' + PadraoNomeTabela( RC ) + ' (' );

20.          GerarChavePrimaria( RC, Tabela );

21.          GerarAtributos( RC, Tabela );

22.          GerarRelacionamentos( RC, Tabela, Relacionamentos );

23.          Tabela.Add(');');

24.        end;

25.        Tabela.AddStrings( Relacionamentos );

26.        Tabela.SaveToFile( Arquivo );

27.      finally

28.        Tabela.Free;

29.        Relacionamentos.Free;

30.      end

31.    end;

32.  end;

Listagem 5

 

1.                  procedure GerarRelacionamentos( RC: IRoseClass; Tab, Rel: TStrings );

2.                  var

3.                    i: integer;

4.                    R1, R2: IRoseRole;

5.                    Assoc: IroseAssociation;

6.                  begin // Relacionamentos (1-1 e 1-n) geram atributos

7.                    for i := 1 to RC.GetAssociations.Count do

8.                    begin

9.                      Assoc := RC.GetAssociations.GetAt( i );

10.                  R1 := Assoc.Role1;

11.                  R2 := Assoc.Role2;

12.                  if R1.GetClassName <> RC.Name then

13.                    TrocaValor( R1, R2 );

14.               

15.                  if (R2.Cardinality = '1') then

16.                  begin // Colocar uma chave estrangeira não-nula

17.                    Tab[Tab.Count-1] := Tab[Tab.Count-1] + ',';

18.                    Tab.Add('  '+PadraoChaveEstrangeira(R2.Class_)+' INTEGER NOT NULL');

19.                    Rel.Add('ALTER TABLE '+PadraoNomeTabela(RC)+ ' ADD FOREIGN KEY '

20.                          + 'ID REFERENCES ' +PadraoNomeTabela(R2.Class_)+';');

21.                  end

22.                  else if (R2.Cardinality = '0..1') and (R1.Cardinality <> '1') then

23.                  begin // Colocar uma chave estrangeira nula

24.                    Tab[Tab.Count-1] := Tab[Tab.Count-1] + ',';

25.                    Tab.Add('  '+PadraoChaveEstrangeira(R2.Class_)+' INTEGER');

26.                    Rel.Add('ALTER TABLE '+PadraoNomeTabela(RC)+' ADD FOREIGN KEY ' 

27.                          + 'ID REFERENCES ' +PadraoNomeTabela(R2.Class_)+';');

28.                  end

29.                  else if (pos('n',R1.Cardinality)>0) and (pos('n',R2.Cardinality)>0) and

30.                          (Assoc.Role1.GetClassName = RC.Name) then

31.                  begin  // Criar a tabela de relacionamento

32.                    Rel.Add('CREATE TABLE '+PadraoTabRel(Assoc)+' (' );

33.                    Rel.Add('  '+PadraoChaveEstrangeira(R1.Class_)+' INTEGER NOT NULL, ');

34.                    Rel.Add('  '+PadraoChaveEstrangeira(R2.Class_)+' INTEGER NOT NULL, ');

35.                    Rel.Add('  FOREIGN KEY('+PadraoChaveEstrangeira(R1.Class_)+

36.                            ') REFERENCES '+PadraoNomeTabela(R1.Class_)+',' );

37.                    Rel.Add('  FOREIGN KEY('+PadraoChaveEstrangeira(R2.Class_)+

38.                            ') REFERENCES '+PadraoNomeTabela(R2.Class_)+',' );

39.                    Rel.Add('  PRIMARY KEY('+PadraoChaveEstrangeira(R1.Class_)+','+

40.                    PadraoChaveEstrangeira(R2.Class_)+')');

41.                    Rel.Add(');');

42.                  end;

43.                end;

44.               

45.                // Superclasses serão implementadas como relacionamentos

46.                for i := 1 to RC.GetSuperclasses.Count do

47.                  Rel.Add('ALTER TABLE '+PadraoNomeTabela(RC)+' ADD FOREIGN KEY ID ' +

48.                          'REFERENCES +PadraoNomeTabela(RC.GetSuperclasses.GetAt(i))+';');

49.              end;

Listagem 6

 

 

CREATE TABLE T_VEICULO (

  ID INTEGER NOT NULL PRIMARY KEY,

  RENAVAM VARCHAR2(30),

  PLACA VARCHAR2(30),

  CHASSI VARCHAR2(30),

  MODELO VARCHAR2(30),

  CILINDRADAS VARCHAR2(30),

  PESSOA_ID INTEGER NOT NULL

);

 

CREATE TABLE T_MULTA (

  ID INTEGER NOT NULL PRIMARY KEY,

  NUMERO VARCHAR2(30),

  DATA VARCHAR2(30),

  VENCIMENTO VARCHAR2(30),

  ESTADO_MULTA VARCHAR2(30),

  PESSOA_ID INTEGER NOT NULL,

  TIPOINFRACAO_ID INTEGER NOT NULL

);

 

CREATE TABLE REL_T_VEICULOT_MULTA (

  VEICULO_ID INTEGER NOT NULL,

  MULTA_ID INTEGER NOT NULL,

  FOREIGN KEY(VEICULO_ID) REFERENCES T_VEICULO,

  FOREIGN KEY(MULTA_ID) REFERENCES T_MULTA,

  PRIMARY KEY(VEICULO_ID,MULTA_ID)

);

 

ALTER TABLE T_MULTA ADD FOREIGN KEY(ID) REFERENCES T_PESSOA;

ALTER TABLE T_MULTA ADD FOREIGN KEY(ID) REFERENCES T_TIPOINFRACAO;

ALTER TABLE T_VEICULO ADD FOREIGN KEY(ID) REFERENCES T_PESSOA;

Listagem  7