Sei sulla pagina 1di 58

2004 - http://www.liws.com.

br/depo

DePO - Delphi Persistent Objects

ndice
0

Introduo
1 2 3 4 5 6 Introduo

4
................................................................................................................................... 4

Apresentao................................................................................................................................... 4 Sobre o DePO ................................................................................................................................... 4 Instalao ................................................................................................................................... 5 "TODO List" e "Problemas conhecidos" ................................................................................................................................... 8 Material de apoio ................................................................................................................................... 8

Arquitetura
1 2 3 4 5

10

Como o DePO funciona ................................................................................................................................... 10 Classes e Propriedades ................................................................................................................................... 10 Units ................................................................................................................................... 12 Declarando Objetos ................................................................................................................................... 12 Mapeamento de Objetos ................................................................................................................................... 13
De Objetos para RDB .......................................................................................................................................................... 13 Mapeando Objetos.......................................................................................................................................................... 15

Sincronizando o banco de dados ................................................................................................................................... 17

Trabalhando com Objetos


1 2 3 4 5 6 7 8 9 10 CRUD Criando Alterando Excluindo

20

................................................................................................................................... 20 ................................................................................................................................... 20 ................................................................................................................................... 21 ................................................................................................................................... 21

Recuperando ................................................................................................................................... 20

Operaes em Cascata ................................................................................................................................... 21 Problemas com Operaes em Cascata ................................................................................................................................... 22 Listas (Colees) ................................................................................................................................... 24 OID ................................................................................................................................... 26 Generalizao ................................................................................................................................... 27

Desenvolvendo Aplicaes
1 2

31

Relacionamento entre os Objetos ................................................................................................................................... 31 (Re)Utilizando os Objetos ................................................................................................................................... 33

Criando Interfaces para Usurios


1

36

Interfaces RAD ................................................................................................................................... 36

<2004> http://www.liws.com.br/depo

Contedo

II

Tcnicas Avanadas
1
Imagem, Memo Boolean TDateTime

38
.......................................................................................................................................................... 38 .......................................................................................................................................................... 41 .......................................................................................................................................................... 42

Tipos de dados ................................................................................................................................... 38

2 3 4 5 6

Clonando Objetos ................................................................................................................................... 42 Utilizando Proxy ................................................................................................................................... 43 Critrios para recuperar Objetos ................................................................................................................................... 44 Ordem de recuperao (Sort) ................................................................................................................................... 46 Transaes ................................................................................................................................... 46

Extras
1

49
ModelMaker ................................................................................................................................... 49

Histria

55

<2004> http://www.liws.com.br/depo

Introduo

Introduo

1
1.1

Introduo
Introduo
Attention The job started by Liws aim to work together with brazilian developer, but if you don't know portuguese, contact us, we will publish english documentation if someone need. Ateno O trabalho iniciado pela Liws tem como objetivo atender a comunidade de desenvolvedores brasileiros, mas se voc no conhece portugus, entre em contato com a Liws, ns estaremos disponibilizando materias em Ingls se algum precisar. Nota: Esta documento preliminar e est sujeito a mudanas. Esta verso esta disponvel para que voc possa conhecer o funcionamento do DePO e para que possa contribuir para o desenvolvimento do mesmo. Se voc desejar contribuir com o manual, programas exemplos ou com a melhora do cdigo do DePO entre em contato pelo email depo@liws.com.br. Um blog est sendo escrito sobre o desenvolvimento do DePO em http://www.liws.com.br/depo at que uma pgina oficial seja publicada.

1.2

Apresentao
"A good business model provides a software-independent description of the business processes to be automated, thereby promoting a good understanding of priorities and risks prior to technology selection."
Introduction to Business Modeling Using the Unified Modeling Language (UML) Conforme este documento da Rational diz, "Um bom modelo de negcios apresenta uma descrio dos processos de negcio, indentente do software, porm promovendo um bom entendimento das prioridades e riscos antes da seleo da tecnologia". Apesar desta afirmao abstrair a tecnologia, se voc esta lendo este manual, provavelmente est pensando em utilizar o DePO e o Delphi, assim sendo, vamos a outra viso desta afirmao, um bom modelo, deve ser independente de como ser armazenado e independente de como ser apresentado ao usurio final, isto o que o DePO pretende fazer, disponibilizar uma base para se criar o modelo de negcios, encapsulando todo o comportamento necessrio para armazenar e recuperar os dados de fontes de dados diversas. [Uma imagem apresentando a arquitetura DePO, algo como a que est na documentao da Arquitetura do Object Space, precisamos de artistas aqui]

1.3

Sobre o DePO
Delphi Persistent Objects encapsula todo o comportamento necessrio para se comunicar com fontes de dados. DePO utiliza um esquema de mapeamento para determinar que campos e tabelas da fonte

<2004> http://www.liws.com.br/depo

DePO - Delphi Persistent Objects

de dados sero mapeados para as propriedades dos objetos. O que significa que campos e tabelas sero utilizados para persistir dados dos objetos, e sero utilizados para popular as propriedades dos objetos recuperados das fontes de dados. DePO foi implementado com base no modelo apresentado por Scott Ambler no documento "The Design of a Robust Persistent Layer for relational Databases" disponvel junto com outros documentos relacionados em http://www.ambysoft.com. [Incluir a data que o projeto comeou] A verso inicial foi implementada por Cosimo de Michele e est disponvel em http://sourceforge.net/projects/depo, a verso disponvel em http://www.liws.com.br/depo difere da disponvel no Source Forge, por algumas correes enviadas pelo Cosimo e algumas alteraes implementadas pelo Liws e seus colaboradores, aconselho que voc utilize a veso disponvel pela Liws. No momento em que este manual esta sendo escrito (Julho de 2004) a Microsoft est trabalhando no desenvolvimento de uma camada de persistncia chamada ObjectSpace que pelo que sei se parece muito com a implementao do DePO e tem suas bases no modelo proposto por Scott Ambler, uma leitura na documentao do ObjectSpace pode ajudar a entender os conceitos de uma camada de persistncia, http://longhorn.msdn.microsoft.com/lhsdk/ndp/daconoverviewofobjectspacesarchitecture.aspx.

1.4

Instalao
Baixe a ltima verso do DePO de http://www.liws.com.br/depo/arquivos. Verses do Delphi suportadas: Esta verso foi testada no Delphi 7, provavelmente voc consiga instalar em uma verso anterior do delphi com poucas alteraes, devido as mudanas das uses no delphi. Provavelmente deve funcionar perfeitamente no Kylix 3, voc deve apenas remover a unit dpoADOMechanism.pas. Ateno: Se voc tiver uma verso anterior do DePO remova-a do Delphi e apague os arquivos Depo.bpl e Depo.dcp do diretrio de Bpls, se voc usa o Delphi 7 provavelmente ele estar em "C:\Arquivos de programas\Borland\Delphi7\Projects\Bpl" Instalando: 1) Decompacte o arquivo deposources.zip Sero criados os diretrios: Source Lib Doc 2) Abra o pacote Depo.dpk no Delphi Em Options|Diretories/Condicionals define o diretrio de Output das Units para o diretrio Lib

<2004> http://www.liws.com.br/depo

Introduo

3) Clique em Compile

4) Clique em Install

<2004> http://www.liws.com.br/depo

DePO - Delphi Persistent Objects

5) Os componentes estaro na paleta DePO 6) Para compilar projetos com DePO inclua o diretrio Lib no menu Tools|Environment Options|Library|Library Path

O DePO est pronto para ser usado, aproveite :)

<2004> http://www.liws.com.br/depo

Introduo

1.5

"TODO List" e "Problemas conhecidos"


Implementar critrios no implementados no DePO Terminar a documentao Escrever novos mecanismos de persistncia (XML, Zeos, IBX por exemplo) Criar um site para o DePO Criar mais exemplos - Relatrios - Web - Outros bancos de dados - com TdpoDataSet Script para criar o banco de dados - Criar ForeignKey - Campos em sincronismo (Blob, Boolean) - PrimaryKey em Sincronismo

1.6

Material de apoio
Fontes de informaes teis Frum OODesign http://www.oodesign.com.br AmbySoft - Scotty Ambler http://www.ambysoft.com Newsgroup Borland newsgroups.borland.com grupo borland.public.delphi.oodesign Microsoft ObjectSpace http://longhorn.msdn.microsoft.com/lhsdk/ndp/daconoverviewofobjectspacesarchitecture.aspx OMG UML http://www.omg.org Site oficial do DePO http://sourceforge.net/projects/depo Site do DePO em Portugus http://www.liws.com.br/depo

<2004> http://www.liws.com.br/depo

Arquitetura

II

Arquitetura

10

2
2.1

Arquitetura
Como o DePO funciona
DePO armazena, recupera e exclui objetos em bancos de dados, todo o trabalho necesario para que estas tarefas sejam executadas so feitos automaticamente pelo DePO. O DatabaseMechanism a implementao basica da interface necessria para que este trabalho seja feito, todo a interao com este mecanismo deve ser feita atravs dos mecanismos especficos para a conexo com o banco de dados, atualmente esto implementados os mecanismos ADOMechanism para comunicao utilizando ADOConnection e DBXMechanism para comunicao utilizando SQLConnection do DBExpress. Aps DePO is able to take care itself in automatic way of the creation, loading, modernization and cancellation of objects. The objects do not have no acquaintance which it is the source or the destination of the contained information in their attributes. Different objects can recover their data from different sources, and the destination can be changed where persist the data changing with little lines of code. Once that has been written the way in which the information must persist (the persistence mechanism), all the the one which is necessary to make in order to persist objects are to mapping the classes and the attributes with the properties of the persistence mechanism, as an example the tables and the columns that will contain the data for the mechanisms that they have to that to make with the database relational. DePO can also persist inherited classes, persistindo the differences from the class base in connected tables. In this way it is possible to see the property common of different classes that they derive from the same class. In other words I can have a list of customers and suppliers in the same grill, and if I want to modify one of these objects, I can make creating it the correct associated type of form the class. DePO manages in way the many simple relations between objects and the persistence of these relations conserving referencial integrity. All the classes of our colloquiano model with the mechanism of persistence or the mechanisms of persistence demanding the operation that they want to carry out through an object arbitrator the PersistenceManager, that knowing all the present mechanisms and the mappings to they it associates to you will turn the operation demanded from the class to the interested mechanism of persistence.

