db4o - Um banco de dados orientado a objetos


Nessa altura todos concordam que a utilização de linguagens orientadas a objetos para acessar banco de dados relacionais traz consigo o problema da incompatibilidade sobre a qual muito se tem escrito e debatido; muitas ferramentas também foram desenvolvidas para tentar contornar o problema sem obter uma solução totalmente satisfatória. No entanto, existe uma forma de resolver o problema de uma maneira bem mais simples do que usar frameworks de mapeamento objeto relacional.

Como ??

Usar um banco de dados orientado a objetos.

Qual ?

Que tal o db4o da DB4Objects. (Afinal banco de dados se combate com banco de dados...)

O db4o é um banco de dados orientado a objetos que trata objetos nativamente como base de dados; nele não existem tabelas, linhas ou colunas , só objetos, onde uma entidade ou tabela pode ser entendido como sendo o escopo do objeto ou seja a sua classe, e, as linhas e colunas são os próprios objetos.

O db4o também não usa a linguagem SQL mas utiliza uma outra tecnologia chamada de Native Query´s para tratar objetos. Os objetos são persistidos em um arquivo com extensão .yap que identifica um arquivo db4o. A conexão é feita com a abertura deste arquivo. Com o db4o você pode criar aplicações cliente-servidor, desktop e também aplicações embarcadas.

"O db4o é um banco de objetos de código aberto que oferece vantagens exclusivas. Objetos são armazenados nativamente, eliminando a complexidade extra e a perda de performance com a conversão para outros formatos como SQL. O suporte nativo às funcionalidades de base dados orientada a objetos, como as aclamadas Queries Nativas e replicação orientada a objetos, aumenta a performance e os ganhos de desempenho de linguagens orientadas a objeto como o Java e as linguagens da plataforma .NET. "

Meu primeiro contato com o db4o foi em 2005 durante o minha pós-graduação. Na época só existia a versão para Java do db4o e meu contato foi rápido e a nível acadêmico.

