Sql Compact 2008 – Esse ilustre desconhecido

by Cássio R Eskelsen 10. janeiro 2009 09:36

Creio que uma das edições menos conhecidas (ou mais desprezadas) do Sql Server seja a Compact Edition.

Antigamente sempre sonhávamos com um banco de dados simples para pequenas aplicações e que não requeresse a instalação de um monstrengo (ou a utilização de aberrações como o Access e/ou Paradox). Creio que hoje em dia como boa parte das aplicações está indo para a WEB e existem outras alternativas (como armazenar dados em XML), a procura por uma versão lightweight  de gerenciador de banco de dados tenha diminuido.

No entanto, sempre existirão as aplicações “Offline” que precisam rodar em um notebook desconectado, um smartphone ou PDA, ou ainda, uma aplicação leve que você quer distribuir pela Net. Para esses casos o Sql Compact 2008 é perfeito.

O Sql Compact é embedded, ou seja, roda no contexto da aplicação, não requerendo a instalação de um servidor. Obviamente isso tem algum custo e o custo é o corte de algumas features. Você não pode, por exemplo, rodar stored procedures.

Longe de querer fazer um tutorial extensivo, quero com esse post apenas relacionar alguns recursos disponíveis na net e passar algumas dicas (coisas que estou aprendendo aqui na porrada).

Links Interessantes:

A página do produto: http://www.microsoft.com/Sqlserver/2008/en/us/compact.aspx

Microsoft SQL Server Compact 3.5 Books Online and Samples (versão offline): http://www.microsoft.com/downloads/details.aspx?FamilyID=1ff0529a-eb1f-4044-b4b7-40b00710f7b7

Tutorial: Introducing Microsoft SQL Server Compact Edition (Part I)(parte 2 linkada no texto. A parte dois trata dos serviços de sincronização)

Veja abaixo algumas dicas rápidas: 

Leia mais...

Tags:

.Net | Banco de Dados

db4oMembership Provide

by Cássio R Eskelsen 5. agosto 2008 22:44

(publicado originalmente em 16/11/2005)

A versão 2.0 do framework introduziu novas funcionalidades ao sistema de autenticação de páginas da versão 1.1 do framework.

Uma das mudanças é a classe Membership Provider, que mantém as informações do cadastro de usários que podem autenticar na página
Essa classe originalmente vem implementada para o banco de dados Sql Server e autenticação via Active Directory, mas pode ser implementada para outros bancos.

Eu comecei um projeto no source forge para implementar essa classe para o banco de objetos db4o. 

Atualmente já está funcionando:

- Criação de usuários
- Exclusão de usuários
- Login de usuários
- Registro do último login
- Envio de senha perdida
- Alteração de senha
- Alteração da pergunta/frase secreta
- Alteração de dados do usuário
- Listar todos os usuários

Se você encontrar algum bug ou tiver alguma sugestão, não exite para entrar em contato!

Tags: ,

Banco de Dados | Web | .Net

db4o - Parte II - Modos de Acesso, Configuração, Performance e Outras Opções

by Cássio R Eskelsen 5. agosto 2008 18:35

(publicado originalmente em 2005)

O primeiro artigo da série dessa série teve uma repercussão surpreendente. Imaginava que havia um interesse por tecnologias pós-relacionais, mas não imaginei que o interesse fosse tão grande assim. Em contato com a equipe do db4o  foi me revelado que houve um número alto de visitas a partir do site Linha de Código e que antes desse artigo o Brasil já era o 7º colocado em visitas, dentre os mais de 190 países que já visitaram o site www.db4o.com e com isso, conquistamos um fórum em português no site do db4o.

Nesse segundo artigo vou começar a aprofundar alguns detalhes na utlização do db4o: modos de acesso, configuração, performance, índices, operações em cascata, etc.

Modos de Acesso

O db4o possui 3 modos de acesso: direto, cliente/servidor e cliente/servidor embutido.

Acesso direto