2.2

Classes e Propriedades
A declarao das classes como de qualquer objeto no delphi com a seguinte particularidade, DePO utiliza a RTTI do Delphi para poder acessar as propriedades publicadas. Segundo o Delphi, e como consta no manual do tiOPF: TPersistent encapsula o comportamento comum para todos os objetos serem acessados por outros objetos, e que podem ler e gravar suas propriedades de e para Stream. Por este proposito TPersistent introduz mtodos que poder ser sobrescritos para: Definir a procedure para carregar e armazenar dados no publicados para um stream. Prover um meio para acessar os valores das propriedades. Prover um meio para acessar o contedo de um objeto para outro.

<2004> http://www.liws.com.br/depo

11

DePO - Delphi Persistent Objects

{$M+} TPersistent = class(TObject) private procedure AssignError(Source: TPersistent); protected procedure AssignTo(Dest: TPersistent); virtual; procedure DefineProperties(Filer: TFiler); virtual; function GetOwner: TPersistent; dynamic; public destructor Destroy; override; procedure Assign(Source: TPersistent); virtual; function GetNamePath: string; dynamic; end; {$M-} A diretiva de compilao $M, ativa antes de TPersistent e desavida aps, faz com que a gerao de informaes RTTI (Run Time Type Information) ocorra para TPersistent e todas as classes derivadas de TPersistent. TdpoPersistentObject = class(TInterfacedPersistent, IdpoPersistentObject) A classe TdpoPersistentObject derivada de TInterfacedPersistent que por sua vez derivada de TPersistent. Para que o DePO possa popular as propriedades de uma classe automaticamente, esta deve ser derivada de TPersistent ou ser compilada com a diretiva $M ativada ou derivada de outra que esteja com RTTI ativada. Mas no basta ser derivada de TPersistent necessrio que estas propriedades estejam publicadas, ou seja na sesso published da classe, como no exemplo a seguir: type TPerson = class(TdpoPersistentObject) private FChields: TdpoRelationshipResolver; FName: String; FFatherOID: String; FID: String; function GetChields: TPeople; function GetHasChields: Boolean; public procedure AfterConstruction; override; property HasChields: Boolean read GetHasChields; published property ID: String read FID write FID; property Name: String read FName write FName; property FatherOID: String read FFatherOID write FFatherOID; property Chields: TPeople read GetChields; end; Na classe TPerson, DePO s ter conhecimento das propriedades ID, Name, FatherOID e Chields, a propriedades HasChields, apesar de pblica, no ser acessivel pelo DePO, pois RTTI s contm informaes published.

<2004> http://www.liws.com.br/depo

Arquitetura

12

2.3

Units

dpoIntrospector in 'dpoIntrospector.pas', dpoDatabaseSchema in 'dpoDatabaseSchema.pas', dpoClassMapping in 'dpoClassMapping.pas', dpoPersistanceObject in 'dpoPersistanceObject.pas', dpoDatabaseMechanism in 'dpoDatabaseMechanism.pas', dpoADOMechanism in 'dpoADOMechanism.pas', dpoDBXMechanism in 'dpoDBXMechanism.pas'; dpoDataSet in 'dpoDataSet.pas'; dpoRegister in 'dpoRegister.pas',

2.4

Declarando Objetos
Os objetos devem ser declarados como qualquer objeto no Delphi, mas o DePO apresenta duas classes bsicas para facilitar o trabalho, implementando vrias propriedades e mtodos. A seguir alguns exemplos de mapeamento de objetos e lista de objetos. TCliente = private FEmails: published property property property end; class(TdpoPersistentObject) TdpoRelationshipResolver; ID: String read FID write SetID; Nome: String read FNome write SetNome; Emails: TEmails read GetEmails;

TEmails = class(TdpoPersistentObjectList) public function Add: TEmail; property Items[const Index: Integer]: TEmail read GetItem; default; end; TEmail = class(TdpoPersistentObject) published property ID: String read FID write SetID; property Endereco: String read FEndereco write FEndereco; property Cliente: TCliente read GetCliente write SetCliente; property ClienteOID: String read FClienteOID write FClienteOID; end;

<2004> http://www.liws.com.br/depo

13

DePO - Delphi Persistent Objects

2.5
2.5.1

Mapeamento de Objetos
De Objetos para RDB
Conceitos Fundamentais de Mapeamento de objetos para banco de dados relacionais, por Marcos Barreto A maioria das modernas aplicaes projetada para o uso da tecnologia orientada a objeto e bancos de dados de relacionais (RDBs) para armazenar os dados. Isto no que dizer que no haja outras opes, por que existem muitas aplicaes construdas com linguagens procedurais e muitos sistemas usaro bancos de dados orientados a objeto ou arquivos XML para armazenar os dados. H uma incompatibilidade (impedance mismatch) entre a tecnologia orientada a objeto e a relational, tecnologias estas que so geralmente usadas para construir os softwares. muito fcil superar esta incompatibilidade, o segredo : voc precisa entender o processo de mapeamento de objetos para RDBs e voc precisa entender como implementar estes mapeamentos. Neste captulo o termo "mapeamento" ser usado para fazer referncia a como um objeto e seus relacionamentos podem ser mapeados para uma tabela e seus relacionamentos no banco de dados. Logo voc descobrir que a coisa no to simples como soa. Conceitos Bsicos Quando aprendendo a mapear objetos para RDB's o ponto inicial para comear com os atributos de uma classe. Um atributo ser mapeado para zero ou mais colunas em um RDB. Lembre-se que nem todos os atributos so persistentes, alguns so usados para clculos temporrios. Por exemplo, um objeto "Estudante" pode ter um atributo Media que necessrio dentro de sua aplicao mas no necessrio ao banco de dados porque ele calculado pela aplicao. Outra questo que um atributo de uma classe pode ser tambm uma outra classe (Classe complexa), temos como exemplo um objeto "Cliente" tem um objeto "Endereo" como um atributo - isto realmente reflete uma associao entre as duas classes que precisaria ser provvel mapeada, e os atributos da prpria classe "TEndereo" precisaro ser tambm mapeados. O mais importante que tudo isto uma definio recursiva: Em algum ponto o atributo ser mapeado para zero ou mais colunas. A mapeamento mais fcil que voc ter uma propriedade mapeada de um simples atributo a uma simples coluna. at mais simples quando ambos tem os mesmos tipos bsicos, por exemplo: elas so ambas datas, o atributo uma string e a coluna um char, ou o atributo um nmero e a coluna um float. Pode ser mais fcil pensar que classes so mapeadas em tabelas, e de certo modo elas so, mas nem sempre isso acontece de forma direta. Com exceo de bancos de dados muito simples voc nunca ter um mapeamento um-para-um de classes para tabelas, isto ser visto neste captulo quando for visto o mapeamento de heranas. A medida que for explicando sobre mapeamento darei exemplo de como isto feito no DePO. Todos os exemplos estaro disponveis no diretrio DePO\Demos e para simplificar somente ser mostrado aqui o cdigo realmente necessrio para demonstrar a parte de mapeamento do framework. Vou colocar este cdigo aqui no lugar assim que tiver montado os outros exemplos. Vamos comear com um simples exemplo que far o mapeamento de um objeto para uma tabela. Simples exemplo de mapeamento no Depo Neste exemplo ns temos uma classe chamada TArtigo que mapeado para uma tabela chamada ARTIGO e os atributos tambm so mapeados diretamente para campos um a um. Vamos a classe: TArtigo = class(TdpoPersistentObject) private FOID: string; FNome: string; FPreco: extended; published property OID: string read FOID write FOID; property Nome: string read FNome write FNome;
<2004> http://www.liws.com.br/depo

Arquitetura

14

property Preco: extended read FPreco write FPreco; end;

Veja que todos os atributos esto published, isto porque o framework usa o Delphi RTTI (RunTime type information) para recuperar os dados contidos nos atributos quando estiver persistindo. Veja agora o mapeamento: procedure TAppManager.InitializeModel; begin ... with MappingManager.Classes.Add do begin ClassObject:= TArtigo; StorageName:= 'ARTIGO'; with AttributesMapping do begin Add('OID', 'OID', 38, True); Add('Nome', 'NOME', 50).Required:= True; Add('Preco', 'PRECO'); end; end; ... end;

Como voc pode perceber bem simples. O mtodo Add, como ele est sendo chamado aqui, retorna um objeto do TdpoDBAttributeMapping e tem como parmetros: function TdpoDBAttributeMappingCollection.Add(AAttributeName, AColumnName: String; ASize: Integer = 0; AIsOID: Boolean = False): TdpoDBAttributeMapping;

