37
1 Aplica-se ao Caché 5.0.x Pré-requisito para leitura Tutorial T1.x “Conhecendo o Caché” Orientação a Objetos Noções de bancos de dados Plataforma Windows Data 24/03/2004 Versão 2.6 Introdução a Orientação a Objetos no Caché Primeiro devemos enumerar os objetos, para então classificá-los. Só então não temeremos a complexidade, e poderemos nos ater ao que interessa no momento. Amir Samary InterSystems Sales Engineer Introdução A idéia deste tutorial é introduzir o leitor à dimensão orientada a objetos do Caché sem, no entanto, esgotar o assunto, que daria páginas para todo um livro. De forma alguma esta afirmação deve ser interpretada como sinal de complexidade que, na verdade não existe. O que é complexo, é o mundo, o que nos dá muito assunto para discutirmos e muitos exemplos e situações que podem ser exploradas. Quando estou fazendo uma palestra, muitas pessoas se irritam com o fato de eu repetir a toda hora que tudo “é simples!”. Elas pensam “não é possível!”, mas a verdade é que a equipe da InterSystems partiu do zero para chegar a um produto que tivesse a orientação a objetos, o mundo multi-dimensional e o mundo relacional trabalhando juntos, de forma paralela e homogênea. Sem camadas a mais, mapeamentos e configurações. A idéia é pensarmos no que interessa, na nossa aplicação. Lembre-se: Você não precisa ser um mecânico para ir da sua casa até o seu trabalho de carro, basta saber dirigir. Você não precisa conhecer os comandos do SMTP e do POP3 para ler e enviar seus e-mails. Basta usar um software de e-mails apropriado. Veremos como o Caché se comporta com aspectos diferentes da Orientação a Objetos tais como: Herança e Polimorfismo Encapsulamento Composição Relacionamentos entre entidades Veremos também, como o Caché apresenta para o mundo relacional os modelos construídos em sua dimensão orientada a objetos, como criar triggers e como fazer a validação de objetos durante o salvamento dos mesmos no banco de dados. É importante que se leia o Tutorial 1.x, “Conhecendo o Caché”, em sua versão mais recente. Nele, descrevemos o produto de forma geral, apresentando sua arquitetura e sua filosofia. Também apresentamos para o leitor, de forma sucinta, o Caché Studio e o Caché SQL Manager. Neste tutorial, para ver como os conceitos de OO estão dispostos no produto, continuaremos utilizando o namespace USER, que é criado automaticamente durante a instalação, e aprenderemos um pouco de Caché Object Script (COS), criando alguns métodos para nossas classes e utilizando o Caché Terminal, outra ferramenta do produto, para executá- los.

T2.6 Orienta..o a Objetos no Cach. - dc.ufscar.brmarilde/PosGraduacaoLatoSensu/BDOO/UNIARANov... · utilizar estes mesmos métodos via Visual Basic, Java, etc... Herança e compartilhamento

  • Upload
    phambao

  • View
    214

  • Download
    0

Embed Size (px)

Citation preview

1

Aplica-se ao Caché 5.0.x Pré-requisito para leitura Tutorial T1.x “Conhecendo o Caché”

Orientação a Objetos Noções de bancos de dados

Plataforma Windows Data 24/03/2004 Versão 2.6

Introdução a Orientação a Objetos no Caché

Primeiro devemos enumerar os objetos, para então classificá-los. Só então não temeremos a complexidade, e poderemos nos ater ao que interessa no momento.

Amir Samary InterSystems Sales Engineer

Introdução A idéia deste tutorial é introduzir o leitor à dimensão orientada a objetos do Caché sem, no

entanto, esgotar o assunto, que daria páginas para todo um livro. De forma alguma esta afirmação deve ser interpretada como sinal de complexidade que, na verdade não existe. O que é complexo, é o mundo, o que nos dá muito assunto para discutirmos e muitos exemplos e situações que podem ser exploradas. Quando estou fazendo uma palestra, muitas pessoas se irritam com o fato de eu repetir a toda hora que tudo “é simples!”. Elas pensam “não é possível!”, mas a verdade é que a equipe da InterSystems partiu do zero para chegar a um produto que tivesse a orientação a objetos, o mundo multi-dimensional e o mundo relacional trabalhando juntos, de forma paralela e homogênea. Sem camadas a mais, mapeamentos e configurações. A idéia é pensarmos no que interessa, na nossa aplicação. Lembre-se: Você não precisa ser um mecânico para ir da sua casa até o seu trabalho de carro, basta saber dirigir. Você não precisa conhecer os comandos do SMTP e do POP3 para ler e enviar seus e-mails. Basta usar um software de e-mails apropriado.

Veremos como o Caché se comporta com aspectos diferentes da Orientação a Objetos tais como:

• Herança e Polimorfismo

• Encapsulamento

• Composição

• Relacionamentos entre entidades

Veremos também, como o Caché apresenta para o mundo relacional os modelos construídos em sua dimensão orientada a objetos, como criar triggers e como fazer a validação de objetos durante o salvamento dos mesmos no banco de dados.

É importante que se leia o Tutorial 1.x, “Conhecendo o Caché”, em sua versão mais recente. Nele, descrevemos o produto de forma geral, apresentando sua arquitetura e sua filosofia. Também apresentamos para o leitor, de forma sucinta, o Caché Studio e o Caché SQL Manager. Neste tutorial, para ver como os conceitos de OO estão dispostos no produto, continuaremos utilizando o namespace USER, que é criado automaticamente durante a instalação, e aprenderemos um pouco de Caché Object Script (COS), criando alguns métodos para nossas classes e utilizando o Caché Terminal, outra ferramenta do produto, para executá-los.

2

Herança, Persistência e Polimorfismo Herança é um dos conceitos fundamentais da orientação a objetos. Até há pouco tempo

atrás, vários analistas tentavam utilizar herança toda vez que houvesse uma brecha. A idéia era “reaproveitar código e definição”. Percebeu-se depois que a herança não poderia ser utilizada de forma indiscriminada e que somente em casos muito específicos ela poderia ser aplicada com sucesso.

Esta maturidade é essencial para se desenvolver um projeto orientado a objetos sem que, no final, tenhamos uma aplicação complexa, lenta, e etc... Em verdade, toda vez que vejo alguém com dificuldade de entender o Caché, observo que faltam a esta pessoa conhecimentos básicos de orientação a objetos. Conceitos como classe, método, objeto, herança, composição e etc... Portanto, é essencial que se conheça a orientação a objetos para que se possa entender e utilizar o Caché como um banco de dados orientado a objetos.

O Caché suporta herança múltipla e utiliza este conceito para permitir que criemos classes de diversos tipos e com diversos comportamentos. Por exemplo, para criarmos uma classe cujos objetos poderão ser armazenados em disco, devemos herdar de %Library.Persistent. Se quisermos adicionar a esta classe suporte para “população” automática de dados, devemos herdar também de %Library.Populate. Você verá que não é preciso ser um expert em Caché para usá-lo de forma apropriada. Os wizards nos ajudam em muitas tarefas e alguns deles mesmo são indispensáveis. Você não consegue criar uma nova classe no Caché sem utilizar um wizard, por exemplo. Mas depois que a classe é criada, você pode optar por usar ou não o wizard de criação de propriedades.

Tipos de Classes Conforme já foi dito, o Caché utiliza herança para se determinar o tipo de uma classe. Uma

classe, cujos objetos são persistentes, ou seja, armazenados em disco, deve herdar da classe %Library.Persistent. Classes, cujos objetos não precisam de persistência, não precisam herdar de ninguém, mas normalmente herdam de %Library.RegisteredObject. Existem outras classes das quais podemos herdar para que nossas classes tenham outros comportamentos.

Mas para conseguirmos uma classe cujos objetos serão persistentes, não basta apenas herdar de %Library.Persistent. Na definição da classe, existe um parâmetro chamado ClassType que define o tipo da classe. Quando você utilizar o wizard de criação de classes para criar uma nova classe (como fizemos no primeiro tutorial), você verá este atributo. Na tabela abaixo, você pode ver alguns dos tipos de classe que o Caché suporta:

Tipo de classe Descrição Herda de Parâmetros adicionais

Persistente Objetos desta classe poderão ser salvos em disco.

%Library.Persistent ClassType = persistent

Serial Objetos desta classe só podem ser salvos em disco se forem atribuídos a um outro objeto persistente. Por exemplo, um Cliente (persistente) tem um Endereço (serial). É a composição da UML. Veremos mais sobre isto adiante neste tutorial.

%Library.SerialObject ClassType = serial

3

Tipo de dados Classes deste tipo definem novos tipos de dados como os já existentes %String (%Library.String) e %Integer (%Library.Integer).

Você poderia criar um tipo de dado chamado CPF que só permitiria salvar valores de CPF com dígito verificador correto, por exemplo.

Não é preciso herdar de nenhuma classe para se fazer um novo data type. Mas é recomendado herdar-se de um data type já existentes, para estender-lhe as funcionalidades. Poderíamos, para o caso do CPF, por exemplo, herdar de %Library.String.

ClassType = datatype

Tabela 1

Outros tipos de classe são determinados simplesmente através das classes das quais herdamos. Por exemplo, classes CSP (herdamos de %CSP.Page) , WebServices (herdamos de %SOAP.WebService), etc...

No primeiro tutorial, vimos como acessar os nossos dados via consultas ODBC. Agora veremos como trabalhar de forma orientada a objetos no Caché. Trabalharemos com os métodos de nossas classes de uma forma muito simples. Novamente, não criaremos nenhuma aplicação neste tutorial. Simplesmente chamaremos os métodos de persistência utilizando o Caché Terminal. É claro que você poderá avançar o sinal e olhar na documentação do Caché como utilizar estes mesmos métodos via Visual Basic, Java, etc...

Herança e compartilhamento de dados Vamos criar uma classe chamada empresa.Pessoa, persistente, com apenas uma única