O acesso direto foi o modo utilizado no exemplo do primeiro artigo. Nesse modo acessamos o arquivo com os objetos diretamente, da mesma forma que se faz quando se trabalha com uma "base" Access:

ObjectContainer _container = Db4o.OpenFile("arquivo.yap");

Esse modo de acesso deve ser utilizado preferencialmente apenas em aplicações desktop standalone, aplicações para dispositivos móveis e aplicações embarcadas. Em aplicações onde há acesso simultâneo de 2 ou mais usuários (aplicações web e c/s) esse tipo de acesso não é recomendado.

Em aplicações standalone você pode abrir um ObjectContainer no início da aplicação e deixá-lo aberto por todo tempo de vida da aplicação. Isso vai facilitar o seu desenvolvimento.

Acesso client/server

Essa é a clássica forma onde há um programa "servidor" e um ou mais programas"clientes" acessando esse servidor.

O db4o permite trabalhar com servidores de uma forma transparente. A classe container é a mesma, mas agora ela é instanciada através de Db4o.OpenClient ao invés de Db4o.OpenFile. Obviamente você precisa criar uma aplicação a mais, que será o seu "servidor".

O programa servidor deverá ter as seguintes linhas de código:

ObjectServer db4oServer = Db4o.OpenServer("arquivo.yap", porta); 
db4oServer.GrantAccess(nomeusuario1,senhausuario1); 
db4oServer.GrantAccess(nomeusuario2, senhausuario2); 
... 
db4oServer.GrantAccess(nomeusuariox,senhausuariox);

 

Db4o.OpenServer abre um arquivo com objetos e monta o servidor na porta indicada (um número de porta TCP/IP). Não existe um valor padrão para a porta.

db4oServer.GrantAccess define o nome e senha dos usuários que podem acessar esse servidor. Você pode criar um  usuário padrão ou definir um nome e senha para cada usuário.

Do lado cliente, você deverá fazer:

 
ObjectContainer _container = Db4o.OpenClient(host, porta, usuário, senha);

 

O host pode ser o nome da máquina (se for uma rede local) ou o número do IP do servidor.

Algumas observações sobre o modo cliente/servidor:

  • As operações cliente/servidor são realizadas na forma read commited, ou seja, um container só verá as alterações já "commitadas" por outros. Não é necessário abrir uma transação explicitamente: o db4o sempre mantém uma transação ativa para cada container, no entanto, para confirmar a transação é necessário chamar o método Commit() do container;
  • O servidor deverá ter conhecimento das mesmas classes que o cliente, ou seja, se você tiver um projeto com suas Classes e/ou regras de negócio, você deverá fazer referência a esse projeto em seu projeto de Servidor.

Acesso client/server embutido

O db4o permite que você inicie um servidor no mesmo escopo de sua aplicação, ou seja, ao invés de ter um servidor "externo", você terá um servidor dentro de sua aplicação. Isso é especialmente útil em aplicações WEB, onde você tem várias usuários acessando a mesma aplicação ao mesmo tempo e não tem como iniciar um servidor em separado (afinal, como você faria para iniciar um servidor dentro de uma máquina de um provedor?). Também é interessante usar esse método quando suas aplicações standalone necessitarem abrir vários containers simultâneos.

Para iniciar um servidor embutido, você usuará o mesmo método OpenServer do modo cliente/servidor mas iniciará o servidor na "porta" zero. Não é necessário definir os usuários que podem acessar pois o servidor será visível apenas dentro da sua aplicação. Uma aplicação externa não terá acesso pois o container deverá ser iniciado a partir do ObjectServer criado, dessa forma:

           
ObjectServer server = Db4o.OpenServer("arquivo.yap", 0);
try
{
ObjectContainer client = server.OpenClient();
// operações......
client.Close();
}
finally
{
server.Close();
}

 

Fora esses detalhes na conexão, o restante das operações é idêntico ao modo Cliente / Servidor.

