Professional Documents
Culture Documents
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 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.
1
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:
2
Tipo de dados Classes deste tipo definem Não é preciso herdar ClassType = datatype
novos tipos de dados como de nenhuma classe
os já existentes %String para se fazer um novo
(%Library.String) e data type. Mas é
%Integer recomendado herdar-
(%Library.Integer). se de um data type já
existentes, para
Você poderia criar um tipo
estender-lhe as
de dado chamado CPF que só
funcionalidades.
permitiria salvar valores de
Poderíamos, para o
CPF com dígito verificador
caso do CPF, por
correto, por exemplo.
exemplo, herdar de
%Library.String.
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...
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
3
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.
4
Para terminar, crie agora uma nova classe empresa.Fornecedor da mesma forma. Só
que ao invés de termos um CPF, teremos um CNPJ:
Figura 2
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 é
5
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:
objCli=<OBJECT REFERENCE>[1@empresa.Cliente]
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:
minhaVar=”Joao da Silva”
objCli=<OBJECT REFERENCE>[1@empresa.Cliente]
USER>
6
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,
7
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 Figura 3
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:
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.
8
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:
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:
9
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:
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
10
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 Figura 4
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:
11
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:
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
12
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!
USER>Set objCli.CPF=1234567
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.
13
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):
14
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
}
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.
15
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:
Figura 5
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:
16
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:
17
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!
18
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
19
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.
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:
20
• 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:
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:
21
Agora nosso modelo ficará assim:
empresa.Cliente empresa.ClienteTipo
1..1
CPF Codigo
+Tipo
Descricao
Figura 6
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>
22
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.
23
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:
24
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:
Figura 7
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
25
Cliente será a propriedade inversa. Olhando portanto na classe Cliente, veremos que o Caché
criou a seguinte linha:
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:
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:
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:
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
26
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)
27
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.
28
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)
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
29
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:
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:
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.
30
No nosso modelo de classes, utilizaríamos o que chamamos de composição por valor:
empresa.Pessoa
Nome
empresa.Cliente empresa.Fornecedor
CPF CNPJ
empresa.Endereco
1..1 1..1
+Endereco Rua +Endereco
Numero
Figura 8
empresa.Pessoa empresa.Endereco
1..1
empresa.Cliente empresa.Fornecedor
CPF CNPJ
Figura 9
31
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:
Cliente Pedido
Endereço
Figura 10
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:
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.
32
Compile a sua nova classe Endereco. Agora basta adicionar a nova propriedade Endereco
em empresa.Pessoa:
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:
Figura 11
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:
33
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:
Pessoa Endereco
1..1
Nome Rua
+Endereco
Numero
1..1
+Cliente
Pedido
PedidoItem 1..n
1..1 Data
Descricao +Pedido +Itens Situacao
Valor
Figura 12
34
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...
35
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!
• 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.
36
Sobre o Autor
Quaisquer sugestões ou críticas para este artigo devem ser enviadas
para:
Amir Samary (asamary@intersys.com)
http://www.InterSystems.com.br
Sales Engineer
37