Exemplo: Depo\Demos\01-Mapping_Basic Mapeamento com associao 1-1 Como um exemplo deste simples tipo de associao ns escolhemos criar uma nova classe chamada TGrupo e ento fazer com que TArtigo tenha uma referncia a esta classe. Esta associao s navegvel do Artigo para seu Grupo. Ambas as classes so modeladas no seguinte diagrama de classe (este diagrama no mostra mtodos propositalmente por no ser relevante ao processo de mapeamento). Diagrama aqui Algumas coisa no exemplo ainda esto obscuras assim que verificar detalharei o resto da parte de mapeamento. Mapeando Estruturas de herana Mapeando uma hierarquia para uma simples tabela Mapeando cada classe concreta para sua prpria tabela Mapeando cada classe para sua prpria tabela Mapeando Classes para uma estrutura de uma Tabela genrica Mapeando herana mltipla Comparando as estratgias Mapeando relacionamentos de objetos Tipos de relacionamentos
<2004> http://www.liws.com.br/depo

15

DePO - Delphi Persistent Objects

One-to-one, One-to-many, Many-to-many Uni-directional, Bi-directional Como os relacionamentos de objetos so implementados Como os relacionamentos do RDB so implementados Mapeando colees ordenadas Mapeando relacionamentos recursivos Ajuste de peformance Ajustando seus Mapeamentos Consultas por demanda (Lazy Reads)

2.5.2

Mapeando Objetos
O objeto que armazena o mapeamento de classes, atributos e seus relacionamento TdpoDBMappingManager var MappingManager: TdpoDBMappingManager; dpoDBMM:= TdpoDBMappingManager.Create(Self); O mapeamento pode ser feito diretamente na IDE do Delphi ou atravs de cdigo, o exemplo a seguir utiliza as classes apresentadas no tpico Declarando Objetos 12 para mostrar o mapeamento via cdigo. A classe TCliente possui a propriedade TEmail, vejamos a seguir o mapeamento destas classes: with MappingManager.Classes.Add do begin ClassObject:= TCliente; StorageName:= 'CLIENTE'; with AttributesMapping do begin Add('Id', 'ID', 38, True); Add('Nome', 'NOME', 50).Required:= True; with RelationshipsMapping.Add do begin RelatedClass:= TEmail; MasterAttribute:= 'Emails'; IsRetrieveCascaded:= True; IsSaveCascaded:= True; IsDeleteCascaded:= True; Bindings.Add('Id=ClienteOID'); end; end; with MappingManager.Classes.Add do begin ClassObject:= TEmail; StorageName:= 'EMAIL'; with AttributesMapping do begin Add('Id', 'ID', 38, True); Add('Endereco', 'ENDERECO', 50).Required:= True; Add('ClienteOID', 'IDCLIENTE', 38).Required:= True; end;
<2004> http://www.liws.com.br/depo

Arquitetura

16

with RelationshipsMapping.Add do begin Cardinality:= OneToOne; RelatedClass:= TCliente; MasterAttribute:= 'Cliente'; Bindings.Add('ClienteOID=Id'); end; end; with MappingManager do begin RegisterList(TEmail, TEmails); end; O mapeamento via IDE do Delphi funciona da mesma forma, mas os valores devem ser informados no Object Inspector com a seguinte particularidade, as propriedades ClassObject e InheritsMappingFrom so do tipo TClass e o Object Inspector no tem como saber quais as classes que esto declaradas em cdigo, para isto foram criadas as propriedades ClassObjectName e InheritsMappingFromName que so do tipo String, que quando definida utiliza o mtodo FindClass para procurar a classe, o problema que para que a classe deve ser registrada antes, na sesso Initialization da unit: Initialization RegisterClasses([TCliente, TEmail, TEmails]); ou Initialization RegisterClasse(TCliente); RegisterClasse(TEmail); RegisterClasse(TEmails); Antes de comear a utilizar estas classes, necessrio tomar cuidado, pois qualquer referncia a .Classes[Index] no evento OnCreate de um Form ou DataModule ir resultar em uma excesso, pois neste momento o Delphi ainda no carregou os objetos armazenados no Form, no mtodo OnShow estes objetos j estaro disponveis. Na imagem a seguir a classe TCliente est informada no Object Inspector:

<2004> http://www.liws.com.br/depo

17

DePO - Delphi Persistent Objects

2.6

Sincronizando o banco de dados


Sincronizando os objetos atravs do mapeamento com o banco de dados relacional, o mtodo ApplySchemaInfo do TdpoCustomMechanism, criar as instrues SQL necessrias para sincronizar o danco de dados de acordo com o modelo. O mtodo a seguir mostra como sincronizar o banco de dados com o modelo: procedure __InitializeDatabase; var SInfoDB: TSchemaInfoTables; lPos: Integer; lStrings: TStrings; procedure __ExecuteSQL(Value: String); var lStr: String; begin lStr:= Trim(StringReplace(Value, #$D#$A, '', [])); if lStr <> '' then TdpoDBXMechanism(DBMechanism).DBXConnection.ExecuteDirect(lStr); end; begin SInfoDB:= TSchemaInfoTables.Create(TSchemaInfoTable); TdpoDatabaseMechanism(DBMechanism).Driver.GetSchemaInfo(SInfoDB); if SInfoDB.Count = 0 then begin if (MessageBox(0, 'Deseja criar o Banco de dados?', 'Ateno', MB_ICONQUESTION or MB_YESNO) = idYes) then begin lStrings:= TStringList.Create; try TdpoDBXMechanism(DBMechanism).ApplySchemaInfo(lStrings); lPos:= 1;
<2004> http://www.liws.com.br/depo

Arquitetura

18

while lPos > 0 do __ExecuteSQL(__StrGetToken(lStrings.Text, ';', lPos)); finally lStrings.Free; end; end; end; end;

Aproveitando as informaes disponveis em TSchemaInfoTables se precisarmos saber se apenas uma tabela existe no banco de dados podemos utilizar o mtodo FindTableByName: if SInfoDB.FindByTableName('Nome_da_Tabela') <> nil then MessageDlg('Tabela existe :)', mtInformation, [mbOK], 0);

<2004> http://www.liws.com.br/depo

Trabalhando com Objetos

III

Trabalhando com Objetos

20

3
3.1

Trabalhando com Objetos


CRUD
Todas as operaes que podem ser feitas em um objeto em relao a persistncia recebe o nome de CRUD que significa Criar, Recuperar, Atualizar e Excluir (Created, Retrive, Update and Delete). Uma instncia de um objeto identificada unicamente por uma "Identificao de Objeto" conhecida como OID, o tipo de OID deve ser escolhido entre os tipos disponveis, voc pode utilizar uma string de 38 posio com uma GUID, que pode ser gerada pelo Delphi. Todos os mtodos CRUD so implementados em TdpoPersistentObject ou no Mecnismo de Persistncia.

3.2

Criando
Supondo que temos a seguinte classe: type TCliente = class(TdpoPersistentObject) private FID: String; FNome: String; procedure SetID(Value: String); procedure SetNome(const Value: String); public procedure AfterConstruction; override; published property ID: String read FID write SetID; property Nome: String read FNome write SetNome; end;

Portanto se desejamos criar um novo cliente, o seguinte cdigo suficiente: var Cliente: TCliente; begin lCliente:= TCliente.Create(DBMechanism); Cliente.Nome:= 'Agostinho Francisco'; Cliente.Save; O mtodo Save ir tornar este objeto persistente. Note que no passamos nenhum parmetro para a propriedade ID, a propriedade ID ser comentada num capitulo exclusivo sobre OID.

3.3

Recuperando
Para recuperarmos um objeto, aps cri-lo, passamos var Cliente: TCliente; begin

<2004> http://www.liws.com.br/depo

21

DePO - Delphi Persistent Objects

Cliente:= TCliente.Create(DBMechanism); Cliente.ID:= '{86EAD56F-0D6C-4913-91CB-9E2B1DA54C3E}'; if not Cliente.Retrieve then begin MessageDlg('Cliente no existe', mtWarning, [mbOK], 0); end; O cdigo acima recupera o objeto com o OID igual a '{86EAD56F-0D6C-4913-91CB9E2B1DA54C3E}', o mtodo Retrieve quando retorna "True" ou "False", como resultado.

3.4

Alterando
Para recuperarmos um objeto, aps cri-lo, passamos var Cliente: TCliente; begin Cliente:= TCliente.Create(DBMechanism); Cliente.ID:= '{86EAD56F-0D6C-4913-91CB-9E2B1DA54C3E}'; if Cliente.Retrieve then begin Cliente.Nome:= 'Agostinho Francisco Barbosa'; Cliente.Save; end; Aps recuperar um objeto, podemos alterar suas propriedades e salvar novamente.

3.5

Excluindo
O mtodo Delete marca o objeto para ser excludo a excluso ocorre quando o mtodo Save chamado. var Cliente: TCliente; begin Cliente:= TCliente.Create(DBMechanism); Cliente.ID:= '{86EAD56F-0D6C-4913-91CB-9E2B1DA54C3E}'; if Cliente.Retrieve then begin Cliente.Delete; Cliente.Save; end; end;

3.6

Operaes em Cascata
Se uma classe possui relacionamento com outras, seja OneToOne ou OneToMany, quando esta classe for acessada, o DePO automaticamente propagar esta operao a todas as classes relacionadas. Antes precisamos informar ao DePO que ele deve fazer isto no mapeamento das classes, veja o tpico Mapeando Objetos 15 para mais detalhes desta caracterstica.

<2004> http://www.liws.com.br/depo

Trabalhando com Objetos

22

Primeiro Mapeamos a classe TCliente seus atributos e o relacionamento com TEmail, e DePOis o mesmo de TEmail para TCliente. Aps entender todo o contexto, a parte de cdigo a seguir a informao que o DePO necessita para recuperar, salvar e excluir todos os emails quando estas operaes forem executadas em TCliente. with RelationshipsMapping.Add do begin RelatedClass:= TEmail; MasterAttribute:= 'Emails'; IsRetrieveCascaded:= True; IsSaveCascaded:= True; IsDeleteCascaded:= True; Bindings.Add('Id=ClienteOID'); end; end; A propriedade Emails, possui o mtodo de leitura GetEmails este mtodo se encarregara de retornar todos os emails do cliente, mas isto s acontecer se voc acessar a propriedade Emails, se nenhuma referncia a Emails for feita durante o contexto em que Cliente existir. Quando a operao for recuperar ou salvar, isto otimiza o processo, evitando o trafego de informao desnecessria, mas se a operao for excluso, como se trata de objetos, se os objetos relacionados no foram acessados DePO no ter conhecimento de sua existncia e no poder exclu-los, isto torna a excluso mais lenta pois primeiro precisamos recuperar os registros, marc-los para excluso (isto propagado automaticamente) e em seguida concluir a operao com Save. Uma implementao de uma propriedade ou mtodo alternativo para que o DePO gere apenas instrues SQL de excluso e envie para que o servidor processe, dentro da mesma transao uma boa alterativa.

Ateno Apesar de definir algumas propriedades para recuperar, salvar e excluir em cascata, algumas particularides so caractersticas do DePO: OneToOne implementadas entre "Objeto - Atributo", mesmo definindo IsRetrieveCascated os dados da classe relacionada sero recuperadas OneToOne em Generalizao, exemplo: TCliente = class(TPessoa) IsRetrievedCascated em TCliente define se os dados de TPessoa ser recuperado ou no. OneToMany IsRetrievedCascated define se os dados da classe detalhe ser recuperado ou no.

3.7

Problemas com Operaes em Cascata


Para os mtodos: TPai.Filhos; TPai.Delete; //no estava apagando os filhos na tabela TPai.Save; Se alguma operao no funciona, ento provavelmente deve estar faltando alguma coisa no seu cdigo: 1) voc criou a propriedade filhos no pai? FFilhos: TdpoRelationshipResolver;

<2004> http://www.liws.com.br/depo

23

DePO - Delphi Persistent Objects

2) No After construction vc criou o objeto? procedure TPai.AfterConstruction; begin inherited; FFilhos:= Relationships.Add('Filhos'); end; Ateno: comum quando adicionar os relacionamentos digitar nomes errados como: type TInvoice = class(TdpoPersistentObject) private FItems: TdpoRelationshipResolver; public procedure AfterConstruction; override; published property Items: TItems read GetItems; end; ... procedure TInvoice.AfterConstruction; override; begin inherited; FItems:= Relationships.Add('Itens'); end; A propriedade foi declarada como "Items" e no relacionamento foi passado o texto "Itens" 3) Na coleo de Filhos vc implementou o mtodo GetFilhos com todos os detalhes? function TPai.GetFilhos: TFilhos; begin if FFilhos.RelatedObject = nil then begin FFilhos.RelatedObject:= TFilhos.Create(DBMechanism, Self, TFilho); FFilhos.Retrieve; end; Result:= TFilhos(FFilhos.RelatedObject); end;