Como muitos já devem estar imaginando, o objeto "ObjectServer" deve ser instanciado uma única vez em toda a aplicação. Esse é um caso típico para aplicação do pattern Singleton. Segue abaixo uma sugestão de aplicação do pattern para garantir que você terá sempre apenas um objeto Server:

class Server
{
private static ObjectServer server;   
private static object syncLock = new object();
protected Server()
{
server = Db4o.OpenServer("arquivo.yap", 0);
}
public static ObjectServer GetServer()
{
if (server  == null)
{
lock (syncLock)
{
if (server  == null)
{
new Server();
}
}
}
return server;
}
}       

 

Para obter uma instância de ObjectContainer, você faria:

ObjectContainer client = Server.GetServer().OpenClient();

Uma observação importante sobre os containers

Para gravar um novo objeto ou atualizar um objeto existente, o db4o disponibiliza o mesmo comando : ObjectContainer#Set(objeto).  Mas aí vem a pergunta:

"Como que o db4o sabe que deve atualizar ou inserir um novo objeto?"

Se o objeto foi carregado em um container, ele estará em seu cache, e assim o db4o sabe que deverá atualizá-lo. Caso contrário, o db4o criará uma nova instância do objeto na base. Então, para atualizar um objeto, você sempre deverá primeiramente carregá-lo na memória a partir da base.

Esse conceito é importante ter em mente pois você deve tormar um cuidado especial para não carregar um objeto em uma instância de um container e salvar em outra instância.

Para entender isso melhor, vamos voltar ao exemplo que criei no primeiro artigo, especialmente nessa parte:

Autor raul = ProcuraAutor("Raul Wazlawick"); 
Console.WriteLine("Autor: " + raul); 
Console.WriteLine("-----LIVROS DO AUTOR (usando Native Queries)-----");

 

Se após esse trecho de código você quisesse alterar o nome do autor para "Raul W.", talvez pensasse em fazer algo parecido com isso(agora já usando nosso singleton):

Autor raul = ProcuraAutor("Raul Wazlawick"); 
raul.Nome = "Raul W.";
ObjectContainer container = Server.GetServer().OpenClient();
container.Set(raul);
container.Close();            

 

Sinto informar que isso não vai funcionar pois o objeto "raul" foi instanciado a partir de um container (o que foi aberto dentro da função ProcuraAutor) e você salvou usando o método Set de outra instância. Na prática o que vai acontecer é a duplicação do objeto na base. Esse é um dos erros mais comuns quando se começa a utilizar o db4o.

Uma das soluções nesse caso seria utilizar o mesmo objeto container onde foi carregado o objeto. Se você verificar aquele código verá que existe um objeto container "global" de nome objectBag. Vamos usá-lo:

Autor raul = ProcuraAutor("Raul Wazlawick"); 
raul.Nome = "Raul W.";
objectBag.Set(raul);

 

Configurações

O db4o permite uma série de configurações. Algumas merecem atenção especial para melhorar a performance de utilização.

É importante observar que as configurações devem ser aplicadas ANTES de "startar" o servidor ou ANTES de abrir o arquivo(no caso do acesso direto). Elas não são gravadas junto com a base.

Vou listar algumas configurações importantes.

Db4o.Configure().Freespace().UseIndexSystem() ou Db4o.Configure().Freespace().UseRamSystem();

Quando um objeto é apagado ou atualizado, o espaço ocupado anteriormente pelo mesmo na base, é marcado como "livre" para que outros objetos possam ocupar esse espaço.O db4o possui duas formas para "lembrar" quais são os espaços livres:

UseRamSystem

Mantém na memória duas listas ordenadas por endereço e tamanho.

Vantagens: rápido
Desvantagens: ocupa muita memória quando ocorrem muitas operações de atualização e exclusão e pode haver perda das listas no caso de um desligamento inesperado do sistema

UseIndexSystem

Mantém um sistema de controle de espaço livre baseado em índices.