Atualmente temos também a versão para a plataforma .NET (linguagem C#) que possui praticamente as mesmas propriedades e características da versão para Java.

Para a plataforma .NET temos disponível a versão do db4o, que você pode baixar neste link: http://www.db4o.com/DownloadNow.aspx
(observe que você tem as versões para a plataforma .NET 2.0 e 3.5)

Você também pode baixar a partir do link: http://www.db4o.com/community/

Você tem que fazer o registro para efetuar o download mas não há custo algum envolvido nesse processo. (A versão atual é a 7.4)

Obs: A licença do db4o é Open-source dual como o MySql. Se você está desenvolvendo para uso dentro de sua empresa ou uso pessoal ele é gratuito para você, MAS se você precisar distribuir sua aplicação, a situação muda e existe um custo.(pequeno se comparado com os benefícios proporcionados.)

Após efetuar o download você deverá obter um arquivo com a extensão .msi, se escolher baixar a versão para a versão 3.5 da plataforma .NET o arquivo será: db4o-7.4-net35.msi. Executando este pacote teremos a tela inicial da instaação conforme a figura abaixo:

A instalação é simples e rápida, basta clicar em Next> e mais duas ou três janelas e o db4o estará instalado na sua máquina local no caminho: :\Arquivos de programas\Db4objects\ (dependendo da sua versão do Windows o caminho pode ser diferente.)

Abaixo temos a exibição dos arquivos para a plataforma .NET versão 3.5.

Conhecer a localização da instalação é importante pois para poder usar o db4o teremos que referenciar estas DLLs.

Observe na estrutura de diretórios do db4o que temos um pasta doc onde temos a documentação da API, referências e um tutorial do Framework.

Para você utilizar o db4o em seus projetos basta você referenciar a dll Db4objects.Db40.dll (a mais usada) e usar o namespace using com.db4o.(lembre-se que estamos usando C#)

Observe que a 'instalação' na verdade foi uma descompactação dos arquivos não havendo portanto nenhum registro no GAC ou no registro do Windows. Isso facilita a distribuição dos nossos projetos com o db4o.

Um recurso muito interessante para quem precisa manter o banco de dados off-line durante um período de tempo e on-line em determinados períodos é o mecanismo de replicação do db4o.

Bem, aposto que você deve estar ansioso para ver o db4o em ação, então mãos a obra...

Usando o db4o com SharpDevelop

Após você ter seguido os passos para o download e instalação do db4o podemos usar o Visual Studio 2008 ou Visual C# 2008 Express Edition ou o SharpDevelop para criarmos projetos e aplicações que usam o db4o como banco de dados. (O mesmo projeto criado no SharpDevelop pode ser aberto sem problemas no Visual C# 2008 Express Edition.)

Você identifica um banco de dados padrão db4o com a extensão .yap e para começar vamos realizar algumas tarefas básicas como  criar um banco de dados db4o e efetuar algumas consultas.

Neste primeiro contato eu vou usar o SharpDevelop versão 3.0 como ferramenta para criar o projeto e acessar o db4o.(Você precisa ter instalada o .NET Framework 3.5 service pack 1. Veja a lista de novas funcionalidades e correções de bugs em : http://support.microsoft.com/kb/951847/en-us)

Nota: Faça o download do Sharpdevelop neste link: http://www.icsharpcode.net/OpenSource/SD/Download/#SharpDevelop30

Para saber mais sobre o SharpDevelop veja os artigos: VB.NET - Usando o SharpDevelop e Usando SharpDevelop - C#

Abra o SharpDevelop 3.0 e crie uma nova solução do tipo Windows Forms com o nome db4oNet;

A seguir precisamos referenciar as dlls do db4o no nosso projeto. Para isso clique com o botão direito do mouse sobre o nome do projeto (db4oNet) e selecione Adicionar Referência;

Selecione a aba Navegador de Assembly .NET e clique no botão Navegar;

Procure pela pasta bin no caminho onde você descompactou os arquivos do db4o, e selecione a dll Db4objects.Db4o.dll e clique em Abrir;

A seguir, na janela Adicionar Referência, clique no botão OK para incluir a referência no projeto:

Neste momento você já pode usar o namespace using Db4objects.Db4o; no projeto para ter acesso a maioria das funcionalidades usadas pelo db4o. Dentre elas podemos destacar:

É bom você saber que o db4o suporta 3 tipos de conexões:

Neste primeiro exemplo vamos usar a conexão direta para fazer a conexão com o db4o usando um banco de dados local. A sintaxe usada neste caso é muito simples e tem a seguinte estrutura:

IObjectContainer db = Db4oFactory.OpenFile("C:/dados/macoratti.yap");
try
{
 
    //efetuar operações com o db4o
}
finally
{
       db.Close();
}

A interface IObjectContainter irá abrir o arquivo macoratti.yap na pasta c:\dados efetuando a conexão com o arquivo. Se o arquivo não existir ele será criado.

- A interface IObjectContainter é o coração do db4o pois pode ser considerada o seu banco de dados db40 ou uma conexão com o um servidor db4o. Cada IObjectContainer possui uma transação e todo o trabalho é transacional.
- Quando você abre uma IObjectContainter você esta em uma transação, quando você realiza um Commit() ou RollBack(), a próxima transação e iniciada imediatamente.
- Cada IObjectContainter mantém sua própria referência para objetos instanciados e armazenados e gerencia a identidade dos objetos e esta apto para obter um alto nível de desempenho.
- A IObjectContainter foi planejada para ficar aberta pelo tempo que você estiver trabalhando com ela; quando você fecha uma IObjectContainter , todas as referências de banco de dados para os objetos na RAM serão descartadas.

Você pode usar o intellisense do SharpDevelop para obter informações sobre os métodos do db4o;


Observe que a instância db criada pelo IObjectContainter oferece os seguintes métodos:

Como vamos trabalhar com nossos dados ?

Aqui começa a grande diferença entre o db4o e os banco de dados relacionais. Lembre-se que estamos usando um banco de dados orientado a objetos onde uma entidade ou tabela pode ser entendidos como sendo o escopo do objeto ou seja a sua classe e as linhas e colunas são os próprios objetos.

Vamos então criar duas classes bem simples para usar como nosso banco de dados. Clique com o botão direito do mouse sobre o projeto e selecine Adicionar -> Novo Item;

A seguir selecione o modelo Classe e informe o nome Aluno.cs;

A nossa classe Aluno terá a seguinte estrutura:

using System;

namespace db4oNet
{
	/// <summary>
	/// Description of Aluno.
	/// </summary>
	public class Aluno
	{
		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 _nome + " (" + _email + ")";
		}
	}
}
A classe Aluno possui duas propriedades Nome e Email e
representam o nome e o email de um Aluno.

 

A seguir vamos criar outra classe também muito simples chamada Curso.cs com a seguinte estrutura:

using System;
namespace db4oNet
{
	/// <summary>
	/// Description of Curso
	/// </summary>
	public class Curso
	{
		private Aluno _aluno;
		private string _disciplina;
		private string _codigo;

		public Aluno Aluno
		{
			get{return _aluno;}
			set{_aluno=value;}
		}

		public string disciplina
		{
			get{return _nomeCurso;}
			set{_nomeCurso=value;}
		}

		public string Codigo
		{
			get{return _codigo;}
			set{_codigo=value;}
		}

		public override string ToString()
		{
		       return _aluno.Nome + _ 
                       " (" + _disciplina + ")" + " (" + _codigo + ")";
		}
	}
}
A classe Curso possui as propriedades Disciplina e
Codigo
e também o objeto Aluno para referenciar
o curso e a disciplina que o aluno possui.

Observe que o membro _aluno é do tipo Aluno
e que o o nome do aluno é obtido através da
propriedade Nome da classe Aluno.(_aluno.Nome)

Agora vamos criar a nossa massa de dados definindo um método estático chamado gerarDados() que irá receber uma instância da interface IObjectContainer.

Para isso eu vou incluir um componente MenuStript no formulário e definir o menu Gerar Dados contendo a opção Gerar Massa de dados db40 conforme o leiaute abaixo:

No evento Click do menu vemos abaixo o código criando a instância de IObjectContainer e em seguida chamando o método gerarDados();

         void GerarMassaDeDadosDb4oToolStripMenuItemClick(object sender, EventArgs e)
          {
            IObjectContainer db = Db4oFactory.OpenFile("C:/dados/macoratti.yap");			
            gerarDados(db);
          }

O código do método estático gerarDados() é dado e explicado a seguir:

	static void gerarDados(IObjectContainer db)
	{
	   Aluno aluno1 = new Aluno();
               aluno1.Nome = "José Carlos Macoratti";
               aluno1.Email = "macoratti@yahoo.com";

               Curso matematica = new Curso();
               matematica.Aluno = aluno1;
               matematica.Codigo = "MAT1209";
               matematica.Disciplina = "Cálculo I";
               db.Store(matematica);

               Aluno aluno2 = new Aluno();
               aluno2.Nome = "Jessica Lima Bueno";
               aluno2.Email = "jessica@bol.com.br";

               Curso moda = new Curso();
               moda.Aluno = aluno2;
               moda.Codigo = "MOD1209";
               moda.Disciplina = "História da moda";
               db.Store(moda);
	}
Vamos entender o que foi feito:

O objetivo deste código é gerar dados para o nosso banco de dados OO;
Por este motivo estou usando as classes Aluno e Curso para criar instâncias
de objetos que representam os nossos dados.

Primeiro criei uma instância da classe Aluno - aluno1 e atribui o nome e o email;

Em seguida cria uma instância da classe Curso e atribui os valores para
o Código a disciplina e especifiquei que o aluno do curso é o aluno - aluno1;

Para persistir os objetos eu estou usando o método Store da instância db.
Com isso teremos os objetos persistidos em nosso banco de dados OO.

Observe que não foi usada nenhuma instrução SQL , nenhum mapeamento,
nenhuma referência a outra camada para efetuar a persistência dos objetos.

Dessa forma acabamos de gerar nosso primeiro banco de dados orientado a
objetos contendo os objetos que acabamos de persistir.

Como o arquivo macoratti.yap não existia ele foi criado na pasta c:\dados.

Para provar que realmente temos os objetos persisitidos vamos usar o recurso do db4o para recuperar os objetos. Podemos usar 4 tipos de pesquisa possíveis :

- QBE (query by example): É a forma de pequisa mais simples que recupera dados através de um objeto modelo;

- SODA query: Permite realizar uma pesquisa de forma bem rápida.O inconveniente é a utilização de strings para identificar os campos na pesquisa o que não permite a verificação de erros na compilação;

- Native Queries: Efetua uma pesquisa de forma nativa orientada a objetos e a verificação de tipos durante a compilação;

- LINQ to db4o: Usa os recursos da LINQ para efetuar consultas em objetos no db4o;

Vamos então incluir no formulário padrçao MainForm os seguintes componentes:

E dispor os controles conforme o leiaute abaixo:

 

Iremos incluir no evento Click de cada botão de comando o código pertinente ao método e a ação desejada.

1-) No nosso exemplo vamos criar uma rotina para recuperar as informações de um aluno representado pela classe Aluno. Vou começar usando o método de pequisa QueryByExamle - QBE.

No evento Click do botão de comando btnQBE inclua o seguinte código:

	void BtnRecuperaObjetosClick(object sender, EventArgs e)
	{
	         IObjectContainer db = Db4oFactory.OpenFile("C:/dados/macoratti.yap");			
	         string nome = txtNome.Text;
	         Aluno aluno = new Aluno();
                         aluno.Nome = nome;
                
                try
                {
	          IObjectSet resultado = db.QueryByExample(aluno);
		  lstbAlunos.Items.Add(resultado.Count);
				 	
		  foreach (object item in resultado)
		  {
		       lstbAlunos.Items.Add(item);
		   }
                }
                catch (Exception ex)
                {
             	  MessageBox.Show("Erro : " + ex.Message.ToString());
                 }
                 finally
                {
             	db.Close();
                }
            }

Executando o projeto e procurando o aluno por nome José Carlos Macoratti iremos obter:

2-) Vamos agora usar o método SODA para recuperar e exibir os cursos de um aluno; com este método podemos obter um detalhamento melhor do critério e efetuar a ordenação do resultado. Este método usa a interface IQuery e para isso precisamos importar o namespace: using Db4objects.Db4o.Query no nosso projeto.