4) No mapeamento da classe pai voc definiu o relacionamento, como IsRetrieveCascated? with MappingManager.Classes.Add do begin ClassObject:= TPai; StorageName:= 'TABPAI'; with RelationshipsMapping.Add do begin RelatedClass:= TFilho;
<2004> http://www.liws.com.br/depo

Trabalhando com Objetos

24

MasterAttribute:= 'Filhos'; IsRetrieveCascaded:= True; IsSaveCascaded:= True; IsDeleteCascaded:= True; Bindings.Add('Id=PaiOID'); end; 5) No mapeamento do Filho, vc criou o campo em que o ID do pai ser salvo para que ele possa recuperar? with MappingManager.Classes.Add do begin ClassObject:= TFilho; StorageName:= 'TABFILHO'; lAttribute:= Add; with lAttribute do begin AttributeName:= 'PaiOId'; ColumnName:= 'PAIOID'; Size:= 38; end; end; 6) No mapeamento do filho, criou o relacionamento com o pai? with RelationshipsMapping.Add do begin Cardinality:= OneToOne; RelatedClass:= TPai; MasterAttribute:= 'ID'; IsSaveCascaded:= False; IsDeleteCascaded:= False; IsRetrieveCascaded:= False; Bindings.Add('PaiOID=Id'); end; Note aqui que neste relacionamento os ...cascated so falsos e a inverso dos campos no Binding..... 7) na Interface do Filho a propriedade com o pai? TFilho = class(TdpoPersistentObject) private ... FPaiOID: String; public ... property PaiOID: String read FPaiOID write FPaiOID; end;

3.8

Listas (Colees)
Com as classes criadas a partir de TdpoPersistentObjectList podemos gerenciar colees de objetos, esta classe ser o "Interator" para acessar cada um dos objetos que compe a lista.

<2004> http://www.liws.com.br/depo

25

DePO - Delphi Persistent Objects

A interface da classe TClientes, ser utilizada para acessarmos cada um dos TCliente. interface type TClientes = class(TdpoPersistentObjectList) private function GetItem(const Index: Integer): TCliente; reintroduce; public property Items[const Index: Integer]: TCliente read GetItem; default; function Add: TCliente; reintroduce; function New: TObject; reintroduce; end; implementation function TClientes.Add: TCliente; begin Result:= TCliente(inherited Add); end; function TClientes.GetItem(const Index: Integer): TCliente; begin Result:= TCliente(inherited GetItem(Index)); end; function TClientes.New: TObject; begin Result:= TCliente.Create(DBMechanism, Father); end; Note que na implementao os mtodos utilizados Add e New para acrescentarmos mais clientes a coleo e a propriedade "Items", para interagirmos com os itens. A propriedade "Items" deve ser utilizada como qualquer coleo de objetos que temos no Delphi como as colunas de uma DBGrid. Associando Listas com seus Objetos e registrando Listas PersistentObjectList: TArtigos = private function public function function property end; class(TdpoPersistentObjectList) GetItem(const Index: Integer): TArtigo; reintroduce; Add: TArtigo; New: TObject; reintroduce; Items[const Index: Integer]: TArtigo read GetItem; default;

PersistentObject: TArtigo = class(TdpoPersistentObject) public procedure AfterConstruction; override;


<2004> http://www.liws.com.br/depo

Trabalhando com Objetos

26

published property OID: string read FOID write FOID; property Nome: string read FNome write FNome; property Preco: Extended read FPreco write FPreco; end; Registrando a lista: MappingManager.RegisterList(TArtigo, TArtigos);

3.9

OID
Por que utilizar OID? Por que utilizar GUID? No documento de Scott Ambler em http://www.agiledata.org/essays/mappingObjects.html procure por "The importance of OIDs" "A importncia dos OIDs". Cada classe deve ter uma propriedade definida como OID, no h padro para o nome da propriedade e nem da coluna do banco de dados, no mapeamento voc deve identificar quais propriedades sero OID atravs da propriedade IsOID. with MappingManager.Classes.Add do begin ClassObject:= TPerson; StorageName:= 'PERSON_TABLE'; with AttributesMapping do begin lAttribute:= Add; with lAttribute do begin AttributeName:= 'Id'; ColumnName:= 'OID_Key'; IsOID:= True; IndexType:= idxUnique; Size:= 38; Required:= True; end; end; Mesmo quando definido qual propriedade ser OID, o valor deve ser atribuido manualmente, voc pode informar este valor antes de salvar o objeto ou utilizar o Evento OnNewObjectID do TdpoDBMappingManager. Definindo OID manualmente: var Pessoas: TPessoas; begin with Pessoas.Add do begin ID:= 1; Name:= 'John Doe'; Save end; end; Definindo OID no evento OnNewObject ID:

<2004> http://www.liws.com.br/depo

27

DePO - Delphi Persistent Objects

var dpoDBMM: TdpoDBMappingManager; procedure TfdmData.DataModuleCreate(Sender: TObject); begin dpoDBMM:= TdpoDBMappingManager.Create(Self); with dpoDBMM do begin Name:= 'dpoDBMM'; OnNewObjectId:= dpoDBMMNewObjectId; end; end; procedure TfdmData.dpoDBMMNewObjectId(Sender: TdpoClassMapping; KeyAttribute: TdpoAttributeMapping; var NewObjectId: Variant); var Guid: TGUID; begin CreateGUID(Guid); NewObjectId:= GuidToString(Guid); end;

3.10

Generalizao
DePO torna as operaes para classes generalizadas transparentes para suas classes bsicas, este comportamento til quando derivamos uma classe de outra e criamos uma nova tabela no banco de dados para armazenar somente as informaes relacionadas a esta nova classe. Exemplo: TPessoa = class(TdpoPersistentObject) published property ID: String read FID write FID; property Nome: String read FNome write FNome; end; TCliente = class(TPessoa) published property LimiteDeCredito: Currency read FLimiteDeCredito write FLimiteDeCredito; end; A classe TCliente uma generalizao de TPessoa, a seguir o mapeamento destas classes: with MappingManager.Classes.Add do begin ClassObject:= TPessoa; StorageName:= 'PESSOA'; with AttributesMapping do begin lAttribute:= Add; with lAttribute do begin AttributeName:= 'Id';
<2004> http://www.liws.com.br/depo