Vantagens:ACID, não é perdida nenhuma informação a respeito dos espaço livres; baixo consumo de memória; .
Desvantagens:mais lento que o método baseado em RAM, pois a informação sobre os espaços livres é gravada junto a cada commit

A segunda forma passou a ser a opção default na versão 5.0 do db4o, no entanto, para aplicações onde não se espera tanta queda/desligamento abrupto   do sistema, a opção mais indicada é a primeira. Para alterar isso, basta adicionar a seguinte linha de código:

 
Db4o.Configure().Freespace().UseRamSystem();

Para aplicações baseadas em dispositivos móveis é mais recomendado que voê utilize o IndexSystem.

Db4o.configure().callConstructors(true);

Em máquinas virtuais onde isso é suportado, o db4o cria instâncias dos objetos sem chamar um construtor e para isso ele usa reflection. No entanto, o processo de reflection é um pouco mais lento do que chamar um construtor pré-definido. Se você quer melhorar a perfomance do seu aplicativo, crie sempre um construtor sem parâmetros para suas classes persistidas e chame o método .callConstructors() passando o parâmetro true. Dessa forma o db4o sempre criará instâncias de suas classes chamando o construtor.

Alternativamente, você pode definir isso por classe, ao invés da forma global como exposto acima. Para isso chame:

 
Db4o.Configure().ObjectClass(sua_classe).CallConstructor(true);

Alterando o tamanho da base

Por padrão, as bases de dados do db4o podem ter no máximo 2 Gb. Aumentando o tamanho do bloco que o db4o utiliza internamente, o limite de tamanho pode ser aumentado em múltiplos de 2Gb. Qualquer valor entre 1 byte (2GB) e 127 bytes (254GB) pode ser usado. O valor default é 1

Deve-se tomar cuidado para não escolher um valor inapropriado pois pode ocorrer desperdício de espaço e também queda na performance. O valor ideal é 8

Para definir o comando você deve usar:

 
Db4o.Configure().BlockSize(novo_tamanho_do_bloco); 
Defragment.main(new String[] {"suabase.yap"}); 

Ao invés de usar uma única gigantesca base, você também pode criar várias bases menores.

Performance

Abaixo listarei algumas dicas para melhoria da performance das aplicações que usam db4o.

Desligar callbacks

O mecanismo de callback permite que métodos com uma determinada assinatura (você pode ver uma lista nesse link ) sejam chamados em algumas situações, como por exemplo CanDelete, que verifica se um objeto pode ou não ser apagado. No entanto, para verificar se a classe implementa ou não esse método é feito um "escaneamento" na classe em busca desse método. Caso você não utilize o mecanismo de callback, pode desligá-lo usando o seguinte comando:

 
Db4o.Configure().Callbacks(false);

Desligar verificação automática de mudança de estrutura

Cada vez que o sistema é iniciado, o db4o, através de reflection, verifica se houve alguma alteração na estrutura da classe (novos campos, etc).

Se você se sentir seguro, pode desabilitar essa verificação automática, cuidando para que quando ocorrer alguma mudança na estrutura, não esquecer de reativar esse recurso para que a estrutura da classe na base seja atualizada.

Para desabilitar, você precisa passar o seguinte comando:

 
Db4o.Configure().DetectSchemaChanges(false);

Desfragmentação

Algumas situações geram um pouco de "lixo" na base do db4o:

  • Sempre que um objeto é alterado, ele é gravado em uma nova posição, e a antiga continua a ocupar 8 bytes, até que seja rodada uma fragmentação.
  • Quando você remove um campo de uma classe, o db4o não elimina esse campo da base, apenas o "oculta"

O processo de fragmentação elimina todo esse lixo copiando todos os objetos para um novo arquivo e deve ser executado regularmente. Para executar a desfragmentação, rode o seguinte trecho de código:

 
new Defragment().Run("seuarquivodeobjetos", delete);

Índices

Como em qualquer banco, o db4o permite que sejam definidos índices para melhorar a performance de leitura

