sexta-feira, 20 de março de 2009

Tagged under: , , ,

Persistência orientada a objetos com db4o

O db4o é um framework de persistência que armazena os objetos exatamente como eles são representados em bytecode na plataforma Java ou em CIL na plataforma .NET. O objetivo desse framework é tornar a vida do desenvolvedor mais fácil ao dar-lhe uma opção de usar uma persistência mais próxima do seu ambiente puramente orientado a objetos.

O db4o possui a mesma confiabilidade encontrada em bancos de dados relacionais por suportar transações ACID. Se uma falha ocorre, os objetos são restaurados para o primeiro estado consistente antes de a transação iniciar. O controle de concorrência e lock é também semelhante ao dos bancos de dados relacionais.

O engine do db4o é simplesmente um JAR (db4o-x.x-javaX.jar) ou uma DLL (Db4oFactory.Db4o.dll), dependendo do ambiente escolhido, e é somente deles que precisamos para utilizar o framework. Como estamos num grupo de usuários que lida com a plataforma Java, você pode obter a última versão para a plataforma Java em http://www.db4o.com/DownloadNow.aspx. A versão que usaremos aqui nesse post é a 7.4.

Ao descompactar o arquivo baixado do site, copie o arquivo db4o-7.4-java5.jar (versão para Java 5 ou superior, que suporta Generics, AutoBoxing e as novidades adicionadas recentemente à linguagem) para uma pasta isolada. Crie uma entrada na sua IDE preferida para esse arquivo. Por exemplo, eu usarei o Netbeans nesse post e isso seria equivalente a criar uma biblioteca na IDE e adicionar esse arquivo JAR a ela.

Crie um projeto na sua IDE e adicione a entrada para o arquivo JAR ao projeto recém-criado. Como eu estou usando o Netbeans, criei um projeto Java Class Library chamado JavaBahia4O e adicionei a biblioteca db4o ao projeto.

Para entendermos como funciona esse framework, vamos utilizar o mesmo contexto de domínio modelado para o tutorial "Desenvolvendo uma aplicação web de forma fácil e prática".


Relembrando, pelo diagrama de classes acima, todo objeto da classe Contrato possui um Usuário cadastrante e uma Empresa contratada. A partir da relação entre um Contrato e uma Empresa, nasce a classe Contratada, que possui informações bancárias para pagamento daquele contrato; outro contrato com a mesma empresa poderá ser pago em uma conta bancária diferente.

Vamos criar as classes Usuario, Empresa, Contrato e Contratada e colocá-las no pacote javabahia.entidade.


Usuario.java:


package javabahia.entidade;

/**
* Classe da entidade Usuário.
* @author Alexandre
*/

public class Usuario {

private String login;
private String senha;
private String nome;
private String email;

public Usuario(String login, String senha, String nome, String email) {
this.login = login;
this.senha = senha;
this.nome = nome;
this.email = email;
}

// get's e set's omitidos
}


Empresa.java:


package javabahia.entidade;

/**
* Classe da entidade Empresa.
* @author Alexandre
*/

public class Empresa {

private String cnpj;
private String nome;
private String email;

public Empresa(String cnpj, String nome, String email) {
this.cnpj = cnpj;
this.nome = nome;
this.email = email;
}

// get's e set's omitidos
}


Contrato.java:


package javabahia.entidade;

import java.util.Date;

/**
* Classe da entidade Contrato.
* @author Alexandre
*/

public class Contrato {

private Contratada contratada;
private Integer numero;
private String objeto;
private Date dataAssinatura;
private Integer prazoVigencia;
private Usuario cadastrante;

public Contrato(Integer numero, String objeto, Date dataAssinatura, Integer prazoVigencia, Usuario cadastrante) {
this.numero = numero;
this.objeto = objeto;
this.dataAssinatura = dataAssinatura;
this.prazoVigencia = prazoVigencia;
this.cadastrante = cadastrante;
}

// get's e set's omitidos
}


Contratada.java:


package javabahia.entidade;

/**
* Entidade da classe Contratada.
* @author Alexandre
*/