Trabalhando com Objetos

28

ColumnName:= 'OID'; IsOID:= True; IndexType:= idxUnique; Size:= 38; Required:= True; end; lAttribute:= Add; with lAttribute do begin AttributeName:= 'Nome'; ColumnName:= 'NOME'; Size:= 50; Required:= True; end; end; end; with MappingManager.Classes.Add do begin ClassObject:= TCliente; InheritsMappingFrom:= TPessoa; StorageName:= 'ClIENTE'; with AttributesMapping do begin lAttribute:= Add; with lAttribute do begin AttributeName:= 'Id'; ColumnName:= 'OID'; IsOID:= True; IndexType:= idxUnique; Size:= 38; Required:= True; end; lAttribute:= Add; with lAttribute do begin AttributeName:= 'LimiteDeCredito'; ColumnName:= 'LIMITEDECREDITO'; Required:= True; DataType:= adtCurrency; end; end; end; No exemplo acima, com todo este cdigo somente uma linha nos interessa: InheritsMappingFrom:= TPessoa; Esta linha identifica o relacionamento de generalizao entre TCliente e TPessoa, somente isto necessrio para que quando criarmos um objeto TCliente e salvarmos o DePO guarde as informaes nos campos respectivos de cada tabela PESSOA e CLIENTE, e quando recuperarmos ou excluirmos um objeto, o mesmo comportamente ocorrer. Note que em ambos registros o valor do campo OID ser identico pois do ponto de vista de objetos as informaes pertencem a um nico objeto do tipo TCliente.
<2004> http://www.liws.com.br/depo

29

DePO - Delphi Persistent Objects

Uma particularidade que a propriedade ID deve ser mapeada novamente, caso contrario o DePO no saber como efetuar resolver este relacionamento, se a propriedade no for declarada novamente, uma excesso ser disparada, como o exemplo a seguir:

Propriedades de tipo objeto, com relacionamentos em classes superiores devem ser declaradas novamente, seno o DePO no saber desta relao. Por exemplo: A classe TPessoa possui a propriedade Emails: TEmails, na generalizao de TCliente = class(TPessoa) a propriedade Emails existir, mas TdpoDBMappingManager s sabera de um mapeamento entre TPessoa e TEmails, assim sendo s poder retornar os emails de uma pessoa e nunca de um cliente. O exemplo a seguir mostra como deve ser este relacionamento: with MappingManager.Classes.Add do begin ClassObject:= TCliente; InheritsMappingFrom:= TPessoa; StorageName:= 'CLIENTE'; ... with RelationshipsMapping.Add do begin RelatedClass:= TPessoa; MasterAttribute:= 'Emails'; IsRetrieveCascaded:= True; IsSaveCascaded:= True; IsDeleteCascaded:= True; Bindings.Add('Id=PessoaOID'); end; end; O mapeamento deste relacionamento idntico ao mapeamento feito em TPessoa, mas essencial para que as operaes com Emails sejam transparentes.

<2004> http://www.liws.com.br/depo

Desenvolvendo Aplicaes

IV

31

DePO - Delphi Persistent Objects

4
4.1

Desenvolvendo Aplicaes
Relacionamento entre os Objetos
Como implementar o relacionamento entre TCliente e TEnderecos e TEmails Os passos necessrios so: 1) Declarar as propriedades para as colees e seus respectivos mtodos GET. 2) Declarar os campos responsveis pelo relacionamento do tipo TdpoRelationshipResolver. 3) Implementar os mtodos GET. 4) Mapear o relacionamento entre as classes 5) Registrar as listas para o controle das colees dos detalhes 1,2) A Interface da Classe: TCliente = class(TdpoPersistentObject) private FEmails: TdpoRelationshipResolver; FEnderecos: TdpoRelationshipResolver; ... function GetEmails: TEmails; function GetEnderecos: TEnderecos; published property Emails: TEmails read GetEmails; property Enderecos: TEnderecos read GetEnderecos; end; A implementao da criao dos relacionamentos procedure TCliente.AfterConstruction; begin inherited; FEmails:= Relationships.Add('Emails'); FEnderecos:= Relationships.Add('Enderecos'); end;

3) A Implementao dos mtodos GET: function TCliente.GetEmails: TEmails; begin if FEmails.RelatedObject = nil then begin FEmails.RelatedObject:= TEmails.Create(DBMechanism, Self, TEmail); FEmails.Retrieve; end; Result:= TEmails(FEmails.RelatedObject); end; function TCliente.GetEnderecos: TEnderecos; begin
<2004> http://www.liws.com.br/depo

Desenvolvendo Aplicaes

32

if FEnderecos.RelatedObject = nil then begin FEnderecos.RelatedObject:= TEnderecos.Create(DBMechanism, Self, TEndereco); FEnderecos.Retrieve; end; Result:= TEnderecos(FEnderecos.RelatedObject); end;

4) A Implementao do mapenamento do relacionamento with MappingManager.Classes.Add do begin ClassObject:= TCliente; StorageName:= 'CLIENTE'; with AttributesMapping do begin { O Mapeamento dos atributos ser coberto em outro captulo } end; with RelationshipsMapping.Add do begin RelatedClass:= TEmail; MasterAttribute:= 'Emails'; IsRetrieveCascaded:= True; IsSaveCascaded:= True; IsDeleteCascaded:= True; Bindings.Add('Id=ClienteOID'); end; with RelationshipsMapping.Add do begin RelatedClass:= TEndereco; MasterAttribute:= 'Enderecos'; IsRetrieveCascaded:= True; IsSaveCascaded:= True; IsDeleteCascaded:= True; Bindings.Add('Id=ClienteOID'); end; end; A funo RelationshipsMapping.Add adiciona um novo relacioanamento, por padrao em cada relacionamento adicionado as seguintes propriedades so definidas como padro: function TdpoDBRelationshipMappingCollection.Add: TdpoDBRelationshipMapping; begin Result:= TdpoDBRelationshipMapping(inherited Add); with Result do begin Cardinality:= OneToMany; IsRetrieveCascaded:= False; IsSaveCascaded:= False; IsDeleteCascaded:= False;
<2004> http://www.liws.com.br/depo

33

DePO - Delphi Persistent Objects

end; end; 5) A Implementao do Registro das listas que controlam a coleo dos itens. with MappingManager do begin RegisterList(TEmail, TEmails); RegisterList(TEndereco, TEnderecos); end;

4.2

(Re)Utilizando os Objetos
Reaproveitamento de objetos. Para evitar ficar criando e destruindo sem critrio, e tambm para no ficar executando instrues SQL desnecessrias, devemos definir como trabalhar com os objetos. Trabalhar com colees de objetos, definindo os critrios de retorno um bom mtodo, assim quando definido um critrio e os objetos forem recuperados, podemos apenas navegar entre eles ai invs de sempre efetuar novas consultas, isto tambm vale para os detalhes do objeto, ou seja, propriedades que so objetos, entao para uma classe TCliente que tem a propriedade do tipo TEnderecos, uma forma de otimizar o processo que se os endereos ja forem recuperados, no precisamos recuperar novamente. Esta mtodologia no apropriada para casos em que os dados podem sofre alteraes constantes com origem em outra localizao, desta forma devemos ter uma forma de saber se os dados esto atualizados, mas como saber sem retornar todos os dados novamente? e se retornarmos todos os dados novamente apenas precisamos atualizar a tela, mas isto gera um trafego de rede desnecessrio se os dados j forem atualizados, e tambm a atualizao de tela consumir recursos do computador. Ento o problema comea a exigir uma anlise mais detalhada de cada caso, algumas solues so: - Criar uma propriedade TimeStamp para ao invs de trafegar todos os campos, podemos comparar atravs de um nico campo, e a cada vez que o registro for atualizado, atualizaremos este campo. - Outra forma a atualizao de um campo contendo o CRC do registro. - Em caso de estaes remotas, em que haja uma conexo de internet de baixa banda, podemos fazer o cache local com arquivos XML, trabalhando com DBExpress esta tarefa se torna muito fcil, e a cada vez que precisamos de um registro, apenas verificamos atravs do TimeStamp - Outra forma seria apenas atualizar os dados antes de serem efetuadas operaes no registro, o correspondente ao evento "OnBeforeEdit" do DataSet se encarregaria de chamar o mtodo que atualizar os registros. Nesto momento temos informaes suficientes para perguntar "Como o Delphi trata isto com os DataSets?". - Vou usar como exemplo o DBExpress/ClientDataSet (Midas) para ter um comparativo. Toda vez que o evento OnAfterScroll acionado, o Provider se encarrega de atualizar o registro atual, internamente o mtodo UpdateRecord chamado, fazendo com que o registro atual seja atualizado, este mtodo garante que sempre os registros sejam sempre os mais atualizados possveis, mas por outro lado o trafego de rede aumenta consideravelmente, h tambm outros incovenientes que

<2004> http://www.liws.com.br/depo

Desenvolvendo Aplicaes

34

podem ocorrer dependendo do banco de dados utilizados, no caso do Interbase e do Firebird, se a aplicao estiver com uma transao ativa e o registro for alterado por outro usurio, mesmo que a transao do outro usurio esteja efetivada, enquanto a transao atual de quem estiver fazendo consulta, no estiver finalizada, o banco dados se encarrega de ter estas informaes guardadas, para que por exemplo no ocorra a seguinte situao, um usurio faz uma consulta e v os resultados na tela (DBGrid ou Pr-Visualizando o Relatrio), ai quando ele manda pra impressora, alguns minutos DePOis, quando o mtodo scroll ocorre, o registro no seja atualizado, evitando assim que o usurio no tenha um relatrio impresso, diferente do que ele viu na tela.