propriedade, o Nome da pessoa, que deverá ser obrigatório. Depois de definir o tipo da classe como persistente, no wizard de criação de classes, não clique em Finish como fizemos no primeiro tutorial. Clique em Next e marque a opção que fala sobre suporte a população automática de classes. A definição de sua classe deverá estar como a definição abaixo:

Class empresa.Pessoa Extends (%Persistent, %Populate)

[ ClassType = persistent, ProcedureBlock ]

{

Property Nome As %String [ Required ];

Index NomeIndex On Nome;

}

Repare que o fato de você ter dito para o wizard que você desejava que a sua classe tivesse suporte a população automática de dados, fez com que o wizard escrevesse a sua classe de forma que ela herdasse não só de %Library.Persistent como também de %Library.Populate. Neste ponto, você já deve ter entendido que %Library.Persistent e %Persistent são equivalentes e a mesma coisa. A mesma coisa acontece com %Populate e %Library.Populate. Isto ocorre por motivos de compatibilidade retroativa. Bom, graças a

4

herança múltipla, agora temos métodos herdados das duas classes, e os utilizaremos em breve. Agora compile a sua classe pressionando CTRL+F7.

Se, quando você tentar compilar a sua classe, você receber uma mensagem de erro do tipo “Objetos da classe ‘classe.Pacote’ estão instanciados em n processos”, é porque existe alguma aplicação (escrita em VB, Java, etc...) ou um ou mais Caché Terminals, que está mantendo objetos de sua classe abertos. Feche a aplicação para compilar a sua classe ou, caso os objetos tenham sido abertos em um Caché Terminal por você, feche o Caché Terminal ou entre com o seguinte comando:

USER>Kill

Este comando apaga todas as variáveis em memória que você criou nesta seção de terminal, fechando, conseqüentemente, qualquer objeto que você tenha deixado aberto.

Esta dica será mais útil quando você começar a utilizar o Caché Terminal, nas próximas seções.

Depois de compilar a sua classe, clique na opção de menu Visualizar > Exibir Documentação da Classe. Esta opção, ao ser clicada, abrirá um browser que apontará para o servidor de HTTP interno do Caché, que está escutando requisições na porta 1972 (porta padrão do Caché para todas as aplicações). Você verá que toda a documentação de sua classe está disponível automáticamente. Se você forneceu descrições para as suas classes e propriedades, elas aparecerão na documentação também. Em um futuro tutorial, desenvolveremos uma aplicação Web completa com o Caché, e você verá que o Caché é um excelente Application Server.

Mas neste momento, quando você estiver visualizando a documentação, você verá que no lado esquerdo da tela existe uma lista de pacotes de classes navegável. Você não só pode clicar nos links da documentação de sua própria classe, como também pode clicar em outras classes, de outros pacotes para saber mais sobre elas.

Agora vamos trabalhar um pouco com herança. Vamos criar uma nova classe, chamada empresa.Cliente que herda de empresa.Pessoa e tem uma propriedade a mais, o CPF, que será do tipo %Integer, obrigatória e única (chave). A sua classe deverá se parecer com o código abaixo:

Class empresa.Cliente Extends empresa.Pessoa

[ ClassType = persistent, ProcedureBlock ]

{

Property CPF As %Integer [ Required ];

Index CPFIndex On CPF [ Unique ];

}

Figura 1

5

Para terminar, crie agora uma nova classe empresa.Fornecedor da mesma forma. Só que ao invés de termos um CPF, teremos um CNPJ:

Class empresa.Fornecedor Extends empresa.Pessoa

[ ClassType = persistent, ProcedureBlock ]

{

Property CNPJ As %Integer [ Required ];

Index CNPJIndex On CNPJ [ Unique ];

}

Pronto, agora temos duas classes herdando de empresa.Pessoa. Não esqueça de compilar estas duas novas classes (CTRL+F7)! Vamos agora utilizar o Caché Terminal para criar alguns objetos. Utilizaremos métodos herdados da classe %Library.Persistent, portanto se você quiser dar uma olhada na documentação de sua classe e navegar para a classe %Persistent, sinta-se à vontade e eu recomendo. Mas recomendo mais ainda que você leia a documentação do produto. Se quiser um bom lugar para começar, veja no final deste tutorial, algumas referências para partes da documentação do Caché que valem a pena ler.

Abra o Caché Terminal clicando no Cubo do Caché e escolhendo a opção Caché Terminal:

Já comentamos sobre o Caché Terminal no primeiro tutorial. A idéia é que você possa trabalhar de forma bastante interativa com o Caché, testar métodos, criar objetos, abrir objetos, fazer consultas SQL, etc... Repare que ao chamá-lo, ele já se abre no namespace USER, que é

Figura 2

6

onde criamos nossas classes. Se você criou um namespace para você, e criou suas classes neste novo namespace, você terá que trocar de namespace com o seguinte comando:

USER>ZN “SEUNAMESPACE”

Ou seja, utilize o comando ZN e passe o nome de seu namespace entre aspas. Uma vez que você estiver no namespace correto, vamos começar a criar alguns objetos e fazer alguns testes para ver como o Caché se comporta com herança.

Primeiro, vamos criar um objeto da classe, empresa.Cliente. No Caché Terminal, digite:

USER>Set objCli = ##class(empresa.Cliente).%New()

USER>Write

objCli=<OBJECT REFERENCE>[[email protected]]

USER>

Estamos utilizando um pouco de Caché Object Script (COS) aqui. Se você quiser saber mais sobre o COS, veja na seção de referências o item “Introdução ao Caché Object Script”. O COS é uma linguagem de script muito poderosa, porém simples. Na primeira linha, utilizamos a sintaxe ##class(nome da classe) que nos permite executar métodos de classe de uma determinada classe que passamos como parâmetro. Neste caso, executamos o método %New() da classe empresa.Cliente. Nós herdamos este método da classe %Persistent. O método %New() cria um novo objeto da classe e retorna-o para nós na variável objCli.

O comando Write, quando chamado sem parâmetros como fizemos, imprime na tela todas as variáveis que criamos em nosso processo. Neste caso, você pode ver que a única variável que criamos foi um objeto da classe empresa.Cliente. O que você está vendo na tela é o que chamamos de OREF (Object Reference). Para você ver a diferença entre um OREF e uma variável comum, faça o seguinte teste:

USER>Set minhaVar=”Joao da Silva”

USER>Write

minhaVar=”Joao da Silva”

objCli=<OBJECT REFERENCE>[[email protected]]

USER>

Agora vamos preencher as propriedades de nosso objeto:

USER>Set objCli.Nome=”Amir Samary”

USER>Set ok = objCli.%Save()

USER>Do ##class(%SYSTEM.Status).DisplayError(ok)

7

ERROR: #5659: Propriedade ‘CPF’ obrigatória

USER>Set objCli.CPF=123456789

USER>Set ok = objCli.%Save()

USER>Do $system.Status.DisplayError(ok)

USER>Write ok

1

USER>Write objCli.%Id()

23

USER>Write objCli.%ClassName(1)

empresa.Cliente

USER>Write objCli.%Oid()

�23�empresa.Cliente

Vamos lá... Tentamos salvar o nosso novo cliente sem informar o seu CPF, que é uma propriedade obrigatória. Chamamos para tanto, o método %Save() que tenta salvar o objeto em questão e quaisquer outros objetos alterados para os quais ele aponta, dentro da mesma transação. Eu digo “tenta salvar” pois, caso haja algum problema durante o salvamento, todas as alterações feitas em disco são desfeitas (rollback da transação) e o erro é retornado pelo método. Por isto, atribuímos o retorno do método %Save() à variável ok. Se ok for diferente do valor 1 (um), temos um erro. Para saber qual é o erro, podemos chamar o método DisplayError() da classe %SYSTEM.Status. Para nos facilitar, este método está mais acessível através da variável de ambiente $system. Eu chamei o mesmo método de duas formas diferentes para que você veja que é a mesma coisa. Você pode chegar até a documentação da classe %SYSTEM.Status navegando, através da documentação de suas próprias classes, navegando pela lista de pacotes a esquerda e procurando pelo pacote %SYSTEM. Ou você pode utilizar a opção de menu do Caché Studio: Ferramentas > Examinador de Classes. Em um tutorial futuro, falarei sobre o tratamento de erros com o Caché.

Bom, os últimos três comandos que executei merecem uma atenção especial. O método %Id(), o qual também herdamos de %Persistent, nos retorna o ID de um objeto. Já disse que todo objeto em memória possui o que chamamos de OREF. Uma referência em memória do objeto. Mas quando este objeto é salvo em disco, ele ganha o que chamamos de ID. O ID de um objeto identifica um objeto de forma única dentro de sua classe. Isto é, podemos ter dois objetos com o mesmo ID em classes diferentes, mas nunca na mesma classe. Além do OREF e do ID, temos também o conceito do OID. O OID nada mais é do que o ID do objeto junto com o nome de sua classe (retornado pelo métdo %ClassName()). Ou seja, uma referência completa a um objeto. Não podem existir em um mesmo namespace dois objetos com o mesmo OID. Executamos o método %Oid() para retornar o OID de nosso objeto recém criado. Por enquanto, não se preocupe com estes detalhes. Basta saber o que eles significam. O conceito mais utilizado será o de ID. Saber qual é o nome da classe de um objeto ou trabalhar diretamente com OIDs é muito útil quando estamos trabalhando com polimorfismo. Todos estes métodos foram herdados da classe %Library.Persistent.

Agora crie mais alguns objetos da classe empresa.Cliente e empresa.Fornecedor para praticar um pouco. Não se engane. Pratique. Vale a pena. Como você viu, no primeiro tutorial, você pode utilizar o Shell de SQL para inserir comandos SQL através do Caché Terminal. Você pode experimentar isto também, mas tente criar vários objetos de forma orientada a objetos,

8

para você praticar. Tente não informar o Nome de um cliente, por exemplo, e veja o que acontece...

