ActiveRecord em java ! é possivel?

Ola.
Eu gostaria de saber se é possivel fazer uma implementação de activeRecorde nas classes de entidades. por exemplo:

Pessoa joana = new Pessoa("joana");
joana.save();

Eu sei que eu poderia fazer isso facilmente escrevendo uma função save na entidade como segue abaixo

public void save(){
    personRepository.save(this);
}

Nos sabemos que o codigo acima quebra as regras do Domain Driven Design, pois uma entidade nao pode persistir ela mesma, e nem qualquer coisa eu acho, mas isso não importa, eu acho muito util o codigo acima e gostaria de fazer uma implementação parecida nas minhas entidades.

Para evitar um pouco o desconforto de quebrar as regras eu pensei em fazer algo mais parecido com o Ruby, porem nao tenho muitas ideias de como abstrair isso da entidade, e tb nao sei como funciona o Ruby, eu pensei em usar um mecanismo de IoC para criar um repositorio generico como o exemplo abaixo.

public void save(){
   repository.save(this);
}

Portanto todas as entidades teriam o mesmo codigo save. Isso na verdade é uma ideia que eu tive mas não achei muito útil pois eu adiciono um impencilio que é a instancia da classe, que agora deve ser feita através de um container IoC, e pra ser sincero nem sei se da pra fazer isso que citei acima.
Alguma idéia ?

Desde ja obrigado.

com AOP da pra fazer bem parecido, mas os metodos find não vão rolar …

Dê uma olhada em:

Rolling with Spring and Hibernate, part 1: setup
Rolling with Spring and Hibernate, part 2: the model

Já adianto que não é só isso. Esta sequência de artigo tem no total 6 partes. Com certeza é o caminho das pedras para implementar o AR de uma forma nteressante!

Abraços!

Aqui vamos nós outra vez …

A) Imagine o seguinte codigo :


//1 
Pessoas p = new Pessoa("Joana");
p.save();
p.delete();
p.save();

//2

Pessoas p = new Pessoa("Joana");
p.delete();
p.save();

Por aqui se vê que implicitamente temos que ter um estado para o objeto. Mesmo não usado ActiveRecord teriamos esse problema.
E ai vêm as perguntas:
Onde/como guardar o estado ?
Eu posso dar um delete antes de um save ?
E como eu controlo a transação ?

Existem padrões para o estado (State) e transação (Unit of Work)
e o delete não é problema. Afinal só se pode apagar o que já existia antes.

B) Para resolver o problema de não ter que escrever save() em todas as entidades existe outro padrão Layer Supertype. Que básicamente significa que se crie um classe abstracta que contém todas esses métodos comuns e delege para o repositorio usando this. O Layer Supertype é o que o Rails usa para capacitar as classes com os métodos de persistência.

C) Se vc prestar atenção na JPA será que ela coabita com os mesmos problemas. o repositorio equivale ao EntityManager.
Só que JPA não usa ActiveRecord.
Desta forma todas as operações têm que passar por outra classe , que é realmente aquela onde as injeções foram feitas à priori.

Se vc quer poder usar o new nos seus objetos de dominio a coisa complica-se porque não existe um ponto de extensão (a menos que use AOP, claro) Sem usar AOP vc tem duas alternativas:a) cria os objetos usando um metodo fábrica (Pessoa p = Pessoa.pessoa(“Joana”):wink:
ou usar processamento de bytecode (que é algo abaixo de AOP).
Eu ja pensei nisto durante muito tempo e a solução mais eficiente , quanto a mim , é usar o processamento de bytecode, já que não obriga a criar metodos static nas classes de entidade.

funciona assim
Vc pode criar o objeto com new e ele será um objeto comum.
Como ele foi criado com new e não veio do repositorio, ele tem o estado “novo” por padrão. Só que não ha nenhuma variável a dizer isso, é uma convenção.
Ai vc faz os get/set que tem a fazer e usa os metodos que tiver que usar , etc… no fim, vc simplesmente faz Repository.save§;
Repare que se estiver num Unit Of Work esse save só será feito no fim , quando houver um commit e não será feito por si e sim pelo objeto controlador. Então quando eu digo “vc faz” eu quero dizer “alguem faz”, o programador, ou a infraestrutura.
Ao fazer o save, dentro do repositorio o objeto Pessoa é processado via manipulação de bytecode e ele vira um outro objeto , digamos, PessoaProxy. A sacada é que PessoaProxy é construida dinamicamente como uma subclasse de pessoa. Isso permite que a partir dai não mais estejamos preocupados com a classe Pessoa em si, já que PessoaProxy tem a mesma interface. Além disso PessoaProxy tem o estado de edição como uma variável. Para melhorar , podemos ainda fazer PessoaProxy implementar outras interfaces genericas que serão as que o DAO vê e manipula (sei lá, por exemplo , Persistable).
Vc pode ainda usar a leitura de anotações em Pessoa para criar PessoaProxy, por exemplo, saber quais campos são a chave.
Este processo é muito poderoso. E algo assim que a JPA usa.

Quando vc der um find no repository, ele vai retornar instancia de PessoaProxy , que são Pessoa tb. Então o codigo a seguir funciona

Pessoa p = repository.findByKey(12);

Vc pode fazer instanceof , que o resultado vai ser true. p, é uma Pessoa, embora a classe seja realmente PessoaProxy que herda de Pessoa dinamicamente.
Agora, vc tem um vantagem extra. Vc pode interceptar todos os set que forem dados em p e saber se o estado de persistencia mudou. Esse estado será usado quando um outro save§ for dado.
Moral da historia, Repositorio trabalha com proxies e as classes de entidade não tem que fazer nada especial (além de algumas anotações tlv)

Agora, como eu não procuro usar o ActiveRecord eu tb não sei como usar este esquema com o ActiveRecord. Eu até já tentei usar o ActiveRecord mas - como diz o Fowler - ele realmente só funciona para coisas muito simples (por isso funciona no Rails). Ele não funciona num nivel enterprise como o JPA ou o hibernate

Espero que isto te ajude…

SIm, é possível e não há qualquer problema em termos de implementação caso você decida que se aplica ao seu caso.

Como Java é estaticamente tipada você vai rpecisar fazer com que a classe tenha este método, seja via herança de uma classe abstrata ou implementação de uma interface.

Quanto à implementação dos métodos do AR em si você pode utilizar JDBC diretamente, o que provavelmente vai exigir que cada classe tenha sua implementação de persistência, ou utilizando um framework como Hibernate -que é agnóstico quanto ao uso de mapeadores como DAOs ou Active Records-, que abstrai completamente esse passo e deixa que a classe-mãe abstrata implemente o método. Lembre-se que assim como DAOs ARs não devem gerenciar transações,a penas fazer parte (ou não, se for o caso) delas. Comot ransação não é um conceito de domínio ou da Camada de Negócios (pelo menos não transação de SGBD, transação nesse contexto pode significar outra coisa) lembre-se que você deve geralmente recorrer a meios como AOP para trabalhar com esse aspecto do sistema.

Mas já que você está nas alternativas de persistência, que tal deixar updates a cargo de um container como Hibernate e seus objetos gerenciados?