O código do evento Click do botão correspondente é o seguinte:

       void BtnSodaClick(object sender, EventArgs e)
       {
          if (txtNome.Text!="")
          {
  	Aluno aluno = new Aluno();
                aluno.Nome = txtNome.Text;
 	cursosAlunoSODA(aluno);
          }
      }

O código apenas chama a rotina cursosAlunoSODA(aluno) passando o objetoAluno com o nome a ser consultado:

A rotina cursosAlunoSODA(aluno) recebe um objeto aluno e tem o seguinte código:

	 void cursosAlunoSODA(Aluno aluno)
	 {
	             IObjectContainer db = Db4oFactory.OpenFile("C:/dados/macoratti.yap");			
			
	            try{
		    IQuery consulta = db.Query();
	                    consulta.Constrain(typeof(Curso));
	                    consulta.Descend("_aluno").Constrain(aluno);
	            
	                    IObjectSet resultado = consulta.Execute();
	            
	               if (resultado.Count > 0)
	               {
	            	   lstbAlunos.Items.Add("Cursos do Aluno : " + aluno.Nome);
	                  while (resultado.HasNext())
	                  {
	                     lstbAlunos.Items.Add(resultado.Next());
	                  }
	            }
	            else
	            {
	                MessageBox.Show("Não há nenhum livro do autor {0} cadastrado" + aluno.Nome);
	            }
	         }
                         catch (Exception ex)
                         {
                        	MessageBox.Show("Erro : " + ex.Message.ToString());
                         }
                         finally
                         {
                       	db.Close();
                         }
	 }