<2004> http://www.liws.com.br/depo

Criando Interfaces para Usurios

Criando Interfaces para Usurios

36

5
5.1

Criando Interfaces para Usurios


Interfaces RAD
Com o componente TdpoDataSet voc pode desenvolver interfaces como com qualquer outro descendente de TDataSet.

Basta apenas defiinir as propriedades Mechanism e ObjectClassName, exemplo: dpoDataSet1.Mechanism:= DBMechanism; dpoDataSet1.ObjectClassName:= 'TInvoice'; dpoDataSet1.Open; As outras operaes comuns a DataSet esto disponveis: First, Previous, Nest, Last, Insert, Delete, Post, Cancel. O mtodo Post far com que os dados digitados nos componentes DBware (TDBEdit, TDBGrid, etc...) sejam armazenados nos atributos dos objetos, para que os objetos sejam persistidos voc deve chamar o mtodo ApplyChanges. dpoDataSet1.Edit; dpoDataSet1.FieldByName('Date').AsDateTime:= Now; dpoDataSet1.ApplyChanges;

Outras caractersticas: Se classe de lista do objeto estiver criada e registrada, o DePO a usar, caso contrrio ele criar uma lista genrica do tipo TdpoObjectList. Em "Trabalhando com Objetos" - "Listas (Colees)"
24

aprenda como criar e manter listas.

aconselhvel que se crie as listas de objetos personalizadas para as classes, pois TdpoObjectList tratar os objetos contidos na lista de forma genrica, no conhecendo detalhes de sua implementao, como propriedades que so classes. Critrio para recuperar os dados: Antes de chamar o mtodo open, voc pode definir critrios para recuperar os dados: dpoDataSet1.Close; dpoDataSet1.Criteria.Add(ctEqualTo, 'Nome', ['Criando aplicaes com DePO']); dpoDataSet1.Open;

<2004> http://www.liws.com.br/depo

Tcnicas Avanadas

VI

Tcnicas Avanadas

38

6
6.1
6.1.1

Tcnicas Avanadas
Tipos de dados
Imagem, Memo
Tipos de dados no comuns ou no suportados pelo banco de dados, precisam ser mapeados atravs dos eventos OnGetValue e OnSetValue, DePO j traz suporte para alguns tipos de dados (Imagem, Strings, GUID e Boolean), mas voc pode definir seus prprios mtodos. O tipo memo pode ser utilizado para trabalhar com TStringList como: RichText, StringStream, TStrings, etc.

Mapeamento Automtico Para trabalhar com tipos de dados Imagem e Memo, utilizando o mapeamento automtico, defina a propriedade TdpoDBMappingManager.AutoConvertDataValues:= [acdvImage,acdvMemo], e: Imagem: Defina a propriedade como TBitmap e no mapeamento, informe o DataType como adtImage Memo: Defina a propriedade como TStrings e no mapeamento, informe o DataType como adtMemo Exemplo: TCliente = class(TdpoPersistentObject) private ... FFoto: TBitmap; FObservacao: TStrings; published ... property Foto: TBitmap read FFoto write FFoto; property Observacao: TStrings read FObservacao write FObservacao; end;

with MappingManager.Classes.Add do begin ClassObject:= TCliente; StorageName:= 'CLIENTE'; with AttributesMapping do begin with Add('Foto', 'FOTO') do DataType:= adtImage; with Add('Observacao', 'OBSERVACAO') do DataType:= adtMemo; end; end;

Mapeamento manual
<2004> http://www.liws.com.br/depo

39

DePO - Delphi Persistent Objects

Um exemplo para Bitmap, por Cosimo de Michele No mapeamento devemos especificar as os mtodos GET e SET que sero chamados codificar e definir e adquirir os valores dos dados em Stream (Sim, voc pode usar estes mtodos para criptografar campos). with AttributesMapping.Add do begin AttributeName:= 'LogoPiccolo'; DataType:= adtImage; ColumnName:= 'LOGO16'; IndexType:= idxNone; IsOID:= False; OnGetValue:= TdpoAttributeGetSetValue.GetBitmap; OnSetValue:= TdpoAttributeGetSetValue.SetBitmap; end; Agora devemos criar uma classe como esta: TdpoAttributeGetSetValue = class class procedure GetBitmap(Attribute: TdpoAttributeMapping; AObject: TObject; var Value: Variant); class procedure SetBitmap(Attribute: TdpoAttributeMapping; AObject: TObject; Value: Variant); class procedure GetStringList(Attribute: TdpoAttributeMapping; AObject: TObject; var Value: Variant); class procedure SetStringList(Attribute: TdpoAttributeMapping; AObject: TObject; Value: Variant); end; class procedure TdpoAttributeGetSetValue.GetBitmap( Attribute: TdpoAttributeMapping; AObject: TObject; var Value: Variant); var Buffer: TStringStream; Intro: TObjectIntrospector; BMP: TBitmap; V: Variant; begin Buffer:= TStringStream.Create(''); Intro:= TObjectIntrospector.Create(AObject); try V:= Intro.AttributeValue[Attribute.AttributeName]; TVarData(V).VType:= VarByRef; BMP:= TVarData(V).VPointer; BMP.SaveToStream(Buffer); if BMP.Empty then Value:= Null else Value:= Buffer.DataString; finally Buffer.Free; Intro.Free; end; end; class procedure TdpoAttributeGetSetValue.GetStringList(
<2004> http://www.liws.com.br/depo

Tcnicas Avanadas

40

Attribute: TdpoAttributeMapping; AObject: TObject; var Value: Variant); var Buffer: TStringStream; Intro: TObjectIntrospector; lst: TStringList; V: Variant; begin Buffer:= TStringStream.Create(''); Intro:= TObjectIntrospector.Create(AObject); try V:= Intro.AttributeValue[Attribute.AttributeName]; TVarData(V).VType:= VarByRef; lst:= TVarData(V).VPointer; lst.SaveToStream(Buffer); if lst.Count = 0 then Value:= Null else Value:= Buffer.DataString; finally Buffer.Free; Intro.Free; end; end; class procedure TdpoAttributeGetSetValue.SetBitmap( Attribute: TdpoAttributeMapping; AObject: TObject; Value: Variant); var Buffer: TStringStream; Intro: TObjectIntrospector; BMP: Graphics.TBitmap; V: Variant; begin Buffer:= TStringStream.Create(VarToStr(Value)); Intro:= TObjectIntrospector.Create(AObject); try Buffer.Position:= 0; V:= Intro.AttributeValue[Attribute.AttributeName]; TVarData(V).VType:= VarByRef; BMP:= TVarData(V).VPointer; BMP.LoadFromStream(Buffer); finally Buffer.Free; Intro.Free; end; end; class procedure TdpoAttributeGetSetValue.SetStringList( Attribute: TdpoAttributeMadpping; AObject: TObject; Value: Variant); var Buffer: TStringStream; Intro: TObjectIntrospector; lst: TStringList; V: Variant; begin Buffer:= TStringStream.Create(VarToStr(Value)); Intro:= TObjectIntrospector.Create(AObject); try
<2004> http://www.liws.com.br/depo

41

DePO - Delphi Persistent Objects

Buffer.Position:= 0; V:= Intro.AttributeValue[Attribute.AttributeName]; TVarData(V).VType:= VarByRef; lst:= TVarData(V).VPointer; lst.LoadFromStream(Buffer); finally Buffer.Free; Intro.Free; end; end;

6.1.2

Boolean
Alguns bancos de dados no suportam o tipo Boolean, assim tambm podemos mapear este tipo atravs do mapeamento automtico ou manual. O suporte ao mapeamento automtico para os tipos boolean est disponvel da mesma forma que os tipos imagem, Strings e GUID, necessitando apenas que se defina o DataType como adtBoolean. Exemplo: TCliente = class(TdpoPersistentObject) private ... FPreferencial: Boolean; published ... property Preferencial: Boolean read FPreferencial write FPreferencial; end;

with MappingManager.Classes.Add do begin ClassObject:= TCliente; StorageName:= 'CLIENTE'; with AttributesMapping do begin with Add('Preferencial', 'PREFERENCIAL') do DataType:= adtBoolean; end; end;

O mapeamento automtico depende de que a propriedade AutoConvertDataValue do componente TdpoDbMappingManager tenha acdvBoolean ou acdvBooleanAsInteger (exclusivos), se acdvBooleanAsInteger estiver definido como True, a propriedade ColumnTypeForBoolean deve ter o valor Integer ou outro tipo numrico suportado pelo Banco de Dados e os valores salvos sero 0 para Falso e 1 para Verdadeiro, caso contrrio, ColumnTypeForBoolean dever ser CHAR(1), ou outro semelhante, e os valores sero T para verdadeiro e F para Falso. Se for necessrio os eventos OnGetValue e OnSetValue podem ser utilizados para personalizar campos boolean, basta apenas definir os mtodos no mapeamento e implement-los: with MappingManager.Classes.Add do begin

<2004> http://www.liws.com.br/depo

Tcnicas Avanadas

42

ClassObject:= TCliente; StorageName:= 'CLIENTE'; with AttributesMapping do begin with Add('Preferencial', 'PREFERENCIAL') do begin DataType:= adtBoolean; OnGetValue:= TCustomMaps.GetBoolean; // Class Function OnSetValue:= TCustomMaps.SetBoolean; // Class Procedure end; end; end;

6.1.3