public class Contratada {

private Contrato contrato;
private Empresa empresa;
private Integer codigoBanco;
private String agenciaBanco;
private String contaBanco;

public Contratada(Contrato contrato, Empresa empresa, Integer codigoBanco, String agenciaBanco, String contaBanco) {
this.contrato = contrato;
this.empresa = empresa;
this.codigoBanco = codigoBanco;
this.agenciaBanco = agenciaBanco;
this.contaBanco = contaBanco;
}

// get's e set's omitidos
}


Note que as classes das entidades não contém qualquer código intrusivo do framework.

Para acessar o arquivo do banco de dados db4o, invocamos o método Db4o.openFile() passando como parâmetro o caminho para o arquivo. Isso retornará uma instância de ObjectContainer, que representa o seu banco de dados db4o e será sua principal interface de acesso a ele. Se o arquivo não existir, ele será criado automaticamente. O método close() fecha o arquivo e libera qualquer recurso preso a ele.

Para armazenar um objeto no banco de dados, invocaremos o método store() da instância de ObjectContainer, passando como parâmetro o objeto a ser persistido. Simples assim!

Para visualizar o que foi dito, crie uma classe chamada Passo1.java com o seguinte código:


package javabahia.app;

import com.db4o.Db4o;
import com.db4o.ObjectContainer;
import java.util.Date;
import javabahia.entidade.Contratada;
import javabahia.entidade.Contrato;
import javabahia.entidade.Empresa;
import javabahia.entidade.Usuario;

/**
* Primeiro passo de execução do teste com o db4o.
* @author Alexandre
*/

public class Passo1 {

public static void main(String[] args) {
// abrindo o arquivo db4o
ObjectContainer db = Db4o.openFile("javabahia.dbo");

try {
// criando um usuário
Usuario usu = new Usuario("teste", "1234", "Usuário de Teste", "teste@teste.br");
// armazenando o usuário acima
db.store(usu);

// criando uma empresa
Empresa emp = new Empresa("11000111000100", "ACME Ltda", "acme@acme.com");
// armazenando a empresa acima
db.store(emp);

// criando o contrato
Contrato ctr = new Contrato(1000, "Compra de roupas", new Date(), 30, usu);
// criando a relação com a empresa contratada
Contratada ctda = new Contratada(ctr, emp, 123, "1234-5", "12345-X");
ctr.setContratada(ctda);

// armazenando o contrato e a sua contratada respectivamente
db.store(ctr);

} finally {
// fechando o arquivo db4o
db.close();
}
}
}


No código acima, armazenamos instâncias de Usuario e Empresa isoladamente. Em seguida armazenamos instâncias de Contrato e Contratada com apenas uma chamada ao método store() passando apenas o contrato. O db4o possui capacidade de entender quais objetos associados ao objeto que se pede para persistir já existem no banco de dados ou são novos. Assim, nesse exemplo, o db4o, ao persistir o contrato, persistiria também a contratada, pegando apenas a referência ao usuário e empresa já cadastrados.

Por hoje, vou ficando por aqui. No próximo post eu mostrarei como recuperar os objetos, atualizá-los e excluí-los. Ate lá!

9 comentários:

Anônimo disse...

Olá.

Não vi em nenhum lugar informando quais os id's de cada objeto, mapeamento de chaves, ou algo que identifique um objeto no banco.

Lendo essa citação:

"O db4o possui capacidade de entender quais objetos associados ao objeto que se pede para persistir já existem no banco de dados ou são novos"

Eu me pergunto: como?

Alexandre M. Lima disse...

Reinaldo,

Existe uma confusão normal quanto aos paradigmas.

Na lógica relacional, precisamos de um atributo que sirva como chave para conseguirmos relacionar uma tupla com outra.

Na lógica orientada a objetos, o objeto é identificado unicamente por um id relacionado com o endereço de memória do computador no qual ele reside (conceito chamado de persistência transparente).

Assim, quando persistimos os objetos usando o db4o, ele armazena fisicamente no disco rígido o estado do objeto (conceito da orientação a objetos) junto com seu id (endereço de memória virtual atribuído pelo db4o). Com isso, o db4o consegue saber quais objetos já foram persistidos ou não.

Comparativamente com a lógica relacional, onde temos mapeamento de chaves para identificar as relações entre as tuplas, na lógica orientada a objetos temos a associação entre objetos através de id's que o desenvolvedor não precisa conhecer; basta conhecer apenas qual é a instância do objeto.