Da mesma forma que os outros comandos de configuração, esse precisa ser dado ANTES de abrir um objectContainer ou objectServer, no entanto, no entanto, uma vez definido o índice, ele não precisa ser re-definido a cada abertura da base.

Baseado no exemplo anterior, se quiséssemos definir um índice no campo "Nome" da classe Autor, usaríamos o seguinte comando

Db4o.Configure().ObjectClass(typeof(Autor)).ObjectField("Nome").Indexed(true);

Como é de se imaginar, para retirar o índice, basta mudar o parâmetro da função Indexed para false:

Db4o.Configure().ObjectClass(typeof(Autor)).ObjectField("Nome").Indexed(false);

Cenas dos próximos capítulos...

Com esses comandos básicos já é possível "afinar" a utilização do db4o. Existem outras opções, mas iremos com calma.

Se você quiser ver uma aplicação "real" usando o db4o, pode ver o provider que criei para o asp.net 2.0. Esse provider implementa as funções de login e regras de acesso disponíveis no asp.net 2.0. O código é LGPL e está disponível no Source Forge.

Até a próxima!

Tags:

Banco de Dados

db4o - Banco de Dados Orientado a Objetos

by Cássio R Eskelsen 5. agosto 2008 18:29

(publicado originalmente em 2005)

Parte I - Introdução

De uns tempos para cá tenho percebido uma procura crescente por ferramentas que facilitem a integração entre o mundo orientado a objetos (a linguagem e o framework) e o mundo relacional (o banco de dados). As tais ferramentas de mapeamento objeto relacional nada mais são do que um "tradutor" entre duas línguas totalmente diferentes.

Em todas as traduções você acaba perdendo as sutilezas de uma língua ou tendo que usar muito mais palavras para expressar um conceito que é relativamente simples na língua de origem. No mapeamento O/R não é diferente: você acaba perdendo uma série de recursos da programação OO, ou tendo que escrever muito mais código para simular no banco de dados algo simples na linguagem (como por exemplo, uma propriedade do tipo array ou o gerenciamento de uma herança).

Particularmente acredito que estaremos em um mundo perfeito quando todos os banco de dados permitirem que você simplesmente pegue seu objeto do jeito que ele está e jogue-o no banco de dados(vou passar a chamar de banco de objetos) sem se preocupar com camadas e mais camadas de código para "traduzir" um objeto em query.

Já existem várias tentativas de se fazer isso, incluindo algumas muito avançadas como o Prevayler (bambooprevalence e xprevail nas encarnações .Net). No entanto, a falta de confiabilidade nos equipamentos e sistema operacional aliadas a uma certa dose de preconceito fazem com que essa solução ainda seja rotulada como "algo para o futuro".

Na outra ponta existem soluções como o LINQ da Microsoft, uma feature que estará disponível oficialmente no C# 3.0 mas que já pode ser testado agora. Não considero que isso seja verdadeiramente uma solução pois misturar conceitos relacionais (selects, wheres, etc) em uma linguagem OO como o C# é o que poderíamos chamar de "código alienígena".

Um banco realmente OO deve permitir que você faça suas consultas de forma orientada a objetos, como se estivesse pesquisando em uma Array, List ou qualquer outro container de objetos.
É aqui que se encaixa o db4o! Claro que existem outras soluções como o Caché, mas prefiro me ater ao db4o pelo seu custo acessível e por já estar mais integrado a plataforma .Net

db4objects

O db4objects(db4o) surgiu a alguns anos atrás inicialmente apenas para Java. Com a grande semelhança de código entre o .Net e o Java, foi um pulo para que fosse criada uma versão .Net. Hoje, as versões .Net e Java caminham lado a lado, tendo ambas os mesmos recursos.