Agora que você criou e salvou vários objetos de suas duas classes, abra o Gerenciador de SQL e clique na sua tabela. A primeira coisa que gostaria que você notasse é a existência da coluna ID que reflete exatamente o que o método %Id() retornaria para um objeto. Outra coluna “estranha” que aparece é a x__classname, da qual falaremos mais tarde e reflete exatamente o que o método %ClassName(1) retornaria se este fosse chamado em um dos objetos da classe.

Experimente agora fazer algumas consultas SQL nas suas tabelas. O que você acabará percebendo é uma das coisas que tornam o Caché algo revolucionário, e isto é apenas o começo. Você verá que o Caché implementa herança também em seu engine relacional. Se você fizer consultas SQL na tabela empresa.Pessoa, você verá tanto Clientes quanto Fornecedores. Se você fizer consultas na tabela empresa.Fornecedor, você verá apenas fornecedores. Isto é o que chamamos de compartilhamento de dados em função da herança. Não é necessário nenhum artifício para se utilizar herança com o Caché. Enquanto em outros bancos de dados você teria que simular o conceito com algum certo trabalho, tendo que fazer alguns joins e guardando em uma coluna a mais o tipo de dado do objeto, aqui tudo é simples, direto e sem desperdício de dados. Se você fizer um count das linhas na tabela de clientes e fornecedores, você verá que a soma será igual ao count das linhas da tabela pessoa. Clientes e fornecedores são pessoas com atributos a mais!

Este comportamento é opcional. Você poderia herdar as propriedades da classe empresa.Pessoa, sem no entanto permitir que sua classe compartilhe os dados com ela. Basta para isto que a sua classe filha (Fornecedor, por exemplo) herde de %Persistent e, depois, herde de empresa.Pessoa. Por exemplo, a classe abaixo, empresa.Diretor não compartilha seu armazenamento com empresa.Pessoa:

Class empresa.Diretor Extends (%Persistent, empresa.Pessoa)

[ ClassType = persistent, ProcedureBlock ]

{

Property Salario As %Integer [ Required ];

}

Experimente criar esta classe e fazer alguns inserts e selects em todas as suas classes para ver como elas se comportam. Você verá que empresa.Diretor herdou a estrutura e métodos, mas não compartilha dados com empresa.Pessoa. Preste atenção ao parâmetro ClassType, ele deve estar lá para que você consiga compilar a sua classe! Repare, portanto, que a ordem de especificação das classes das quais herdamos influencia o resultado final.

Figura 3

9

Populate Existe uma maneira mais fácil de criarmos uma base de dados de testes. Existe uma forma

de criarmos, de forma automática e instantânea, vários objetos para uma determinada classe. Para tanto, basta fazer sua classe herdar de %Library.Populate.

Quando criamos nossa classe empresa.Pessoa, nós pedimos para que o Caché Studio adicionasse à nossa classe, o suporte a população automática de dados. Isto foi feito, fazendo com que a nossa classe herdasse de %Library.Populate. Como todas as outras classes (Cliente, Fornecedor e Diretor) herdam de Pessoa, todas elas também herdam de %Library.Populate:

Class empresa.Pessoa Extends (%Persistent, %Populate)

….

Esta classe nos dá o método Populate(quantos, verbose) que quando chamado, criará quantos objetos tivermos pedido. Se passarmos o valor 1 (um) para o segundo parâmetro, o método nos dirá se conseguiu ou não salvar cada um dos objetos e uma mensagem de erro quando for o caso. No Caché Terminal, digite o seguinte comando para criar mais 100 fornecedores:

USER>Do ##class(empresa.Fornecedor).Populate(100)

Faça o mesmo para clientes e diretores. Experimente passar 1 (um) como segundo parâmetro para ver o que acontece:

USER>Do ##class(empresa.Cliente).Populate(100,1)

Depois faça algumas consultas via Gerenciador de SQL ou Shell SQL do Caché Terminal para ver o efeito disto.

Desta forma, você consegue fazer muitos testes interessantes com volumes maiores de dados.

Polimorfismo Para terminar este tópico, vamos ver um pouco de polimorfismo. Você tem vários

fornecedores e clientes em sua base de dados. Todos eles são pessoas. Se você quisesse fazer um relatório onde constassem todas as pessoas de sua empresa, sem interessar o fato de elas serem clientes ou fornecedores, o que você faria? Você já viu que um simples select na tabela de pessoas faria o serviço. Aqui, você já está vendo um pouco de polimorfismo.

Agora digamos que você quisesse executar um outro relatório onde você quer mostrar quantos fornecedores e clientes existem em sua empresa. O que você faria?

Conforme já observamos em tópicos anteriores, na visão relacional existe uma coluna especial chamada x_classname, que pode te dizer exatamente de qual classe pertence o objeto que está sendo exibido em cada linha do resultado da query. Esta coluna não aparece por default, você precisa pedir:

Select x_classname as Tipo, count(*) as Quantos

10

from empresa.Pessoa

group by x__classname

Desta forma, se você estivesse percorrendo um ResultSet (via JDBC por exemplo) e, para cada linha retornada, você quisesse determinar seu tipo (classe) para fazer algum tipo de processamento específico, esta coluna escondida é uma boa solução.

Agora vamos ver como faríamos a mesma coisa no mundo orientado a objetos. A classe %Library.Persistent nos dá vários métodos que já usamos aqui. Vamos fazer alguns testes com o %ClassName() e descobrir o que ele faz e como ele pode nos ser útil. Abra o Gerenciador de SQL e faça a seguinte query:

Select * from empresa.Cliente

Anote um dos IDs retornados. Este é um ID de um cliente de sua base. Normalmente, faríamos uma query para obter este ID, mas vamos pular este passo por enquanto. Simplesmente assuma que você recebeu este ID de um processamento anterior qualquer. Suponha que o ID retornado seja igual a 30 (trinta). Abra um Caché Terminal e vá para o seu namespace e faça o seguinte:

USER>Set obj=##class(empresa.Pessoa).%OpenId(30)

USER>If obj’=”” { Write obj.%ClassName()} else { Write “Não existe!” }

Cliente

USER>If obj’=”” { Write obj.%ClassName(1)} else { Write “Não existe!” }

empresa.Cliente

USER>If obj.%ClassName()=”Cliente” { Write “CPF:”,obj.CPF } else { Write “CNPJ:”,obj.CNPJ }

CPF:4545454545

Estamos utilizando um novo método da classe %Persistent, o método %OpenId(id).

Este método recebe um ID e tenta abrir o objeto que possui aquele ID. Poderíamos ter chamado este método diretamente na classe empresa.Cliente, já que já sabíamos que o ID 30 pertencia a um cliente. Mas ai não teria graça! Quero mostrar a você um pouco de polimorfismo. Já que um cliente é uma pessoa, vamos abri-lo através da classe empresa.Pessoa! Nós sabemos que este objeto existe, mas poderíamos não saber, é por isto que estou verificando se o que o método %OpenId() me retornou é igual à string nula ou não. Repare que usei uma aspa simples para fazer a negação da igualdade (veja na seção de referências, os links para a parte da documentação do Caché que fala de Caché Object Script).

Como sabemos que o objeto existe, sabemos que o método %ClassName() será executado. Este método, quando chamado sem parâmetros, retorna apenas o nome da classe. Se passarmos 1 (um) para ele como primeiro parâmetro, ele exibirá o nome completo da classe, incluindo o pacote. É por isto que repeti o comando, só que desta vez chamando %ClassName(1).

Agora precisamos imprimir a propriedade CPF do cliente ou o CNPJ do fornecedor. Então perguntamos para o objeto quem ele é e, de acordo com a resposta, imprimimos a propriedade

11

correta. Como neste caso, o objeto cujo ID é 30 é um cliente, imprimiremos um CPF. Experimente executar este exemplo novamente utilizando um ID de um fornecedor ou um ID que não existe.

Encapsulamento e Polimorfismo O encapsulamento é um dos conceitos mais importantes da orientação a objetos. A idéia é

que o mundo exterior não precise ver ou compreender a complexidade de nossos componentes. Ele simplesmente vê uma interface que fornece uma série de serviços. Vamos começar com um exemplo simples de encapsulamento, a criação de métodos. Sim, o conceito de método na orientação a objetos foi criado, entre outras razões, para disponibilizar serviços ao mundo exterior, cuja lógica é restrita à classe do método. Depois vamos analisar como o Caché nos permite lidar com propriedades.

Um método de Instância Um método de instância é um método que só deve ser chamado a partir de uma instância

de uma classe, um objeto. Por exemplo, o método %Save() é um método de instância. Você salva objetos. Não faria sentido algum chamar o método %Save() a partir de uma classe. Já o método %OpenId() é um método de classe. Você deve executá-lo a partir de uma classe, usando a sintaxe ##class().

Vamos agora criar um método de instância simples, para demonstrar um pouco de encapsulamento e ao mesmo tempo demonstrar como se criam métodos de instância. Abra a sua classe empresa.Pessoa. Clique com o botão direito do mouse no fonte de sua classe e escolha a opção Adicionar > Novo Método.

O nome do método será RecuperarIdentificacao. Sem acentos ou cedilhas. Clique em Next e informe %String como Tipo de Retorno do método. Como ele não terá nenhum argumento, simplesmente clique em Next. Os três checkbox deverão estar desmarcados. Tenha certeza de que a Linguagem do método é cache. Você verá que poderíamos escrever nosso método em VBScript colocando a linguagem como basic. Mas vamos escrevê-lo em Caché Object Script mesmo, pois temos usado COS no Caché Terminal até agora e não vamos começar a nos confundir, certo?

Agora você pode clicar em Finish. O Caché deverá acrescentar o seguinte código à sua classe:

Method RecuperarIdentificacao() As %String

{

}

Vamos agora colocar código neste método. Ele deverá ficar assim:

Method RecuperarIdentificacao() As %String

{

Quit “” }

Figura 4

12