Espero ter sido esclarecedor. Mais informações podem ser obtidas em http://pt.wikipedia.org/wiki/Banco_de_dados_orientado_a_objetos e http://pt.wikipedia.org/wiki/Orienta%C3%A7%C3%A3o_a_objetos .

No post de amanhã eu mostrarei como recuperar objetos baseado em seu estado.

Julio disse...

Alexandre, boa noite, seguinte sou iniciante em java, tenho algumas experiencias, e estou começando a utilizar o DB4o, e um dos problemas que estou tendo é em relação ao relacionamento de dois objetos, o que acontece é o seguinte, projeto meu:
tenho uma classe Carro(int codigo, String marca, String modelo), classe criada certinha.
E tenho uma outra classe Venda(int codigo, Float valor, Carro car).

Bom o problema q estou tendo é o seguinte, eu armazeno no banco o OBJETO CARRO, até ai sem problemas.
Quando vou armazenar o objeto venda, eu passo o objeto VENDA que já tem no seu método construtor o objeto carro. E quando armazeno este objeto ele duplica o objeto CARRO. Tentei seguir teu exemplo mas não obtive sucesso. Por favor me de um help. Obrigado
armazeno no BD o

Alexandre M. Lima disse...

Oi Julio,

O seu problema é o mais comum a quem inicia no uso do DB4O.

Toda vez que você faz um new Carro(), você está criando outro objeto carro e por isso o DB4O duplica-o quando armazena a Venda.

O que você tem que fazer é sempre que for ligar um objeto já existente no banco a um outro qualquer, obtê-lo primeiramente para depois fazer a ligação.

Ou seja, você primeiro tem que recuperar do banco o objeto Carro que você quer, jogá-lo no construtor da Venda e depois armazenar o objeto Venda.

Jean Bruno Souto Villete disse...

Ola Alexandre M. Lima.
A nivel de informacao, sou academico de Ciencias da Computacao, e Sr, gostaria de um auxilio seu.
Estou sendo encorajado por um professor a estudar e utilizar o db4o em minhas aplicacoes Java. Tenho estudado sobre o db4o com informacoes do proprio site que suporta o projeto "www.db4o.com"(Tutorials and How To).
O meu problema eh quanto a aplicacoes web, no qual, senao me engano, eh o que voce eh Doctor.
Bem, todo material de apoio que eu tenho encontrado com exemplos, eu devo executar como aplicoes Desktop, pois eu informo onde eh que o db4o ira salvar o arquivo que eh o banco de dados Db4o.openfile("mydb.yap") by example.
Mas e quanto aplicacoes na web.
Tenho que ter servidores especificos?
Se sim, voce pode me indicar servidores que suportam databases OODBMS?
Atualmente tenho trabalhado com servidores que servem dados com o SGDB MySQL.
Alexandre, por favor, me ajude.
Se possivel, me adicione no msn "brunopib@hotmail.com" ou google talk "brunopib@gmail.com".
Um grande abraco.

Unknown disse...

Gente dando continuidade no Forum, meu problema é o seguinte.

Tenho uma classe chamada de Jogador.

Tenho uma classe Equipe que possui uma lista de Jogadores.

No caso recupero muitos jogadores do banco com suas instancias e quando adiciono a lista de jogadores da equipe, ele também duplica o jogador.

ajudemmmmm

vinicios disse...

Alguém sabe me dizer como posso visualizar os dados da minha base?

Estou utilizando no desenvolvimento Android, então não sei muito bem como faço pra visualizar de forma gráfica os dados que estão salvos na minha base, queria algo tipo um SGBD. Alguém sabe me dizer se existe e como que funciona?

Bruno Paulino disse...

Vinicius você pode usar o OME, um plugin que vem junto com o DB4O para eclipse onde você poderá abrir o arquivo .db4o e visualizar todos os objetos.

Unknown disse...

olá, como o exemplo que vc está fazendo poderia ser feito com cada operação em uma activity diferente? por exemplo, ter uma activity que insere e outra que mostra os dados no bd. Tentei mas não consegui, deu erro no contexto quando coloquei a segunda activity. (com tudo na mesma estava funcionando)