Com esse banco você pode desenvolver aplicações WEB, Windows.forms e Compact Framework. Você não precisa instalar nem configurar um servidor de banco de dados. Basta enviar junto com sua aplicação uma pequena dll. Claro que você pode fazer uma aplicação cliente/servidor. O próprio db4o provê recursos para que isso seja feito, mas sempre de uma forma simples, sem a necessidade de ser um PHD em configuração de banco de dados.

O db4o tem um mecanismo de replicação muito útil para quem tem necessidade por exemplo, de manter bancos off-line parte do tempo e
on-line o restante (quem desenvolve para forças de venda deve imaginar o que estou falando!).

Quando falo em db4o, algumas perguntas são inevitáveis:

a) Não tenho problemas de performance? Não. Alguns testes mostram inclusive que o db4o é muito mais rápido que soluções que envolvam o uso de Nhibernate por exemplo. Veja benchmarks aqui: http://www.db4o.com/about/productinformation/benchmarks/

b) Ele não é caro como o Caché? Não. A licença do db4o é open-source dual como a do MySql, ou seja, se você está desenvolvendo para uso dentro de sua empresa, criando seu website ou desenvolvendo um programa GPL, ele é gratuito para você.
Mas mesmo que você precise distribuir sua aplicação, o custo da licença runtime é muito baixo.

db4o na prática

Nesse primeiro artigo sobre o db4o irei listar algumas operações básicas para que você veja a facilidade que é trabalhar com um banco origentado a objetos. Nos artigos seguintes irei mostrar um Membership provider para db4o e dicas de como deixar o trabalho com o db4o mais prático e rápido.

Instalação

Você pode puxar o db4o nesse link: http://www.db4o.com/community/ Os exemplos que farei já serão baseados na nova versão 5.0.
A instalação não fará nada mais do que descompactar os arquivos em um diretório.
Você terá a seguinte estrutura de diretórios
\dll: as dlls do db4o com duas versões: compact framework e para uso em PC
\doc: help e um excelente tutorial
\src: fontes do db4o e testes unitários que podem servir de fonte de estudo

Utilização

Para você utilizar o db4o em seus projetos basta referenciar a respectiva dll e adicionar um "using com.db4o"


Criando um database

Não existe no db4o um processo formal de criação do database nem das respectivas classes. A medida em que você for salvando seus objetos o db4o faz tudo para você.Em um código que usa o db4o como persistência tudo gira em torno de ObjectContainer. É o ObjectContainer que irá gravar, ler e alterar seus dados.
O arquivo de dados do db4o tem normalmente a extensão .yap.
A primeira operação que faremos é "conectar" no arquivo:

ObjectContainer objectBag = Db4o.OpenFile("arquivo.yap");

Isso abrirá o arquivo (se já existente) ou criará um novo. Claro que você pode colocá-lo em qualquer diretório em que sua aplicação tenha direito de acesso/gravação.
Pelo intellisense do Visual Studio você poderá ver que objectBag tem uma série de métodos e entre eles podemos citar:
Set: grava objetos na base
Get: recupera um objeto da base procurando-o através de um template
Delete: apaga um objeto
Query: faz uma pesquisa na base retornando uma lista do tipo ObjectSet
Query<T>: faz uma pesquisa retornando uma lista tipada
Commit e Rollback: confirma/anula uma transação (semelhante ao commit/rollback dos RDBMs)

Inserindo dados

Para nossos testes iremos criar duas classes muito simples:

 
    public class Autor
    {        
        private string _nome;
        private string _email;
        
        public string Nome
        {
            get { return _nome; }
            set { _nome = value; }
        }
        public string Email
        {
            get { return _email; }
            set { _email = value; }
        }
        public override string ToString()
        {
            return this.Nome+"("+this.Email+")";
        }
    }
    public class Livro
    {
        private Autor _autor;
        private string _titulo;
        private string _isbn;

        public string ISBN
        {
            get { return _isbn; }
            set { _isbn = value; }
        }	
        public string Titulo
        {
            get { return _titulo; }
            set { _titulo = value; }
        }
        public Autor Autor
        {
            get { return _autor; }
            set { _autor = value; }
        }
        public override string ToString()
        {
            return string.Format("Titulo: {0}\nAutor: {1}\nISBN: {2}\n\n", 
this.Titulo, this.Autor.Nome, this.ISBN); } }