Como assim? Simples, uma pessoa não tem identificação! Apenas clientes e fornecedores a possuem! Mas nossas três classes, Cliente, Fornecedor e Diretor, agora já possuem este mesmo método. Isto é uma forma simples de se definir uma interface de serviços que deverão ser especializados por cada uma das classes filhas. O que devemos fazer agora é especializar este método onde for necessário. Copie o código deste método e cole-o na classe empresa.Cliente. Altere-o para que fique assim:

Method RecuperarIdentificacao() As %String

{

Quit ..CPF

}

Cuidado, ao copiar e colocar código pois durante o processo, ele pode ser alterado. Aspas duplas, por exemplo, normalmente devem ser redigitadas após a colagem.

Depois da colagem, selecione todo o código colado, já no Caché Studio, e pressione a tecla TAB para movê-lo para frente. O Caché Studio exige que o código não fique colado na margem esquerda da página.

Faça a mesma coisa com Fornecedor, só que retorne CNPJ ao invés de CPF. Repare no uso dos dois pontinhos “..” antes do nome da propriedade. Lembre-se que este método será chamado através de um objeto, uma instância. Os dois pontinhos indicam que queremos o valor armazenado na propriedade CPF desta instância. É como o this do Java ou o Me do VB. Não se esqueça de compilar suas classes, inclusive a classe empresa.Diretor que também ganhou nosso novo método!

Agora podemos abrir qualquer objeto da classe empresa.Pessoa, sem saber se ele é um Cliente ou um Fornecedor, e executar o método RecuperarIdentificacao() para obter a identificação do objeto, seja ela um CPF ou um CNPJ! Vamos fazer um teste então. Faça algumas queries usando o Gerenciador de SQL e obtenha um ID de um Cliente, um Fornecedor e de um Diretor. Abra o seu Caché Terminal e execute os seguintes comandos:

USER>Set obj=##class(empresa.Pessoa).%OpenId(30)

USER>Write obj.RecuperarIdentidade()

4545454545

USER>Set obj=##class(empresa.Pessoa).%OpenId(101)

USER>Write obj.RecuperarIdentidade()

555555555

USER>Set obj=##class(empresa.Diretor).%OpenId(2)

USER>Write obj.RecuperarIdentidade()

Verifique se as identidades retornadas estão de acordo com o CPF ou CNPJ dos objetos que

você abriu. Repare que eu também chamei o método RecuperarIdentidade() em cima de um

13

objeto da classe empresa.Diretor. Nós não o especializamos nesta classe, portanto ele sempre retornará a string nula, conforme definimos em empresa.Pessoa, e conforme aconteceu no exemplo acima.

Assim, você acabou de ver como criar um método de instância simples em uma classe. Você pode agora criar seus próprios métodos utilizando lógicas mais complexas e utilizando todos os recursos de OO que o Caché oferece. Estes métodos poderão ser acessados via Java, ActiveX, WebServices, e etc... Sem qualquer trabalho de mapeamento para estas linguagens! Tutoriais futuros mostrarão como isto pode ser feito. Mas o melhor de tudo é que é tudo muito simples!

Propriedades e Propriedades Calculadas As pessoas costumam estranhar quando, com o Caché, acessamos as propriedades dos

objetos diretamente, ao invés de usarmos métodos de acesso como “setNome()” ou “getNome()”. Mas para surpresa de todos, eu digo que usamos métodos de acesso o tempo todo. Quando fazemos:

USER>Set objCli.CPF=1234567

O Caché, internamente interpreta isto como:

Set ok=objCli.CPFSet(1234567)

Apesar de você poder chamar este método diretamente, é extremamente aconselhável que você acesse as propriedades do objeto diretamente e deixe estas questões para o Caché. Se a sua classe possui uma propriedade CPF, como a nossa classe empresa.Cliente, haverá um método implícito CPFSet e outro CPFGet.

Como você já deve ter imaginado, nós podemos sobrepor estes métodos para realizar algum tipo de processamento nos dados antes de aceitá-los (Set) ou de devolvê-los (Get). Mas não faremos isto neste tutorial.

Veremos que quando projetamos nossas classes Caché para classes Java, por exemplo, existirão métodos set e get para cada uma das propriedades de nossa classe. Portanto, a sua propriedade deve se chamar “Nome”, com “N” maiúsculo e não “nome” como em Java. Lá no Java, você verá que existirá um método “setNome”. Se você escrever o nome de sua propriedade como “nome”, o método no Java será projetado como “setnome”, assim mesmo, tudo minúsculo. Portanto, siga as convenções que eu adotei aqui que tudo vai ficar perfeito no mundo Java e no mundo Microsoft.

Criando um método de classe (para ajudar a validar nossos objetos) Existem dois tipos básicos de métodos:

• Métodos de classe – Métodos que são executados a partir da classe, como os métodos %New, %OpenId, %DeleteId, Populate, etc... Veja o caso do %OpenId, por exemplo. Não faz sentido pedirmos para um objeto abrir outro objeto. Se quisermos abrir um objeto específico, pedimos isto para sua classe. Por isto o método %OpenId é um

14

método de classe. Métodos de classe não necessariamente interagem com objetos de sua classe. É comum termos classes utilitárias com um punhado de métodos de classe que fazem diversas coisas diferentes.

• Métodos de instância – Métodos que são executados a partir de uma instância da classe (objeto). Normalmente estes métodos executam alguma atividade específica em apenas um objeto da classe como os métodos %Save e %Close. Veja o caso do método %Save, por exemplo. Se quisermos salvar um objeto específico, pedimos isto para ele (salve-se!). Não faz sentido pedirmos para a classe salvar um objeto assim como não faria sentido pedirmos para uma classe fechar um objeto. Sentido pode até fazer, mas faz mais sentido pedirmos para o objeto se salvar, pois ele sabe o que ele alterou e sabe o que deve ser feito. São questões relativas a encapsulamento e distribuição de responsabilidades.

Vamos criar, como exemplo, um método para validar CPFs e usá-lo para validar nossa propriedade CPF. Para tanto, criaremos um método de classe em nossa classe empresa.Cliente chamado ValidaCPF. Este método recebe um CPF como um número inteiro e devolve verdadeiro ou falso, isto é, diz se o CPF é válido ou não. Teoricamente, criaríamos este método em uma classe utilitária, criando assim uma biblioteca de classes que estaria disponível para todo o nosso sistema. Mas neste caso, para manter as coisas simples, criaremos este método na nossa classe Cliente mesmo.

Portanto, crie na classe empresa.Cliente um método de classe com o seguinte código fonte (você pode copiar e colar diretamente na classe – é aconselhável):