O resultado pode ser visto na figura a seguir:

3-) Efetuando a mesma operação com o método Native Queries usamos a própria linguagem para montar as consultas.

O código do evento Click do botão correspondente é dado a seguir:

	void BtnNativeQueriesClick(object sender, EventArgs e)
	{
	    if (txtNome.Text !="" )
	    {
	      Aluno aluno = new Aluno();
	      aluno.Nome = txtNome.Text;
	      cursosAlunoNativeQueries(aluno);
	    }
	}

O código da rotina   cursosAlunoNativeQueries(aluno); que recebe um objeto aluno como parâmetro é o seguinte:

	void cursosAlunoNativeQueries(Aluno aluno)
	{
                      IObjectContainer db = Db4oFactory.OpenFile("C:/dados/macoratti.yap");			
	      try
	       {
	         IList<Curso> cursos = db.Query(delegate(Curso c) 
	         {
	                    bool inclui = c.Aluno.Nome == aluno.Nome;
	                    return inclui; 
	         });
	         foreach (Curso curso in cursos)
	         {
	                 lstbAlunos.Items.Add(curso);
	         }
	      }
                      catch (Exception ex)
                      {
                        	MessageBox.Show("Erro : " + ex.Message.ToString());
                      }
                     finally
                     {
                       	db.Close();
                     }
              }