Por default, O db4o grava o conteudo de todos os campos. Caso você não deseja gravar algum campo, você deve marcá-lo
com o atributo [Transient]
Note que não estou as classes não descendem de nenhuma classe base, e também não estou implementando nenhuma interface específica.
Vamos criar uma pequena massa de dados e salvá-los na base:
        static void CriaDados()
        {           

            Autor raul = new Autor();
            raul.Nome = "Raul Wazlawick";
            raul.Email = "raul@acme.com";

            Livro analise = new Livro();
            analise.Autor = raul;
            analise.ISBN = "8535215646";
            analise.Titulo = "Análise e Projetos de Sistemas de Informação Orientados a Objetos";
            objectBag.Set(analise);

            Autor fabio = new Autor();
            fabio.Nome = "Fabio Camara";
            fabio.Email = "fabio@acme.com";

            Livro dominando = new Livro();
            dominando.Autor = fabio;
            dominando.Titulo = "Dominando o Visual Studio.Net com C#";
            dominando.ISBN = "8575021095";
            objectBag.Set(dominando);

            Livro cinquentaEOito = new Livro();
            cinquentaEOito.Autor = fabio;
            cinquentaEOito.Titulo = "58+ Soluções em .Net";
            cinquentaEOito.ISBN = "8575021583";
            objectBag.Set(cinquentaEOito);
        }

Em ordem o que fiz:
- Criei uma nova instância de Autor (o Raul)
- Criei uma nova instância de Livro (o de Análise)
- Disse para a instância de Livro que seu autor é o Raul;
- Gravei na base a instância de Livro. Como o autor ainda não estava gravado, o db4o grava-o automaticamente também.
- Repeti a operação com um novo autor e dois novos livros
Percebam que a gravação foi muito simples. Nada de xml de mapeamento, nada de atributos, nada de pilhas de camadas para fazer uma
simples gravação de dados.

Recuperando dados

O db4o tem 3 tipos de pesquisa possíveis:
* QBE (query by example): recupera dados através de um objeto modelo. É a forma mais simples, mas possui uma série de restrições

* SODA query: um estilo de query que até pouco tempo foi o mais indicado para ser usado pelo db4o. Um ponto negativo é a utilização de
strings para identificar os campos na pesquisa o que não permite a checagem de erros na compilação. O ponto positivo é a sua
velocidade de pesquisa

* Native Queries: lançadas recentemente no db4o, permitem uma pesquisa de forma nativa orientada a objetos e checagem de tipos durante a compilação.

Recuperando dados via QBE

Vamos criar uma pequena função de pesquisa de autor:

   static Autor ProcuraAutor(string nome)
   {
	Autor proto = new Autor();
	proto.Nome = nome;	
	ObjectSet result = objectBag.Get(proto);
	if (result.HasNext())
		return (Autor)result.Next();
	else
	 	throw new Exception(nome + "  nao encontrado");
   }

Em ordem, o que fiz:
- Criei um objeto Autor para servir de template (proto)
- pesquisei no container atual o objeto proto através de Get. O resultado será retornado em um ObjectSet
- Verifico se existe algum objeto no ObjectSet com a função HasNext()
- Se existir um objeto, retorno o mesmo através da função Next()
É importante salientar que o ObjectSet não contem objetos instanciados automaticamente. Eles são apenas instanciados quando forem explicitamente chamados,como no exemplo acima, com a função Next(). O db4o chama isso de Ativação de Objetos e assim reduz-se a quantidade de memória utilizada.

Recuperando dados via SODA