TDateTime
Para Firebird e Interbase, quando mapear as classes, os campos do tipo TDateTime devem ser prdefinidos como adtTimeStamp: with MappingManager.Classes.Add do begin ClassObject:= TPerson; StorageName:= 'PERSON_TABLE'; with AttributesMapping do begin lAttribute:= Add; with lAttribute do begin AttributeName:= 'Nascimento'; ColumnName:= 'DT_NASCIMENTO'; DataType:= adtTimeStamp; end; end; end; Se estiver trabalhando com componentes dbware, antes de passar os valores para os TFields, teste se a propriedade no est com valor igual a zero, pois quando um TField recebe uma data de valor zero ele atribui a primeira data vlida, utilizada pelo Delphi "30/12/1899" if lCliente.DataNascimento <> 0 then cdsClientes.FieldByName('DataNascimento').AsDateTime:= lCliente.DataNascimento; if lCliente.QualquerHora <> 0 then cdsClientes.FieldByName('QualquerHora').AsDateTime:= lCliente.QualquerHora;

6.2

Clonando Objetos
A classe TdpoPersistentObject implementa o mtodo AssignTo como protected, desta forma todas as classes derivadas de TdpoPersistentObject podero ser clonados. Exemplo: var

<2004> http://www.liws.com.br/depo

43

DePO - Delphi Persistent Objects

Person: TPerson; ClonedPerson: TPerson; Person:= TPerson.Create(DBMechanism); ClonedPerson:= TPerson.Create(DBMechanism); { definindo as propriedades de Person } Person.Name:= 'Cesar Romero'; Person.Email.Add('cesar@liws.com.br'); Person.Email.Add('DePO@liws.com.br'); { Clonado Person para ClonedPerson } Person.AssignTo(ClonedPerson); Criei um gmeo meu :) Isto mesmo, como se criasemos um gmeo, pois quando voc executa o seguinte mtodo: ClonedPerson:= Person; ClonedPerson armazena um ponteiro para Person, ou seja Person e ClonedPerson so o mesmo objeto, se voc destruir um, o outro tambm ser excludo, pois so o mesmo. Mas com AssignTo, voc faz uma cpia idntica, mas se uma for destruda a outra continuar intacta.

6.3

Utilizando Proxy
De acordo com Scotty Ambler, Proxy um objeto que identifica outro, mas sem a sobrecarga que o representado tem, ou seja, em todas as propriedades. Um proxy deve conter informaes suficientes para que ambos, o computador e o usurio possa identific-lo e nada mais. Por exemplo o proxy para uma pessoa deve conter seu OID para que a aplicao possa identifica-lo e Nome e Iniciais para representa-lo e o usurio poder identificlo. Proxys so comuns em listas de objetos para que o usurio possa selecionar somente os desejados, aps a seleo somente o objeto selecionado ser recuperado completo. Utilizando proxy voc pode reduzir a carga de informaes que iro trafegar na rede. Ao invs de representar o proxy por outro objeto, DePO implementa Proxy como uma lista de campos que voc deseja, esta lista a propriedade ProxyAttribute: TStringList da IdpoQuery, para incluir os campos. Esta implementao torna fcil a programao, dispensando a necessidade de se criar outra classe para ser proxy, de uma forma simples economizamos trafego de rede, por outro lado os objetos esto carregados na memria completos, mas sem valores nas propriedades. Exemplo: type TClientes = class(TdpoPersistentObjectList) private function GetItem(const Index: Integer): TCliente; reintroduce; public property Items[const Index: Integer]: TCliente read GetItem; default; function Add: TCliente; reintroduce; function New: TObject; reintroduce; end; TCliente = class(TdpoPersistentObject) published property ID: String read FID write SetID;

<2004> http://www.liws.com.br/depo

Tcnicas Avanadas

44

property property property property property property end;

Nome: String read FNome write SetNome; UltimoNome: String read FUltimoNome write FUltimoNome; Iniciais: String read FIniciais write FIniciais; Telefone: String read FTelefone write FTelefone; Foto: TBitmap read GetFoto write SetFoto; Observacao: TStrings read GetObservacao write SetObservacao;

var DBMechanism: TdpoCustomMechanism; Clientes: TClientes; lCliente: TCliente; lCliente:= TCliente.Create(DBMechanism); Clientes:= TClientes.Create(DBMechanism, TCliente); Clientes.Query.ProxyAttribute.Add('ID'); Clientes.Query.ProxyAttribute.Add('Nome'); Clientes.Populate; No exemplo acima utilizamos a propriedade Query para definir o proxy. Outra forma de utilizar Proxy definindo diretamento quando executamos a query no TdpoCustomMechanism, como no exemplo a seguir: with DBMechanism.CreateQuery(TCliente) do begin ProxyAttribute.Add('ID'); ProxyAttribute.Add('Nome'); Open; end;

6.4

Critrios para recuperar Objetos


Recuperar Objetos por Critrios, Bsico, por Paulo Roberto Quicoli O DePO oferece critrios para seleo de objetos que operam como os comparadores SQL ( AND, LIKE, >=, ISNULL, etc). A lista abaixo mostra os critrios para se comparar um campo a um valor: ctEqualTo ctLike ctGreaterOrEqualThan ctGreaterThan ctLessOrEqualThan ctLessThan ctBetween ctIn ctIsNull ctCustom Utilizando os Critrios Nada explica melhor do que um pequeno exemplo comentado. Segue abaixo: FContatoList:= TContatos.Create(DBMechanism, TContato); FContatoList.Criteria.Add(ctIsNull, 'ID', [Null], True);

<2004> http://www.liws.com.br/depo

45

DePO - Delphi Persistent Objects

FContatoList.Populate; 1 linha : Criamos nossa lista de objetos, que contm objetos do tipo TContato; 2 linha : Atravs do mtodo Criteria.Add incluimos um criterio para busca. no exemplo acima estamos recuperando os objetos que possuam o atributo 'ID' nulo. O SQL gerado pelo criteria algo assim: SELECT ... FROM CONTATO WHERE NOT (ID IS NULL) 3 linha : Recuperamos os objetos. Veja a assinatura da funo function Add(ACriterionType: TdpoCriterionType; AAttribute: String; const AValue: array of Variant; AIsNOT: Boolean = False): TdpoCriterion; overload; Parmetros: ACriterionType - um dos critrios mencionados acima AAttribute - Nome do atributo da tabela AValue - Array de valores a serem comparados AIsNOT - Se for TRUE, acrescenta negao comparao. Vide exemplo acima. Combinando Critrios Atravs do mtodo Add, voc pode adicionar mais de um critrio para recuperao dos objetos. Todo critrio de seleo adicionado pelo mtodo Add, ir gerar uma clusula AND ao WHERE. Veja exemplo: Root:= TCategories.Create(dpoADOMechanism1, TCategory); Root.Criteria.Add(ctIsNull, 'FatherOID', [Null], True); Root.Criteria.Add(ctLike, 'DESCRIPTION',['Ce']); Root.Populate; Atravs dessa configurao gerado o seguinte SQL : SELECT CATEGORY.ID, CATEGORY.DESCRIPTION, CATEGORY.ID_FATHER FROM CATEGORY WHERE NOT(CATEGORY.ID_FATHER IS NULL) AND CATEGORY.DESCRIPTION LIKE:P0 Obs.:O valor 'Ce' ser passado para a sentena atravs do parmetro :P0 Para adicionar uma clusula OR, temos o mtodo AddOR, conforme demonstrado abaixo: Root:= TCategories.Create(dpoADOMechanism1, TCategory); Root.Criteria.Add(ctIsNull, 'FatherOID', [Null], True); Root.Criteria.AddOr(ctLike, 'DESCRIPTION',['Ce']); Root.Populate; Que resulta na seguinte sentena SQL: SELECT CATEGORY.ID, CATEGORY.DESCRIPTION, CATEGORY.ID_FATHER FROM CATEGORY WHERE NOT(CATEGORY.ID_FATHER IS NULL) OR CATEGORY.DESCRIPTION LIKE:P0 Obs.:O valor 'Ce' ser passado para a sentena atravs do parmetro :P0

<2004> http://www.liws.com.br/depo

Tcnicas Avanadas

46

6.5

Ordem de recuperao (Sort)


Ordenao de Resultados, por Paulo Roberto Quicoli Pode-se tambm ordernar os objetos retornados. O DePO disponibiliza o mtodo OrderBy.Add para realizao de ordernaes. Veja o exemplo: Root:= TCategories.Create(dpoADOMechanism1, TCategory); Root.Criteria.Add(ctIsNull, 'FatherOID', [Null], True); Root.Criteria.AddOr(ctLike, 'DESCRIPTION',['Ce']); Root.OrderBy.Add('DESCRIPTION'); Root.Populate; Atravs dessa combinao ser gerada a seguinte sentea SQL: SELECT CATEGORY.ID, CATEGORY.DESCRIPTION, CATEGORY.ID_FATHER FROM CATEGORY WHERE NOT(CATEGORY.ID_FATHER IS NULL) OR CATEGORY.DESCRIPTION LIKE:P0 ORDER BY CATEGORY.DESCRIPTION ASC Veja a assinatura do mtodo OrderBy.Add: function Add(AAttribute: String; ADescending: Boolean = False): Parmetros: AAttribute: Atributo da tabela que ser utilizado para ordenao; ADescending: Indica se a ordenao ser Ascendente ou Descendente, sendo padro Ascendente; Combinando ordenaes Podemos tambm combinar ordenaes. Veja o exemplo: Root:= TCategories.Create(dpoADOMechanism1, TCategory); Root.Criteria.Add(ctIsNull, 'FatherOID', [Null], True); Root.Criteria.AddOr(ctLike, 'DESCRIPTION',['Ce']); Root.OrderBy.Add('DESCRIPTION', True); Root.OrderBy.Add('FatherOID'); Root.Populate; Resultar na seguinte sentena SQL: SELECT CATEGORY.ID, CATEGORY.DESCRIPTION, CATEGORY.ID_FATHER FROM CATEGORY WHERE NOT(CATEGORY.ID_FATHER IS NULL) OR CATEGORY.DESCRIPTION LIKE:P0 ORDER BY CATEGORY.DESCRIPTION DESC, CATEGORY.ID_FATHER ASC