Executando o projeto obtemos:

4-) Vamos agora usar os recursos do LINQ no sabor LINQ to db4o para efetuar consultas e recuperar informações dos objetos persistidos. Para poder fazer isso teremos que adicionar uma nova referência ao nosso projeto então clique com o botão direito do mouse sobre o nome do projeto (db4oNet) e selecione Adicionar Referência;

Selecione a aba Navegador de Assembly .NET e clique no botão Navegar;

Procure pela pasta bin no caminho onde você descompactou os arquivos do db4o, e selecione a dll Db4objects.Db4o.Linq.dll e clique em Abrir;

A seguir clique no botão OK para confirmar a inclusão da referência ao projeto. Agora basta importar o namespace Db4objects.Db4o.Linq no nosso projeto e pronto, já podemos usar os recursos da LINQ to db4o. Até o momento temos os seguintes namespaces usados no projeto:

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using Db4objects.Db4o;
using Db4objects.Db4o.Query;
using Db4objects.Db4o.Linq;

Basta incluir o código abaixo no evento Click do botão pertinente para listar os cursos para o aluno indicado:

	void BtnLINQClick(object sender, EventArgs e)
	{
	   IObjectContainer db = Db4oFactory.OpenFile("C:/dados/macoratti.yap");			
	   try
	  {
		var resultado = from Curso c in db 
		                        orderby c.Disciplina
			        where c.Aluno.Nome == txtNome.Text
			        select c;
		foreach (var item in resultado)
		{
		   lstbAlunos.Items.Add(item);
		}
	   }
                  catch (Exception ex)
                  {
             	    MessageBox.Show("Erro : " + ex.Message.ToString());
                  }
                 finally
                 {
             	  db.Close();
                  }
	}

Executando o código para esta opção com LINQ temos:

5-) Para as informações dos objetos Aluno iremos usar o o código abaixo no evento Click do botão - Listar Alunos;

               void BtnAlunosClick(object sender, EventArgs e)
               {
                IObjectContainer db = Db4oFactory.OpenFile("C:/dados/macoratti.yap");			
               foreach (Aluno aluno in db.QueryByExample(typeof(Aluno)))
               {
                    lstbAlunos.Items.Add(aluno.Nome);
               }
               db.Close();
              }

O resultado é visto a seguir:

6-) Para listar as informações de código e da disciplina dos objetos Cursos usamos o código abaixo no evento Click do botão - Listar Cursos;

	void BtnCursosClick(object sender, EventArgs e)
	{
	 IObjectContainer db = Db4oFactory.OpenFile("C:/dados/macoratti.yap");			
                  foreach (Curso curso in db.QueryByExample(typeof(Curso)))
                  {
                       lstbAlunos.Items.Add(curso.Codigo + " - " + curso.Disciplina);
                  }
                 db.Close();
                }		

Abaixo temos o resultado exibindo as disciplinas para o aluno:

Neste primeiro contato vimos como foi fácil e rápido instalar e usar o db40 em um projeto usando o SharpDevelop criar um banco de dados como objetos e recuperar informações sobre estes objetos. Tudo isso sem usar SQL nem frameworks de mapeamentos e persistência nem ter que efetuar configurações usando componentes de terceiros.

Meu objetivo foi apenas apresentar o db4o e mostrar como podemos efetuar algumas operações com o mesmo.(por isso não me esmerei em criar um código mas refinado como vocês podem notar.)

Em um próximo artigo pretendo abordar como realizar as operações CRUD neste banco de dados orientado a objetos.

Pegue o projeto completo aqui: db4oNet.zip

Eu sei é apenas db4o, mas eu gosto...

Referências:


José Carlos Macoratti