A pesquisa via SODA permite estabelecer critérios mais detalhados e também permite ordenar o resultado. Essa pesquisa permite acessar diretamente os nós de um graph de pesquisa:

	
static void LivrosDoAutor(Autor autor)
{
	Query query = objectBag.Query();
	query.Constrain(typeof(Livro));
	query.Descend("_autor").Constrain(autor);
	ObjectSet resultado = query.Execute();
	if (resultado.Count > 0)
	{
		while (resultado.HasNext())
		{
			Console.WriteLine(resultado.Next());
		}
	}
	else
	{
		Console.WriteLine("Não há nenhum livro do autor {0} cadastrado", autor.Nome);
	}
}

Em ordem, o que fiz:
- Criei uma nova Query
- Estabeleci como critério apenas os objetos do tipo livro;
- Dentro dos livros quero que sejam listados apenas os objetos cujo campo "_autor" seja igual ao objeto Autor passado como parâmetro. Aqui está o probleminha que citei mais acima. Se alterarmos o nome do campo na classe, temos que tomar cuidado de alterar o nome nas pesquisas também.
- Mandei rodar a query através de query.Execute()
- Mostra todos os resultados da query na tela.
Se você quiser ordernar os livros em ordem descendente, poderia acrescentar (antes da linha ObjectSet resultado = query.Execute(); ):

query.Descend("_titulo").OrderDescending();

Recuperando dados via Native Queries

Acredito que vocês acharão esse modo o melhor de todos, pois aqui você usa a própria linguagem de desenvolvimento para montar suas pesquisas!
Os exemplos utilizarão Generics, mas você também pode fazer as pesquisas no framework 1.1 (vou dar um exemplo mais abaixo)
Vamos criar uma nova versão do exemplo de pesquisa via SODA, agora usando native queries:

	
static void LivrosDoAutorNQ(Autor autor)
{
	IList livros = objectBag.Query(delegate(Livro candidato) 
	{
		bool podeAdicionar = candidato.Autor == autor;
		return podeAdicionar; 
	});
	foreach (Livro livro in livros)
	{
		Console.WriteLine(livro);
	}
}

Para fins didáticos fui um tanto quanto prolixo.
Em ordem, o que eu fiz:
- Em uma IList<Livro> (uma lista só de livros) coloquei o conteúdo da Query tipada. Cada elemento Livro da base é verificado pelo delegate. Se atender a condição, ele é adicionado à lista.
- Faço um foreach nos resultados e mostro o resultado na tela
No caso do Framework 1.1 teremos um pouco mais de trabalho pois não temos nele métodos anônimos. Teremos que criar uma classe de pesquisa herdando de com.db4o.query.Predicate.
	
public class PesquisaLivros : Predicate
{
	private Autor _autor;
	public PesquisaLivros(Autor autor)
	{
		_autor = autor;             
	}
	public Boolean Match(Livro livro)
	{
		return (livro.Autor == _autor);
	}
}

usando:
	
  static void LivrosDoAutorNQFramework11(Autor autor)
  {
	System.Collections.IList livros = objectBag.Query(new PesquisaLivros(autor));                
	foreach (Livro livro in livros)
	{
		Console.WriteLine(livro);
	}
  }

Um pouco mais trabalhoso, não? Mais um motivo para você migrar urgentemente para a versão 2.0 do .Net ;-)

Um outro exemplo: se você quiser pegar todos os objetos do tipo Livro na base:

	
  static void Catalogo()
  {
	foreach (Livro livro in objectBag.Query(typeof(Livro)))
	{
		Console.WriteLine(livro);
	}
  }

E no próximo capítulo...

Meu objetivo nessa primeira parte foi só introduzir o assunto. Creio que muitos nunca ouviram falar em db4o portanto preferi mostrar apenas as operações básicas. Nos próximos artigos mostrarei mais alguns exemplos de utilização.

Por enquanto sugiro que você puxe o instalador do db4o e faça algumas brincadeiras. O instalador disponibiliza um tutorial muito detalhado de todas as funções do db4o.

Até a próxima!

Tags:

Banco de Dados