ClassMethod ValidaCPF (cpf As %Integer) As %Boolean { // Não existe CPF com mais de 11 dígitos If $Length(cpf)>11 Quit 0 // Completar os zeros a esquerda If $Length(cpf)<11 Set cpf=$Translate($Justify(cpf,11)," ","0") // Calcular dígitos verificadores Set soma1= ($Extract(cpf,1)*10) + ($Extract(cpf,2)*9) + ($Extract(cpf,3)*8) + ($Extract(cpf,4)*7) + ($Extract(cpf,5)*6) + ($Extract(cpf,6)*5) + ($Extract(cpf,7)*4) + ($Extract(cpf,8)*3) + ($Extract(cpf,9)*2) Set resto1=soma1\11 Set multiplica1=resto1*11 Set resto=soma1-multiplica1 If ((resto=0) ! (resto=1)) { Set dig1=0 } Else { Set dig1=11-resto } Set soma2= ($Extract(cpf,1)*11) + ($Extract(cpf,2)*10) + ($Extract(cpf,3)*9) + ($Extract(cpf,4)*8) + ($Extract(cpf,5)*7) + ($Extract(cpf,6)*6) + ($Extract(cpf,7)*5) + ($Extract(cpf,8)*4) + ($Extract(cpf,9)*3) + (dig1*2)

15

Set resto2=soma2\11 Set multiplica2=resto2*11 Set resto2=soma2-multiplica2 If ((resto2=0)!(resto2=1)) { Set dig2=0 } Else { Set dig2=11-resto2 } If ($Extract(cpf,10)=dig1) && ($Extract(cpf,11)=dig2) { Quit 1 // Tudo ok! } Quit 0 // Se chegarmos aqui, é porque é inválido }

Não vou detalhar o funcionamento deste método. Fica como exercício de casa. Se você ainda não fez, trate de fazer o tutorial de COS que acompanha o Caché. Veja no final deste documento, a nossa seção de referências. Bom, compile a sua classe e teste o seu método. Isto é, abra um Caché Terminal e chame o seu método de classe passando um CPF válido e um CPF inválido e imprima o resultado:

USER>Write ##class(empresa.Cliente).ValidaCPF(12345678)

0

USER>Write ##class(empresa.Cliente).ValidaCPF(22222222222)

1

Sim, 11 números iguais passam pela fórmula. Eu desafio você a mudar o método ValidaCPF para tratar deste caso.

16

Validando o Objeto (usando um método de instância herdado) Agora que temos um método que nos diz se um CPF é válido ou não, podemos usá-lo para

validar nosso objeto. Para tanto, vamos implementar um método herdado de %Persistent chamado %OnValidateObject(). Abra a classe empresa.Cliente e clique no menu Classe da barra de menu. Escolha a opção Override e ache o método %OnValidateObject na lista que lhe aparece:

Depois de pressionar o botão Ok, observe que o Caché já trouxe a assinatura do método para dentro de sua classe. Esta é uma maneira bem prática de se sobreescrever métodos herdados, pois evita que você erre o nome do método ou seus argumentos. O nosso código de validação ficará assim:

Method %OnValidateObject() As %Status [ Private, ProcedureBlock = 1 ]

{

If '..ValidaCPF(..CPF)

{

Quit $system.Status.Error(5001,"CPF inválido!")

}

Quit $system.Status.OK()

}

Figura 5

17

O código não poderia ser mais simples, certo? Na primeira linha já validamos a nossa propriedade CPF e se encontrarmos um erro, retornamos um %Status, conforme definido na assinatura de nosso método de validação. Para criarmos este status, chamamos um método do objeto $system.Status (da classe %SYSTEM.Status) chamado Error. Este método recebe um código de máscara de erro e várias strings para preencher esta máscara. O código 5001 significa que não há mascara. Ou seja, a primeira string que informarmos em seguida será a própria mensagem de erro. Para saber sobre os códigos de máscara de erro disponíveis, dê uma olhada na seção de referências no final deste documento no item Códigos de Máscaras de Erros.

Repare que recuperamos o CPF do objeto atual através da sintaxe:

..CPF

E repare que fazemos a mesma coisa para executar um método de nossa própria classe:

If ..ValidaCPF(..CPF)

{

...

Os dois pontos consecutivos significam que desejamos executar um outro método de nossa própria classe ou recuperar uma propriedade do objeto. É como o this do Java ou o Me do VB. Esta linha poderia ser escrita como:

If ##class(empresa.Cliente).ValidaCPF(..CPF)

Mas a sintaxe compacta é útil não só para manter o código menor, mas também é muito útil quando estamos lidando com polimorfismo e herança. Mas não vou me aprofundar nesta discussão agora.

Mas se não encontrarmos erros nas nossas propriedades, retornamos o código de status correspondente à situação de ausência de erros. Este código é retornado pelo método OK() do objeto $system.Status.

Pronto! Agora tente criar um objeto da classe cliente com um CPF inválido:

USER>Set objCli = ##class(empresa.Cliente).%New()

USER> ; Preencha todas as propriedades de seu objeto aqui…

USER>Set objCli.CPF=34343434

USER>Set ok = objCli.%Save()

USER>Do $system.Status.DisplayError(ok)

ERROR: #5001: CPF inválido!

USER>

18

Altere a propriedade CPF para um CPF válido e tente salvar de novo. Verifique a variável ok para ver se tudo deu certo e depois faça uma query SQL para verificar se a propriedade está com o valor correto. Pronto! Agora não é mais possível salvar um objeto com o CPF inválido!

Triggers e métodos (validando o lado relacional também) Mas nós ainda temos um problema. Esta validação só ocorre no lado orientado a objetos

do Caché. Se fizermos um insert via ODBC, com um CPF inválido, o Caché não vai reclamar. Isto ocorre porque o nosso método %OnValidateObject é um método de instância, isto é, ele depende de uma instância de um objeto para ser executado. O lado relacional do Caché só entende de triggers e stored procedures como em qualquer banco de dados relacional. É claro que as triggers e as stored procedures podem abrir objetos e executar métodos. Mas a linguagem SQL, como o comando insert, não abre objetos e executa métodos, como o nosso %OnValidateObject, sozinha. Senão eu estaria mentindo para vocês quando dizia que não existe mapeamento objeto relacional no Caché, pois o que de fato ocorre é que o Caché possui dois engines que compartilham do mesmo armazenamento e da mesma estrutura de meta-dados. Para resolver este problema, podemos criar uma trigger que executa nosso método ValidaCPF() e faz a verificação:

Trigger VerificaCPF [ EVENT = INSERT, TIME = BEFORE ]

{

Set %ok=1

If '##class(empresa.Cliente).ValidaCPF({CPF})

{

Set %ok=0

Set %msg=”CPF inválido!”

}

}

Veja na nossa seção de referências no final deste documento, os links relativos ao SQL no Caché. Lá você achará tudo relativo à criação de triggers, stored procedures e etc. Usando DDL ANSI SQL ou o próprio Caché Studio.

Mas voltando ao nosso exemplo, a princípio está tudo bem, portanto atribuo à variável %ok o valor 1 (true). Então verifico se o CPF da linha que está sendo incluída não é válido, chamando o nosso método de classe ValidaCPF(cpf). Repare no operador de negação depois do If, representado pela aspas simples. Repare também na sintaxe {COLUNA}. Ela permite que façamos referência a uma coluna específica da linha atualmente processada. Se verificarmos que o CPF é inválido, fazemos %ok ser igual a 0 (false) e colocamos uma mensagem de erro em %msg. Agora vocês podem perceber claramente a diferença entre o engine relacional e o engine orientado a objetos do Caché.

Agora compile a sua classe novamente e tente inserir um CPF inválido.

O método %OnValidateObject() não é chamado no lado relacional. Temos que criar uma trigger cujo EVENT seja igual a INSERT e outra cujo EVENT seja igual a UPDATE para obter o mesmo efeito no lado relacional. É por isto que costumamos criar um único método de classe com a lógica de validação e chamá-lo onde quer que ele seja necessário (no %OnValidateObject(), ou nas triggers, passando as propriedades a serem validadas como parâmetros). Tudo isto ficaria mais ou menos assim:

19

Trigger ValidaPropriedadesInsert [ EVENT = INSERT, TIME = BEFORE ]

{

Set %ok=1

Set %msg = ##class(empresa.Cliente).ValidaPropriedades({CPF})

If %msg'= ""

{

Set %ok=0

}

}

Trigger ValidaPropriedadesUpdate [ EVENT = UPDATE, TIME = BEFORE ]

{

Set %ok=1

Set %msg = ##class(empresa.Cliente).ValidaPropriedades({CPF})

If %msg'= ""

{

Set %ok=0

}

}

Method %OnValidateObject() As %Status [ Private]

{

Set msgErro=..ValidaPropriedades(..CPF)

If msgErro'= ""

{

Quit $system.Status.Error(5001,msgErro)

}

Quit $system.Status.OK()

}

ClassMethod ValidaPropriedades(cpf As %String) As %Status [ Private ]

{

If '..ValidaCPF(cpf)

{

Quit "CPF inválido!"

}

Quit ""

}

Agora compile a sua classe novamente e tente inserir ou atualizar um objeto com um CPF inválido. Depois tente fazer uma validação do CNPJ do Fornecedor. Para tanto, você vai precisar

20

de um algoritmo de validação de CNPJ. Mas não se preocupe. Crie o método ValidaCNPJ() na classe Fornecedor e simplesmente verifique se o CNPJ começa por 1 (um). Se sim, retorne 1, caso contrário, retorne 0. Depois você tenta criar o código real de validação de CNPJs.

Relacionamento entre classes Podemos relacionar objetos de diversas maneiras diferentes. No mundo relacional, fazemos

isto utilizando Foreing Keys (chaves estrangeiras). No início da era relacional, as foreing keys utilizadas eram os próprios dados das tabelas, isto é. Se um cliente tinha um CPF, na tabela de pedido, existia o CPF do cliente que fez o pedido. Muitos DBAs fazem isto até hoje. Só que se o CPF do cliente estivesse errado (por erro de digitação), teríamos que alterá-lo em diversas tabelas do sistema. Para resolver este problema, o Cliente ganhou um código (um ID) que o identificava para o sistema. O Pedido agora aponta para o ID do cliente, ao invés de para seu CPF.

Como já vimos, todo objeto dentro do Caché já possui um ID que é dado a ele pelo Caché no momento de seu salvamento. Apenas objetos persistentes possuem ID. Objetos seriais (veja depois a seção sobre Composição) não possuem ID, já que estes são armazenados como parte de outros objetos e nunca são armazenados sozinhos.

No Caché, podemos relacionar objetos de diversas maneiras:

Tipo de Relacionamento Descrição

Referência simples Em uma referência simples, um objeto simplesmente aponta para um outro objeto através de seu ID. Se o outro objeto for apagado, o primeiro não saberá disto e, ao tentar abri-lo, ocorrerá um erro de <INVALID OREF>. Isto é, não existe integridade referencial.

Relacionamento Um relacionamento é uma referência entre objetos de duas classes, mantida através do ID dos objetos envolvidos. As diferenças entre um Relacionamento e uma Referência simples são:

• O Caché garante a integridade referencial.

• O relacionamento aparece para o mundo relacional como uma foreing key.

• A entidade referenciada é mantida em uma lista dinâmica nos objetos que a referenciam (inverse property).

Um relacionamento pode ser de dois tipos: One-to-Many e Parent-Children, isto é, “um para muitos” e “pai filho” (onde os filhos seriam as “entidades fracas”).

Composição/Agregação Uma referência simples para um objeto de uma classe do tipo serial. Quando o objeto que mantém a referência é salvo, o objeto serial referenciado é serializado e salvo junto com ele.

Foreing Key relacional Um relacionamento entre objetos mantido através de uma ou mais colunas que juntas representam um outro objeto (linha de uma outra tabela). O Caché garante a integridade referencial.

Tabela 2

Referências Simples A maior desvantagem das referências simples é o fato de que o Caché não garante a

integridade referencial dos objetos. Mas esta também é sua maior vantagem por dois motivos:

21

• Performance. Se soubermos que um objeto jamais será apagado, para que verificar se ele existe a toda hora? Se existisse uma classe chamada UF, onde armazenaríamos os estados de um dado país. A possibilidade de um estado sumir é muito remota. Ele pode até mudar de nome, mas sumir é muito difícil. Diversas classes de nosso sistema podem apontar para um objeto da classe UF sem se preocuparem com integridade referencial, pois de acordo com nossas regras de negócio, ninguém pode apagar um objeto da classe UF.

• Encapsulamento/Acoplamento. Como um relacionamento simples não precisa verificar a integridade referencial, não existe a propriedade inversa na classe referenciada, como especificado pela ODMG. Esta propriedade às vezes é muito incômoda quando queremos trabalhar com classes componentes. Isto é, quando construímos uma classe cujos objetos serão referenciados por muitas outras classes de sistemas diferentes. Neste caso, qualquer classe pode referenciar objetos de nossa classe sem que nós fiquemos sabendo.

Desta forma, é preciso se perguntar sempre se devemos utilizar uma referência simples ou um relacionamento.

Para exemplificar, vamos criar uma classe chamada empresa.ClienteTipo de tal forma que ela seja persistente e possua as propriedades Descricao e Código:

Class empresa.ClienteTipo Extends (%Persistent, %Populate) [ ClassType = persistent, ProcedureBlock]

{

Property Codigo As %Integer [ Required ];

Property Descricao As %String [ Required ];

Index CodigoIndex On Codigo [ Unique ];

}

Poderíamos ter criado esta classe em outro pacote, com outro nome (“empresa.cliente.Tipo”, por exemplo), mas vamos manter esta discussão fora de nosso escopo por enquanto. Agora, para dizermos que um Cliente é de um determinado tipo, adicionamos na classe Cliente uma propriedade nova do tipo empresa.ClienteTipo:

Class empresa.Cliente Extends empresa.Pessoa [ ClassType = persistent, ProcedureBlock ]

{

Property Tipo As empresa.ClienteTipo [ Required ];

......

}

22

Agora nosso modelo ficará assim:

Ainda não existem tipos de cliente cadastrados. No Caché Terminal, digite os seguintes comandos Caché Object Script (COS) para criar um novo Tipo:

USER>Set novoTipo=##class(empresa.ClienteTipo).%New()

USER>Set novoTipo.Codigo=10

USER>Set novoTipo.Descricao=”Varejo”

USER>Set ok= novoTipo.%Save()

USER>Write ok

1

USER>Write novoTipo.%Id()

1

Repare que o nosso primeiro Tipo criado ganhou, naturalmente, o ID de número 1. Se você criar mais um Tipo, repetindo os mesmos comandos, mas com valores de propriedades diferentes (11, “Atacado”, por exemplo), você verá que o método %Id() retornará 2, e assim por diante. Crie este novo Tipo e observe.

Agora que temos dois Tipos de Cliente, vamos fazer com que nossos clientes apontem para um deles, o de ID de número 1 (um). Para tanto, ainda no Caché Terminal, você pode chamar o Shell de SQL e executar um update:

USER>Do $system.SQL.Shell()

>>update empresa.Cliente set Tipo=1

(1)>>go SQLCODE=0 ROWCOUNT=100

>>exit

USER>

Para ver isto, execute esta query no seu Gerenciador de SQL:

Select ID, Nome, Tipo->Descricao from empresa.Cliente

empresa.Cliente

CPFempresa.ClienteTipo

CodigoDescricao

1..1

+Tipo

empresa.Cliente

CPF

empresa.Cliente

CPFempresa.ClienteTipo

CodigoDescricao

empresa.ClienteTipo

CodigoDescricao

1..1

+Tipo

Figura 6

23

Repare na sintaxe que utilizei para navegar através da propriedade Tipo da classe Cliente (um hífen seguido de um sinal de maior-que, isto é, uma “setinha”). Chamamos isto de Join Implícito e isto pode ser utilizado tanto em referências simples quanto em relacionamentos, evitando assim joins complexos entre várias tabelas.

Se você tentar apagar um Tipo para a qual um Cliente já está apontando, o Caché não se oporá. Você só conseguirá ter integridade referencial automática usando relacionamentos. Mas neste caso, nossas regras de negócio deixam claras que um Tipo de Cliente nunca será apagado. Portanto não há porque utilizar relacionamentos e ficar verificando a integridade referencial.

Relacionamentos Como já foi dito, quando queremos relacionar um objeto a outro no Caché e queremos que

o Caché gerencie a integridade referencial, isto é, impedir que um objeto seja apagado se o mesmo estiver sendo referenciado por outro, utilizamos Relacionamentos (Relationships).

Existem dois tipos de relacionamentos no Caché:

• Um para Muitos. Vários objetos (A1, A2,..., An), podem apontar para um objeto B1. Portanto, em A1 existe uma propriedade do tipo B (referência simples) e em B existe uma lista do tipo A, para sabermos quem aponta para B. Esta lista é mantida dinamicamente pelo Caché e é utilizada pelo mesmo para se manter a integridade referencial dos objetos. Desta forma, não é possível apagar B1, enquanto houver um objeto da classe A apontando para ele.

• Pai-Filho. Um objeto A1 pode apontar para um ou mais objetos (B1, B2,...,Bn). Portanto, em A1 existe uma lista do tipo B (os filhos de A1) e em B existe uma propriedade do tipo A, para sabermos quem é o pai de Bn. Esta lista é mantida dinamicamente pelo Caché e é utilizada pelo mesmo para se manter a integridade referencial dos objetos. Desta forma, se apagarmos A1, o Caché se encarregará de apagar todos os seus filhos (B1, B2,...,Bn).

Não existe no Caché (pelo menos por enquanto) os tipos de relacionamento “Um para Um” e “N para N”. Estes tipos de relacionamentos são implementados no Caché utilizando-se os tipos de relacionamento já existentes, em conjunto com classes de apoio, se necessário. Por exemplo, o relacionamento “Um para Um” pode ser implementado utilizando-se o “Um para Muitos” e garantindo-se no código de validação (%OnValidadeObject()) de que na lista mantida pelo Caché, exista apenas 1 objeto (obj.Lista.Count()=1). E o relacionamento “N para N” pode ser implementado através de dois relacionamentos “Um para Muitos” e uma classe de relacionamento (como em um modelo relacional).

Para exemplificar, vamos construir uma estrutura clássica de Pedido e Itens de Pedido. Em resumo, um Cliente de nossa empresa pode fazer vários pedidos de produtos que vendemos. Cada pedido pode ter vários itens que o compõem. Aqui podemos mapear dois tipos de relacionamento:

• Um relacionamento “Um para Muitos” entre Cliente e Pedido. Isto é: Um Cliente pode ter muitos Pedidos.

• Um relacionamento “Pai-Filho” entre Pedido e Itens de Pedido. Isto é: Um Pedido pode ter um ou mais itens que só existem em função do Pedido em si. Se o Pedido for excluído, seus itens também o serão automaticamente.

24

Para tanto, vamos criar uma nova classe persistente chamada empresa.Pedido, com suporte a população automática de dados e com as seguintes propriedades:

Propriedade Tipo Descrição Parâmetros

Data %Library.Date Data em que o pedido foi emitido.

Obrigatória.

Obrigatória

Situacao %Library.Integer 1-Pendente

2-Atendido

Obrigatória

MINVAL=1

MAXVAL=2

Valor %Library.Numeric Valor total do pedio.

Tabela 3

Sua classe deverá estar assim:

Class empresa.Pedido Extends (%Persistent, %Populate)

[ ClassType = persistent ]

{

Property Data As %Date [ Required ];

Property Situacao As %Integer(MAXVAL = 2, MINVAL = 1) [ Required ];

Property Valor As %Numeric [ Required ];

}

25

Figura 7

Agora vamos criar o relacionamento entre Cliente e Pedido. Para tanto, basta adicionar em empresa.Pedido uma nova propriedade chamada Cliente. Mas esta propriedade será um relacionamento:

Clique agora em Next e responda as perguntas do wizard de acordo:

• Esta propriedade-relacionamento refere-se a: “One: um outro objeto”.

• Esta propriedade-relacionamento referencia objetos do seguinte tipo: “empresa.Cliente”

• O nome da propriedade correspondente (inversa) na classe referenciada é: “Pedidos”

Desta forma, um objeto da classe pedido vai apontar para um objeto da classe cliente. E, por outro lado, um objeto da classe cliente terá um propriedade (inversa) chamada Pedidos mantida automaticamente pelo Caché, que apontará para cada um dos pedidos de um determinado cliente. Esta forma de relacionamento bi-direcional foi definida pelo ODMG e é seguida a risca pelo Caché. Clique em Next e em seguida em Finish para terminar. Sua classe Pedido ganhará um novo relacionamento:

Relationship Cliente As empresa.Cliente [ Cardinality = one, Inverse = Pedidos ];

Como o Pedido aponta para um único Cliente, o parâmetro Cardinality está configurado como one. O parâmetro Inverse serve para explicar ao Caché qual propriedade na classe

26

Cliente será a propriedade inversa. Olhando portanto na classe Cliente, veremos que o Caché criou a seguinte linha:

Relationship Pedidos As empresa.Pedido [ Cardinality = many, Inverse = Cliente ];

Na classe Cliente, portanto, vemos que também existe uma definição de relacionamento que diz que um Cliente pode ter vários Pedidos. Por isto o parâmetro Cardinality está configurado como many. E aqui também definimos a propriedade inversa na classe Pedido como Cliente, fechando e amarrando portanto a definição do relacionamento.

Parece complexo, mas é muito simples e prático. Compile (CTRL+F7) as duas classes, Pedido e Cliente. Preste atenção nas mensagens da janela de mensagens para verificar se não houve erros de definição.

Agora vamos criar a classe PedidoItem, que representarão os itens de nossos Pedidos. Crie uma nova classe persistente, com suporte a população automática de dados cujo nome será PedidoItem. A única propriedade que colocaremos nesta classe será a Descricao do item:

Class empresa.PedidoItem Extends (%Persistent, %Populate) [ ClassType = persistent ]

{

Property Descricao As %String [ Required ];

}

Pronto, agora só precisamos criar o relacionamento entre PedidoItem e Pedido. Para tanto, adicione uma nova propriedade chamada Pedido a sua classe PedidoItem e defina-a como um Relacionamento. Clicando em Next, vamos responder às perguntas:

• Esta propriedade-relacionamento refere-se a: “Parent: o pai deste objeto”.

• Esta propriedade-relacionamento referencia objetos do seguinte tipo: “empresa.Pedido”

• O nome da propriedade correspondente (inversa) na classe referenciada é: “Itens”

Clique em Next e depois em Finish. O Caché adicionou a seguinte linha em sua classe PedidoItem:

Relationship Pedido As empresa.Pedido [ Cardinality = parent, Inverse = Itens ];

Então agora cada objeto da classe PedidoItem aponta para um objeto da classe Pedido, seu objeto “pai”. Por isto a cardinalidade está definida como parent. Aqui vemos também a definição da propriedade inversa como Itens. Isto significa que na classe Pedido haverá uma propriedade relacionamento chamada Itens. Vamos ver:

Relationship Itens As empresa.PedidoItem [ Cardinality = children, Inverse = Pedido ];

Ou seja, cada objeto da classe Pedido possui uma lista, chamada Itens, de objetos da classe PedidoItem. Como um pedido tem vários itens, seus itens são encarados como seus

27

filhos, por isto a cardinalidade está configurada como children. Aqui também vemos definida a propriedade inversa na classe PedidoItem como Pedido, fechando e amarrando portanto a definição do relacionamento.

Pronto, agora compile (CTRL+F7) a sua classe Pedido e a sua classe PedidoItem. Para testar nossa definição, vamos criar um pedido para um cliente qualquer. Faça uma consulta e escolha o ID de um cliente com um CPF inválido. Eu vou utilizar o ID 95. Abra o Caché Terminal e vamos digitar os seguintes comandos:

SER>Set objCli=##class(empresa.Cliente).%OpenId(95)

USER>Write objCli.CPF

3458

USER>Set objPed=##class(empresa.Pedido).%New()

USER>Set objPed.Cliente=objCli

USER>Write objCli.Pedidos.Count()

1

USER>Write $H

59476,60748

USER>Set objPed.Data=$Piece($H,",",1)

USER>Write $ZDate(objPed.Data,4)

03/11/2003

USER>Set objPed.Valor=85

USER>Set objPed.Situacao=1

USER>Set objItem=##class(empresa.PedidoItem).%New()

USER>Set objItem.Descricao="Casa Blanca"

USER>Do objPed.Itens.Insert(objItem)

USER>Set objItem=##class(empresa.PedidoItem).%New()

USER>Set objItem.Descricao="O Poderoso Chefão"

USER>Do objPed.Itens.Insert(objItem)

USER>Write objPed.Itens.Count()

2

USER>Set ok=objPed.%Save()

USER>Do $system.Status.DisplayError(ok)

ERROR #5001: CPF inválido!

USER>Set objPed.Cliente.CPF=33333333333

USER>Set ok=objPed.%Save()

USER>Do $system.Status.DisplayError(ok)

28

Muitos comandos, não? Mas vamos a eles. Primeiramente eu abri o objeto da classe Cliente que me interessava. A idéia é criar um novo pedido para este cliente. Eu imprimi o nome do Cliente apenas para verificar que o objeto Cliente foi aberto corretamente. Em seguida, criei um novo objeto da classe Pedido. Como este pedido pertence ao cliente que escolhemos, eu simplesmente atribuo a propriedade Cliente do nosso novo objeto Pedido, o objeto cliente que abrimos:

USER>Set objPed.Cliente=objCli

Depois, para mostrar para você que o Caché mantém automaticamente a propriedade inversa do relacionamento, eu perguntei para o nosso objeto cliente, quantos pedidos ele já possui:

USER>Write objCli.Pedidos.Count()

1

Perfeito. Mas um pedido possui uma data. O Caché armazena e lida com datas em um formato numérico, o qual chamamos de “formato interno”. Existe uma variável de ambiente chamada $HOROLOG (ou $H para abreviar), que nos dá a data de hoje e o número de segundos que já se passaram a partir das 00:00hs de hoje, separados por vírgula. O número que corresponde à data é exatamente o número de dias que se passaram desde a data de 31/12/1840. Existem diversas funções do Caché para transformar uma data do “formato interno” para o “formato externo” e vice-versa. Consulte a referência de Caché Object Script que acompanha a documentação do produto.

Para atribuir a data de hoje a nosso pedido, precisamos apenas atribuir o número que corresponde à data de hoje (primeiro “pedaço” da variável $H) à propriedade Data de nosso Pedido. Para tanto, vamos utilizar uma função Caché ObjectScript que nos retorna “pedaços” de uma string. Basta dizer para esta função qual é a string, o que separa um pedaço de outro e qual pedaço queremos. Como queremos apenas o primeiro pedaço da variável $H, fizemos o seguinte:

USER>Set objPed.Data=$Piece($H,",",1)

USER>Write objPed.Data

59476

USER>Write $ZDate(objPed.Data,4)

03/11/2003

Repare que eu passei para a função $Piece, como segundo argumento, o caractere vírgula entre aspas e depois, como terceiro argumento, qual pedaço da string eu queria. Como a nossa variável $H só possui dois pedaços (o primeiro é a data e o segundo é a hora), dissemos que queríamos apenas o primeiro (1). Depois eu imprimi a propriedade Data apenas para verificar e utilizei em seguida uma outra função COS, a $ZDate, para confirmar que o valor de data que atribuímos representa realmente a data de hoje. O segundo argumento da função $ZDate é o formato da data. O valor quatro representa o formado europeu (DD/MM/YYYY). Veja a referência de COS na documentação do Caché para saber mais.

29

Depois de definir a data, atribuímos um valor e uma situação para o nosso novo pedido. Depois disto, criamos dois itens de pedido e os inserimos na propriedade Itens de nosso objeto Pedido:

USER>Set objItem=##class(empresa.PedidoItem).%New()

USER>Set objItem.Descricao="Casa Blanca"

USER>Do objPed.Itens.Insert(objItem)

USER>Set objItem=##class(empresa.PedidoItem).%New()

USER>Set objItem.Descricao="O Poderoso Chefão"

USER>Do objPed.Itens.Insert(objItem)

USER>Write objPed.Itens.Count()

2

Repare que desta vez eu fiz diferente. Eu poderia ter atribuído à propriedade Pedido (de nosso novo objeto objItem) o objeto objPed. Mas decidi usar a propriedade Itens de nosso objeto objPed e inserir os novos itens de pedido (objItem) que criei. Fiz isto apenas para mostrar que ambas as formas funcionam perfeitamente. E para verificar, pedi para a propriedade Itens me informar quantos objetos ela já estava contando.

Para finalizar, eu pedi para o pedido se salvar, chamando seu método %Save(). O método %Save conforme vimos, nos retorna um %Status o qual atribuímos à variável ok. Usando o método DisplayError do objeto $system.Status, verificamos que o CPF do cliente que escolhemos é invalido (conforme já sabíamos). O interessante aqui é notar que ao pedir para salvar o nosso novo pedido, o Caché também executou automaticamente o método %OnValidateObject da nossa classe empresa.Cliente. Como o nosso cliente possui um CPF inválido, o método retornou uma mensagem de erro. Vamos corrigir o CPF de nosso cliente e pedir para que ele seja salvo de novo:

USER>Set ok=objPed.%Save()

USER>Do $system.Status.DisplayError(ok)

ERROR #5001: CPF inválido!

USER>Set objPed.Cliente.CPF=33333333333

USER>Set ok=objPed.%Save()

USER>Do $system.Status.DisplayError(ok)

Pronto! Nenhum erro foi retornado desta vez. O nosso novo pedido está salvo. Neste ponto, você já aprendeu bastante sobre como utilizar relacionamentos. Basicamente você trabalha com objetos e listas de objetos. Consulte a documentação de suas classes para saber mais sobre os recursos a sua disposição. Por exemplo, selecione a classe Pedido e escolha na barra de menus de seu Caché Studio a opção “visualizar > Exibir Documentação da Classe”.

Agora vamos ver como isto ficou no lado relacional. Abra o seu Gerenciador de SQL e clique na tabela empresa.Pedido. Você verá que lá existe uma coluna chamada Cliente, conforme definimos em nossa classe, que representa o Cliente do Pedido. Esta coluna foi mapeada para o lado relacional do Caché como uma foreign key. Se você clicar na aba Restrições (constraints), você verá a definição desta foreing key. Fazendo um select na tabela

30

empresa.Pedido, você verá o nosso primeiro pedido (ID = 1) criado acima, onde a coluna Cliente possui o valor do ID do cliente que escolhemos quando criamos o nosso pedido. Ou seja, no lado relacional, trabalharemos sempre com os IDs dos objetos.

Para terminar vamos fazer dois testes no lado relacional. Sabemos que existe pelo menos um pedido que aponta para o cliente cujo ID é 95 (95 foi o ID que eu escolhi, o seu pode ser diferente, use o seu). Portanto, vamos tentar excluir este cliente:

Delete from empresa.Cliente where ID=95

O Gerenciador de SQL retornará para você um erro dizendo que existe pelo menos uma linha na tabela empresa.Pedido cuja coluna aponta para este cliente que estamos tentando apagar. Portanto ele não pode deixar você excluir este cliente. Ou seja, integridade referencial.

O segundo teste é apagar o Pedido. Lembra que definimos um relacionamento do tipo “Pai-Filho” entre Pedido e PedidoItem? Portanto, se apagarmos o nosso Pedido, todos os seus itens deverão ser apagados automaticamente, certo? Vamos primeiro verificar os itens de nosso pedido:

Select * from empresa.PedidoItem where Pedido=1

Pronto, ai estão os itens do Pedido cujo ID é igual a 1 (nosso primeiro e único pedido). Agora vamos apagar este pedido:

Delete from empresa.Pedido where ID=1

Agora vamos ver se aqueles itens de pedido foram apagados:

Select * from empresa.PedidoItem where Pedido=1

Nenhum item? Ótimo! Pois este é o comportamento esperado. Não há mais itens de pedido para o Pedido 1, pois excluímos o pedido 1.

Composição Bom, se quiséssemos guardar o endereço completo de nossos clientes e fornecedores,

como faríamos? Se estivéssemos criando um modelo de classes em UML, como pensaríamos? Criaríamos uma classe Endereco e diríamos que tanto Cliente quanto Fornecedor possuem um Endereco, certo? Mas a classe Endereco não pode ser uma classe persistente comum. Não faz sentido inserirmos um endereço na base de dados que não pertença a ninguém. Um endereço só faz sentido se estiver relacionado com uma entidade que o possui (um cliente ou um fornecedor). Mas nós não podemos simplesmente relacionar Endereco com Cliente e, depois, relacionar novamente Endereco com Fornecedor. Isto não funciona. Se o endereço pertence a um cliente, ele manteria a propriedade Fornecedor nula? Isto é um design muito ruim.

31

No nosso modelo de classes, utilizaríamos o que chamamos de composição por valor:

Mas se nós já estamos utilizando herança, podemos simplesmente fazer o seguinte:

empresa.Cliente

CPF

empresa.Fornecedor

CNPJ

1..1

+Endereco

empresa.Pessoa

Nome

empresa.Endereco

RuaNumero

empresa.Cliente

CPF

empresa.Cliente

CPF

empresa.Fornecedor

CNPJ

empresa.Fornecedor

CNPJ

1..1

+Endereco

empresa.Pessoa

Nome

empresa.Pessoa

Nome

empresa.Endereco

RuaNumero

empresa.Endereco

RuaNumero

Figura 9

empresa.Cliente

CPFempresa.Fornecedor

CNPJ

1..1

+Endereco

empresa.Pessoa

Nome

empresa.Endereco

RuaNumero

1..1

+Endereco

empresa.Cliente

CPF

empresa.Cliente

CPFempresa.Fornecedor

CNPJ

empresa.Fornecedor

CNPJ

1..1

+Endereco

empresa.Pessoa

Nome

empresa.Pessoa

Nome

empresa.Endereco

RuaNumero

empresa.Endereco

RuaNumero

1..1

+Endereco

Figura 8

32

Ou seja, se Pessoa tem um e apenas um endereço, clientes e fornecedores também terão. Só que como estamos utilizando composição, o objeto da classe Endereco estará contido em um objeto da classe Cliente ou Fornecedor, conforme for o caso. É muito diferente de um relacionamento entre entidades diferentes, onde uma aponta para a outra. Por exemplo, o nosso Cliente possui um Endereco, mas um Pedido aponta para um Cliente:

Como fazemos isto no Caché? Como implementamos a composição por valor? Primeiro, vamos criar a nossa classe Endereco. Portanto, crie uma nova classe chamada empresa.Endereco e defina seu tipo como Serial. Marque a opção para suporte a população automática de dados e adicione duas propriedades, Rua e Número:

Class empresa.Endereco Extends (%SerialObject, %Populate)

[ ClassType = serial, ProcedureBlock]

{

Property Rua As %String [ Required ];

Property Numero As %Integer;

}

Repare que conforme exibido na Tabela 1, a nossa nova classe herda de %SerialObject e possui seu ClassType igual a serial. Repare também que cada classe é responsável pelo seu próprio armazenamento. Um objeto Cliente, ao pedir para um objeto Endereco ser armazenado, recebe, ao invés de um ID do Endereco, a versão serializada do objeto Endereco, já que Endereco não é persistente.

Cliente

Endereço

PedidoCliente

Endereço

Pedido

Figura 10

33

Compile a sua nova classe Endereco. Agora basta adicionar a nova propriedade Endereco em empresa.Pessoa:

Class empresa.Pessoa Extends (%Persistent, %Populate)

[ ClassType = persistent, ProcedureBlock ]

{

Property Endereco As empresa.Endereco [ Required ];

Property Nome As %String [ Required ];

Index NomeIndex On Nome;

Method RecuperarIdentificacao() As %String

{

Quit “” }

}

Agora compile a sua classe Pessoa e depois compile também Fornecedor e Cliente. Abra o gerenciador de SQL e clique em cima da tabela empresa.Cliente:

Repare nas novas colunas Endereco_Numero e Endereco_Rua. Esta é a forma que o Caché encontrou de mapear um objeto embutido (serial) na sua tabela. Repare também que não existe uma Tabela chamada Endereco. Isto é porque simplesmente não faz sentido. Apesar de a classe empresa.Endereco existir, a tabela não existe. Repare também na coluna escondida chamada Endereco. Esta coluna não aparece nas suas pesquisas a não ser que seja solicitada, assim com a coluna x__classname. Você pode executar uma query do tipo:

Select * from empresa.Cliente where Endereco is not null

Figura 11

34

Você verá que a query não retorna nada. Isto porque não existem clientes com endereços. Acabamos de criar a propriedade! Vamos agora fazer um teste. De posse de um ID de um Cliente (vou supor que seja 95), abra o Caché Terminal e digite o seguinte:

USER>Set objCli = ##class(empresa.Cliente).%OpenId(95)

USER>Set objCli.Endereco.Rua=”Rua X”

USER>Set objCli.Endereco.Numero=11

USER>Set ok = objCli.%Save()

USER>Do $system.Status.DisplayError(ok)

Agora repita aquela última query que fizemos e veja o resultado. O contrário também funciona, claro. Ou seja, você pode executar um insert via SQL e depois abrir o objeto e verificar que ele possui um objeto interno da classe Endereco cujas propriedades possuirão os valores que você informou no comando insert.

Como ficou nosso modelo Abaixo você pode ver como ficou nosso modelo de classes de uma forma resumida,

mostrando apenas propriedades e relacionamentos:

Cliente

CPFClienteTipo

CodigoDescricao

1..1

+Tipo

Pessoa

Nome

Diretor

Salario

Fornecedor

CNPJ

Pedido

DataSituacaoValor

Endereco

RuaNumero

1..1

+Endereco

1..1

+Cliente

PedidoItem

Descricao1..1

+Pedido

1..n

+Itens

+Pedidos

0..n

Cliente

CPF

Cliente

CPFClienteTipo

CodigoDescricao

ClienteTipo

CodigoDescricao

1..1

+Tipo

Pessoa

Nome

Pessoa

Nome

Diretor

Salario

Diretor

Salario

Fornecedor

CNPJ

Fornecedor

CNPJ

Pedido

DataSituacaoValor

Pedido

DataSituacaoValor

Endereco

RuaNumero

Endereco

RuaNumero

1..1

+Endereco

1..1

+Cliente

PedidoItem

Descricao

PedidoItem

Descricao1..1

+Pedido

1..n

+Itens

+Pedidos

0..n

Figura 12

35

Conclusão Abordamos diversos assuntos interessantes neste tutorial enorme. Agora está mais claro

ainda o potencial deste poderoso produto. A sinergia entre o mundo orientado a objetos e o mundo relacional conseguida com o Caché é algo sem igual em toda a industria da computação.

Pode ter certeza de que um dia estes tutoriais farão parte de um livro meu sobre o Caché. Mas enquanto isto, escrevo para vocês ao mesmo tempo que escrevo para o livro e quando o livro chegar, este material já terá passado pelo crivo de vocês, leitores, e o livro com certeza será excelente. É claro que o livro terá muito mais coisas, será atualizado para a versão de Caché da época em que for lançado e etc...

Mas agora vocês estão mais preparados para o que vem em seguida. Os próximos tutoriais se basearão muito em conhecimentos aqui disceminados. Portanto, se você acha que a coisa ainda está confusa ou precisa de mais exemplos, não hesite em me dizer, pois ai modificarei este tutorial e lançarei uma versão mais atual com correções, mais exemplos e etc... A idéia é esta. Mas para os preguiçosos que me perguntarem sobre coisas que estão claramente ditas aqui, eu só responderei para lerem de novo.Mas agora vocês estão preparadados para os próximos tutoriais. Falarei de como acessar nossos objetos e lógicas de negócio através do Java, WebServices, CSP e etc...

36

Referências

• Caché – InterSystems http://www.intersystems.com.br ou http://www.intersystems.com

• Newsgroups da InterSystems news://news.intersystems.com Aqui você pode acompanhar calorosas discussões sobre o Caché. É um grupo aberto e você tem a chance de ser respondido pelos próprios engenheiros do produto!

• Lista de Discussão Brasileira http://br.groups.yahoo.com/group/cache-br/ Nova lista de discussão sobre o Caché, totalmente em português. Para se cadastrar na lista, envie um e-mail para: [email protected]

• Introdução ao Caché Objects Script (COS) http://127.0.0.1:1972/csp/docbook/DocBook.UI.Page.cls?KEY=GCOS Tutorial de COS: http://127.0.0.1:1972/csp/docbook/DocBook.UI.Page.cls?KEY=TCOS_TutEx Referência para todos os comandos e funções da linguagem: http://127.0.0.1:1972/csp/docbook/DocBook.UI.Page.cls?KEY=RCOS O seu Caché deve estar no ar para que você possa ver estes documentos.

• O SQL no Caché Nesta documentação, você verá como criar triggers, stored procedures, tabelas e etc, usando comandos DDL do SQL ANSI ou o próprio Caché Studio. http://127.0.0.1:1972/csp/docbook/DocBook.UI.Page.cls?KEY=GSQL Referência para todos os comandos e funções da linguagem: http://127.0.0.1:1972/csp/docbook/DocBook.UI.Page.cls?KEY=RSQL O seu Caché deve estar no ar para que você possa ver estes documentos.

• Introdution to Caché Objects http://127.0.0.1:1972/csp/docbook/DocBook.UI.Page.cls?KEY=GOBJ_intro O seu Caché deve estar no ar para que você possa ver este documento.

• Introdução ao Caché Basic http://127.0.0.1:1972/csp/docbook/DocBook.UI.Page.cls?KEY=GBAS Tutorial de Caché Basic: http://127.0.0.1:1972/csp/docbook/DocBook.UI.Page.cls?KEY=TBAS_TutEx Referência para todos os comandos e funções da linguagem: http://127.0.0.1:1972/csp/docbook/DocBook.UI.Page.cls?KEY=RBAS O seu Caché deve estar no ar para que você possa ver estes documentos.

• Códigos de Máscaras de Erros http://127.0.0.1:1972/csp/docbook/DocBook.UI.Page.cls?KEY=RERR_obj O seu Caché deve estar no ar para que você possa ver estes documentos.

37

Sobre o Autor Quaisquer sugestões ou críticas para este artigo devem ser enviadas

para:

Amir Samary ([email protected])

http://www.InterSystems.com.br

Sales Engineer