6.6

Transaes
Transaes no DePO so tratadas de forma transparente, se nenhuma for definida manualmente, o DePO se encarregara de criar uma, se o mecnismo de persistncia suportar. Se um objeto possui relacionamentos, definidor por propriedades do tipo TdpoRelationshipResolver, e este relacionamento estiver com a propriedade "IsSaveCascaded = True", todos os objetos relacionados sero salvos na mesma transao.

<2004> http://www.liws.com.br/depo

47

DePO - Delphi Persistent Objects

Se uma transao for definida manualmente antes de chamar o mtodo save, DePO no criar outra transao enquanto esta transao no for finalizada atravs de Commit ou Rollback. Exemplo procedure TfrmMenu.sbtPopulateClick(Sender: TObject); var I, J: Integer; lFather: TPerson; lChield: TPerson; lTransaction: TTransactionDesc; ID: Integer; begin { Defining own transaction } ID:= 0; lTransaction.TransactionID:= ID + 1; lTransaction.IsolationLevel:= xilREADCOMMITTED; DB.StartTransaction(lTransaction); try for I:= 1 to 10 do begin lFather:= lotPeople.Add; { OID as Sequence, will raise if Populate more than once } lFather.ID:= IntToStr(I); lFather.Name:= Format('Father %d', [I]); for J:= 1 to 15 do begin lChield:= lFather.Chields.Add; lChield.ID:= Format('%d.%d', [I, J]); lChield.Name:= Format('Chield %d.%d', [I, J]); lChield.FatherOID:= lFather.ID; end; { Save Father and all Chields } lFather.Save; end; DB.Commit(lTransaction); except DB.Rollback(lTransaction); end; end;

<2004> http://www.liws.com.br/depo

Extras

VII

49

DePO - Delphi Persistent Objects

7
7.1

Extras
ModelMaker
Para utilizar os componentes do DePO com o ModelMaker basta importar a unit dpoPersistanceObject e os objetos base estaro disponveis para serem generalizados e transformados em Objetos de Negcio. Para importar uma unit basta clicar com o boto direito na "View Unit (F4)" e selecionar "Import unit..."

No dilogo "Select aliased Path..." clique em "<No alias>"

Selecione o arquivo "dpoPersistanceObject.pas"

<2004> http://www.liws.com.br/depo

Extras

50

Para no criar vnculo com o arquivo da unit, evitando que alteraes no modelo afetem a unit no DePO, clique no Boto "Interfaces", desta forma o ModelMaker selecionar Membros e Opes de importao, para ter o cdigo dos mtodos disponveis no ModelMaker, marque a opo "Include Method Implementation". Clique no boto "Abrir" e o dilogo "Import classes" trar a lista de todas as interfaces, classes, registros e eventos disponveis na unit, selecione as classes TdpoPersistentObject e TdpoPersistentObjectList.

<2004> http://www.liws.com.br/depo

51

DePO - Delphi Persistent Objects

Clique em OK e as classes estaro disponveis para utilizar no ModelMaker na View Classes

Desta forma quando for adicionar uma nova classe, basta selecionar o Ancestor desejado, TdpoPersistentObject ou TdpoPersistentObjectList:

<2004> http://www.liws.com.br/depo

Extras

52

Para definir o relacionamento "TItems.Items[Index] -TInvoiceItem" no dilogo Property Association, defina: ReadAccess como Method WriteAccess como None Marque "array property",e Marque "Default array property"

<2004> http://www.liws.com.br/depo

53

DePO - Delphi Persistent Objects

<2004> http://www.liws.com.br/depo

Histria

VIII

55

DePO - Delphi Persistent Objects

Histria
08/08/2004 - DePO (Novo) TdpoDataSet

Corrigido bugs de operaes em cascata - DePO Manual Arquitetura - Mapeamento de Objetos - De Objetos para RDB, por Marcos Barreto Trabalhando com Objetos - Listas Colees, atualizado Trabalhando com Objetos - Problemas com Operaes em Cascata Criando Interfaces para usurios - Interfaces RAD (TdpoDataSet) Extras - ModelMaker Novo Logotipo, por Marcos Barreto 58 pginas 29/07/2004 - DePO (Novo) Implementado InternalConnection para DBXMechanism e ADOMechanism, uma conexo interna chamada InternalConnection ser criada, mas uma personalizada poder ser selecionada, neste caso a InternalConnection ser destruda. A propriedade DBXConnection do DBXMechanism foi eliminada e Connection sobrescrita como TSQLConnection Ateno: Quando abrir os Forms que contm o componente TdpoDBXMechanism, Delphi mostrar uma mensagem de erro, dizendo que a propriedade DBXConnection no existe, pressione OK para continuar, se em seu cdigo voc estiver usando esta propriedade substitua por Connection.

Sugesto de Marcos e Paulo (Novo) cones para componentes DePO: TdpoDBMappingManager, TdpoDBXMechanism e TdpoADOMechanism

(Novo) Criada a propriedade TdpoDBMappingManager.AutoConvertDataValues = (acdvImage, acdvMemo, acdvGUID, acdvBooleanAsInteger), para mapeamento automtico de tipos de dados no suportados pelo banco de dados, como Imagem, Strings e Boolean no Firebird e Interbase. Estas alteraes dispensam a necessidade que havia de se criar os Get's e Set's para estes tipos de dados. Sugesto de Marcos

<2004> http://www.liws.com.br/depo

Histria

56

Critrio: Between e IN por Paulo R. Quicoli em 26/07/2004 Corrigido problemas com excluso em cascata no mtodo TdpoCustomMechanism.DeleteList, sugesto de AledeOl - DePO Manual Tcnicas Avanadas - Tipos de dados - Imagem, Memo Tcnicas Avanadas - Tipos de dados - Boolean 45 pginas - Exemplos (Novo) Exemplo Enumeration: mostra como utilizar tipo enumerados: TRealizacao = (reDiario, reDomingo, reSegunda, reTerca, reQuarta, reQuinta, reSexta, reSabado); Many, atualizado para utilizar TdpoDBMappingManager.AutoConvertDataValues para Boolean, Imagem e Memo.

22/07/2004 - DePO TdpoDBClassMapping, criada as propriedades ClassObjectName e InheritsMappingFromName para mapeamento diretamente na IDE do delphi no Object Inspector. dpoClassMapping, dpoPersistanceObject, implementada operaes em cascata para Generalizao (herana). - DePO Manual Arquitetura - Declarando Objetos (no finalizado) Arquitetura - Mapeando Objetos Arquitetura - Sincronizando o banco de dados Trabalhando com Objetos - Operaes em Cascata Trabalhando com Objetos - Generalizao 40 pginas - Exemplo Cascate Herana, exemplo de derivar uma classe e o DePO salvar automaticamente a classes de que derivada.

21/04/2004 - DePO

<2004> http://www.liws.com.br/depo

57

DePO - Delphi Persistent Objects

dpoDatabaseMechanism.TdpoSQLSelectStatement function TdpoSQLSelectStatement.GetSQLSelectFrom: String; Quando havia ProxyAttribute na query, o nome da tabela se repetia N vezes campos, gerando FROM errado no SQL dpoPersistanceObject.TdpoObjectList Includa a propriedade Query como pblica, se para poder utilizar Proxy com TdpoPersistentObjectList. dpoDBXMechanism TdpoDBXCommand.SetParamList e TdpoDBXQuery.SetParamList Eliminado cdigo desnecessrio para definir parmetros. - Exemplo DBWare Includo proxy na busca dos registros Definido o DataType dos campos dos tipos TDateTime, TImage/TBitmap e TSream no mapeamento das classes. - DePO Manual Arquitetura - Classes e Propriedades Trabalhando com Objetos - Herana Tcnicas Avanadas - Critrios para recuperar, por Paulo Roberto Quicoli Tcnicas Avanadas - Ordem de recuperao, por Paulo Roberto Quicoli Tcnicas Avanadas - Utilizando Proxy Tcnicas Avanadas - Clonando Objetos Tcnicas Avanadas - Tipos de dados - "Imagem, RTF, Stream", Boolean e TDateTime Atualizado "Todo List" e "Problemas conhecidos", itens implementados foram eliminados

20/04/2004 - Criado um novo exemplo Cascate.dpr: Salvar em cascata Excluir em cascata Gerenciar Transaes em DBExpress manualmente Utilizar OID de origens diferentes na mesma tabela Objeto pai Popular TListBox com Objetos - DePO Manual Tcnicas Avanadas - Transaes Tcnicas Avanadas - Campos Blob (Imagem, RTF), por Cosimo de Michele Trabalhando com Objetos - OID 19/04/2004 - Correes nos parmetros da TDBXCommand e TDBXDataSet para trabalhar com campos TDateTime, Boolean e Blob, testado com firebird - Implementado Transaes no DBXMechanism

<2004> http://www.liws.com.br/depo

Potrebbero piacerti anche