Persistir atributos privados

51 respostas
J

Gostaria de uma ideia de como persistir atributos privados de uma classe sem expo-los para o ambiente externo.
ex:

class Funcionario
{
   private int id;
   public int getId() {}
   
}

Não tem setId, pois esse campo é atribuido internamente.
O ambiente externo não pode atribuir valor para ele, somente consultar.
Como eu faria para recuperar ou persistir esse campo em um DAO e no Hibernate ?

51 Respostas

_fs

Hibernate trabalha sem getters e setters:
http://wrschneider.blogspot.com/2005/01/avoiding-anemic-domain-models-with.html
Legal né :smiley:

Mesmo que não use Hibernate, pode fazer alguma coisa com reflection. E não é a mesma coisa que usar setters. Persistência é um problema que precisa ser resolvido, mas não é por causa dele que você precisa ferrar o resto hehe :smiley:

J

Mas nestes casos se a segurança do JVM não permitir que os atributos sejam acessados ?
Tem alguma coisa nesse sentindo Security…Alguma coisa que eu não lembro.

_fs

Tem sim!

Se por algum motivo o ambiente de produção tem essa restrição, você pode usar um desses:
http://jakarta.apache.org/bcel/index.html
http://cglib.sourceforge.net

Ou implementar você mesmo algo semelhante, o que não é tão dificil assim.

Aliás, esqueça todo o resto e use Hibernate hehe

“Ah, é mais fácil ter getters e setters”. Você que sabe, oras :smiley:

mister_m

Se você criar um private setter, o Hibernate consegue persistir sem problemas.

J

Mas em uma DAO ?
se eu extendesse o objeto a partir de uma classe com métodos para trabalhar a persistencia
ex:

class Funcionario extends Persistente
{
   private int id;

  protected void persistir(Dados dados)
  {
     dados.setInt("ID",geraId());
  }


  protected void recuperar(Dados dados)
  {
     id = dados.getInt("ID");
  }

}

Isso fica legal ?

_fs

Isso tem nome já :smiley:
http://www.martinfowler.com/eaaCatalog/activeRecord.html

Novamente, não sou nenhum expert em patterns, mas ActiveRecord + IoC cheira bem para mim.

louds

Voce pode usar métodos e atributos package protected e colocar classes auxiliares de persistencia no mesmo pacote dos teus objetos de domínio.

J

Essa ideia é muito boa.
Fica a mais elegante e menos gambiarrenta.

Mas eu poderia ter vários DAOs para diferentes tipos de dados.
Como eu lidaria com isso ?
Colocaria todos os DAOs dentro do mesmo package da classes de negócio ?

Mas o caminho é por aí mesmo…

louds

Ou você pode usar um pouco de mágica com reflection, o ruim é que você perde a tipagem forte do java.

J

Tive uma ideia vejam se fica tosca

teste.funcionario

class FuncionarioDAO
{
  protected final setId(Funcionario func,int id)
  {
     func.setId(id);
  }
}

class Funcionario
{
   private int id;
   setId(int id) {}
   public getId() {} 	
}


teste.data


class DAOFactory
{
  FuncionarioDAO getFuncionarioDAO()
  {
     
  }
}

class FuncionarioHibernateDAO extends teste.funcionario.FuncionarioDAO
{
  // nao precisa fazer nada de diferente
}

class FuncionarioSqlDAO extends teste.funcionario.FuncionarioDAO
{
  public Funcionario consultar()
  {
     Funcionario f = new Funcionario();
     setId(f,1);
  }   
}
J

Nessas horas é que eu vejo que é melhor deixar dados e operaçoes tudo na mesma classe.
Se a classe Funcionario se persistisse esse problema não ocorreria.

Mas como criar uma classe de negócios que se auto-persista e ainda permita diversos mecanismos de persistencia ?

louds

jprogrammer:
Nessas horas é que eu vejo que é melhor deixar dados e operaçoes tudo na mesma classe.
Se a classe Funcionario se persistisse esse problema não ocorreria.

Mas como criar uma classe de negócios que se auto-persista e ainda permita diversos mecanismos de persistencia ?

Delegates, Strategy, State.

J

Exemplifique

louds
interface FuncionarioPersistenceStrategy {
   void save(Funcionario f);
}

class FuncionarioHibernate implements FuncionarioPersistenceStrategy {
  void save(Funcionario f) {
    session.save(f);
  }
}

class FuncionarioJDBC implements FuncionarioPersistenceStrategy {
  void save(Funcionario f) {
    insert into funcionario(nome) values ('oi');
  }
}

class Functionario {
  FuncionarioPersistenceStrategy strategy;


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

O livro do GOF explica State e Strategy muito bem, leitura recomendada.

F

Louds,

Me diz uma coisa, essa classe FuncionarioPersistenceStrategy tu instanciaria dentro da propria classe Funcionario ou usaria do tipo de DI pra isso?

Ps.: Eu ando fazendo isso num sistema aqui e nao tinha me dado conta do padrao.

]['s

louds

Fabio, fica a seu gosto e necessidade :wink:

Normalmente, eu pelo menos, tenho uma classe enumerando as implementações mais freqüêntes e uns helpers de uma Strategy.

kuchma

louds:
...

O livro do GOF explica State e Strategy muito bem, leitura recomendada.

Essa estrategia eh legal, pois apenas as camadas adjacentes comunicam-se entre si, mas ainda assim as classes de persistencia precisam obter/armazenar dados na classe de modelo. E ai vem o problema de ter gets/sets apenas para isso. Poderiamos fazer as classes de persistencia extender do modelo ou vice-versa, mas nao acho isso muito elegante.

Deparei-me com essa situacao essa semana: estou criando um modelo sem gets/sets, MAS na hora de puxar os dados da fonte tem essa encrenca. Nao posso utilizar Hibernate.

Ou seja - a solucao mais viavel parece ser criar gets/sets com acesso default/package e deixar as classes de persistencia e modelo no mesmo pacote. Voces veem alguma outra solucao?

A questao pode ser resumida em (IMHO): preciso dar acesso aos campos internos da classe, mas apenas para um caso especifico e excepcional (persistencia).

Marcio Kuchma

kuchma

Gosto de usar uma factory simples e, pra ganhar flexibilidade, mover os nomes das implementacoes para um properties externo (de preferencia fora do pacote principal, pra nao precisar redistribuir/fazer redeploy quando precisamos mudar alguma coisa).

Em projetos sem limitacoes e que voce pode fazer as coisas sem medo de ser feliz, IoC pode ser uma boa (evita esse trampinho tosco de instanciar as classes dinamicamente e tal, apesar de ser coisa que se faz apenas uma vez).

Marcio Kuchma

_fs

kuchma:
…MAS na hora de puxar os dados da fonte tem essa encrenca. Nao posso utilizar Hibernate.

Hibernate não precisa de getters e setters.

kuchma

LIPE:
kuchma:
…MAS na hora de puxar os dados da fonte tem essa encrenca. Nao posso utilizar Hibernate.

Hibernate não precisa de getters e setters.

Exato. Se eu pudesse utiliza-lo, o problema estaria resolvido. CQD. :mrgreen:

Marcio Kuchma

_fs

Ah, confundi causa com consequencia no seu post :smiley:

J

Adorei essa estratégia bem interessante.
A instaciação pode ser via factory ?

Mas e o problema dos campos privados, como o mecanismo de persistencia vai exergar os atributos privados ?
Seria interessante que cada tipo de mecanismo estivesse pacotes diferentes.

kuchma

jprogrammer:
Adorei essa estratégia bem interessante.
A instaciação pode ser via factory ?

Sim. Essa eh a funcao dela. :smiley:

jprogrammer:
Mas e o problema dos campos privados, como o mecanismo de persistencia vai exergar os atributos privados ?
Seria interessante que cada tipo de mecanismo estivesse pacotes diferentes.

Sem colocar gets/sets publicos ou utilizar estrategias heterodoxas com reflection nao vejo solucao. Alguem? :roll:

Marcio Kuchma

_fs

Injeta via IoC :smiley:

E quanto aos campos private, usa Hibernate :smiley: ou faz alguma coisa nojentinha com reflection ou alguma coisa assustadora com CGLib ou BCel ou faz como o cara sugere aqui:

J

E se o mecanismo de persistencia extendesse de uma classe abstrata do mesmo pacote da classe persisitida ao invés de uma interface.
E nessa classe tivesse métodos acessores para a classe a ser persistida.

ex:

package teste.funcionario;

class FuncionarioPersistStrategy
{
   protected abstract void save(Funcionario f);

   protected void setId(Funcionario f, int id)
  {
     f.setId(id);
  }
}

class Funcionario
{
   private int id ;
   void setId(int id) {}
}
_fs

Para mim parece melhor do que o normal :smiley: mas não entendo por que não usar Hibernate @.@

J

É pro caso de não usar.
Devemos estar preparados para tudo, não é por isso que não usaria.
Ninguém é louco.
Esse é ponto que sempre coloco.

_fs

Entendo isso perfeitamente. Nesse caso eu aplicaria a mesma solução que o Hibernate, alterando o bytecode em runtime, o que não é tão complicado assim. Mas já que está prontinho pra usar … :smiley:

Alguém pode se perguntar: para que essa merda toda? Posso colocar os getters e setters só para a camada de persistência funcionar e não chamá-los nunca.

Mas aí não fica bonitão né?

Thiago_Senna

Deixe-me ver se eu entendi. Isso quer dizer que seu eu criar a seguinte classe abaixo, ela é perfeitamente válida para ser persistida com o Hibernate?

public class Cat {

private int id;
private String nome;
private sex;

}

Será que estou confundindo Hibernate com Spring? Spring precisa de pelo menos um contrutor default né? Mas ele também depende dos gets e sets?

Perdoem-me pela confusão!

Abraços!
Thiago

J

Caramba !!!
O Hibernate muda o bytecode da classe ?
Eu pensei que ele fazia isso via reflection.
Isso não seria uma gambi.

Esse hibernate é do cara…

Thiago_Senna

jprogrammer:
Caramba !!!
O Hibernate muda o bytecode da classe ?
Eu pensei que ele fazia isso via reflection.
Isso não seria uma gambi.

Esse hibernate é do cara…

Aproveitando o embalo!

Por acaso é a biblioteca CGLIB quem faz com que esse tipo de gambi seja possível?

Observação:
Comecei um relacionamento amoroso com o Hibernate. Por isso vou começar a encher o saco! :wink:

Abraços!
Thiago

_fs

Leiam aqui:
http://wrschneider.blogspot.com/2005/01/avoiding-anemic-domain-models-with.html

E com CGLib dá para fazer isso e muito mais :smiley: cuidado @.@

kuchma

Vou investigar isso pra ver o grau de complexidade. Se nao for limitado pelo mecanismo de seguranca da VM pode ser otimo.

LIPE:
Alguém pode se perguntar: para que essa merda toda? Posso colocar os getters e setters só para a camada de persistência funcionar e não chamá-los nunca.

Mas aí não fica bonitão né?

Exatamente. Sem contar que eh deprimente aquele monte de gets/sets no Outline do Eclipse. Polui muito o visual. :XD:

Sobre o Hibernate, como voces ja falaram, ele nao resolve todos os problemas (persistencia nao-SGBD, p.ex.). Ah, e nao precisamos de recomendacao de outro mecanismo de persistencia. Apenas de como resolver esse problema de falta de elegancia no modelo. :smiley:

Marcio Kuchma

kuchma

Uma outra coisa boba que estive pensando hoje de manha a caminho do trabalho…

E se as implementacoes de persistencia devolvessem um Map, que a classe de modelo utilizasse para se auto-popular? Seria uma especie de “contrato” entre as partes: o modelo apenas aceita uma entrada tipo Map enquanto que todas as implementacoes de persistencia devolvem como produto final o Map citado. Isso para carregar os dados. Para salvar, processo inverso.

MAS, com isso perde-se a verificacao de tipos. Sem contar os trocentos efeitos colaterais nao-previstos na minha sonolencia matutina - aguardo as criticas. :smiley:

Enquanto isso vamos olhar a CGLib…

Marcio Kuchma

_fs

Muito tenso isso cara, ainda mais com objetos aninhados. Sei que há o commons-beanUtils, mas … dor hehe prefiro colocar os métodos mais próximos à classe.

J

Para usar esse negócio doido de CgLib é melhor usar HashMap.
Já pensei nisso o problema é com o hibernate.
Como vou persistir com o hibernate usando HashMap.

A tipagem talvez não seria o probelma, pois isso fica interno.

Alguém sabe ?

_fs

Hibernate suporta isso, leia a documentação do 3.0. Na verdade você pode mapear os dados para qualquer coisa que bem entender hehe

J

Aí não vira uma gambi medonha.
O ideal é o hibernate persistir a classe em si, não o hashmap.

Já que o phillip deu a ideia de criar um factory dos domains model objects, por que não criar várias implementações.
Um para cada persistencia.
Seria tosco isso ?

louds

Usar um map para isso seria um desastre, com direito performance deprimente e manutenção infernal.

A questão é que java não possui um mecanismo claro de persistencia na linguagem, e isso exige que toda e qualquer solução tenha um pouco de “gambiarra” ou seja complexa o suficiente para não valer a pena.

Sinceramente, hibernate/EJB3.0/ORMxyz usando atributos privados me parece o mais próximo do ideal que pode se conseguir com java.

Isso falando de persistencia relacional, claro.

kuchma

jprogrammer:
Aí não vira uma gambi medonha.
O ideal é o hibernate persistir a classe em si, não o hashmap.

Nao sei se eu perdi o fio da meada, mas o problema existe NAO utilizando o Hibernate. Com Hibernate voce pode persistir os atributos privados mesmo (ou seja la o que for que voce queira utilizar :D).

Marcio Kuchma

J

Equanto utilizar o hibernate não temos mais dúvidas.
Mas vamos supor que a persistencia é não relacional.

Porque o hashmap traz perda de performance tão grande ?

Thiago_Senna

Usando o hashmap você fica dependente do desempenho do algoritmo de classificação que você esta implementando mo método hashcode de suas classes persistentes.

Talvez seja isso? É isso mesmo?

Abraços!
Thiago

J

Pegando embalo com o shoes…
E a possibilidade de ter várias implementações

abstract class Funcionario
{ 
    private int id;
    protected void setId(int id) { //omite codigo }
    public int getCodigo() {}

    public void salvar()
    {
        if (id == 0) {} // validacao e regras de negocio
     }
}

class FuncionarioHibernate extends Funcionario
{
    public void salvar()
   {
      setId(geraId()); 
     super.salvar();
      session.save(this);
   }
 
}

class FuncionarioOutraCoisa extends Funcionario
{
    public void salvar()
   {
      setId(geraId()); 
     super.salvar();
   }
}

class FuncionarioFactory
{
    public Funcionario getFuncionario()
    {
        return new FuncionarioHibernate();
    }
}

Funcionario f  = FuncionarioFactory.getgetFuncionario();
f.salvar();
_fs

Na minha opinião é muito mais limpo que DAOs. E fica melhor ainda com IoC ao invés de factories.

Mas aí acho que deve doer para controlar transações e evitar a proliferação de chamadas ao banco. Spring ajuda nisso?

J

Não vejo porque isso traria muitas chamadas ao banco.
Posso ter um factory global e controlar conexao e transação a partir dele.

Mas o que eu acho esquisito de classes que tem métodos de negócios é quando eu tenho agregação.

class Funcionario
{
   private Departamento departamento;
   public Departamento getDepartamento() {};

   public void salvar()
}

class Departamento 
{
   public void salvar()
}

Funcionario f = getFuncionario();
f.salvar().
f.getDepartamento().salvar() // esse método esquisito

Isso fica estranho ou é só frescura (rs…) ?

Thiago_Senna

Tá estranho sim! Não seria melhor que dentro do método salvar de funcionario tivesse uma chamada para salvar o departamento?

louds

Hmm, isso depende de como você quer implementar a semântica de cascateamento.

Se salvar o funcionario implica em salvar o departamento, sim.

Eu acho que manter o código que faz pensistencia no mesmo lugar que está o negocio uma caca.

E se a implementação de persistencia ficar toda separada em um objeto separado e se usar agregação/herança para dar acesso a esses dados no modelo.

O código não ficar mais claro com isso já que java não suporta herança múltipla nem visibilidade de herança (herança privada do c++ faz muita falta :cry: ).

J

No caso não estou salvando o departamento, apenas estou mostrando que ao agregar a classe Departamento a Funcionario o método de salvar
departamento também fica disponível.

Esse é o assunto do outro tópico.
Pelo conceito OO as operaçoes devem ficar juntas com os dados.
Se quisermos programar OO deve ficar mais ou menos parecido com isso.

Estão vendo como é difícil programar OO com relacional.
Várias coisas não se batem. São duas coisas que não se encaixam.
O pessoal meteu o pau no artigo da java magazine.

editado:
Tem outra coisa na classes que fazem as operações.
Os métodos de consulta.
É muito estranho criar uma instancia para depois obter instancias.
O ideal seria usar métodos estáticos para consulta.
Só que a herança já quebra.
Pois há somente “ocultação” não sobreposição.
ex:

class Funcionario
{
   public static Funcionario consultar() {}
}

class FuncionarioHibernate
{
   public static Funcionario consultar() { // oculta}
}

Mas exemplifique sua ideia louds.

louds
class PersistentFuncionacio {
  private int xxx;
  private String yyy;

  //Setter/getters com bound properties
}


class Funcionario {
 private final PersistentFuncionacio f = new PersistentFuncionacio ();//ou via construtor


 public double salarioLíquito() {
   return f.getSalarioBruto * (1 - f.getDescontos());
 }
}

Para que isso? Bom, usando bound properties evitamos precisar chamar save/update explicitamente e delete em alguns casos. Além disso toda lógica e dados para falar com o banco de dados fica em lugar só.

J

E se eu quiser acessar diretamente um atributo que é pesistível como Nome (por ex) teria que duplicar sua declaração nas duas classes não é.

J

Pensei nisso. Vejam se é tosco !

package teste.funcionario;

abstract class Funcionario
{
   protected void setId(int id);
   public int getId() { return this.id } ;

   public void salvar() {
     // regras de negocio
   }
}


abstract class FuncionarioFactory
{
  public abstract Funcionario createNew();
  public abstract Funcionario find(int id);
  public static final FuncionarioFactory getInstance()
  {
    return new teste.funcionario.jdbc.FuncionariosJDBCFactory();
  }
}


package teste.funcionario.jdbc;

class FuncionariosJDBCFactory extends FuncionarioFactory
{
   public Funcionario createNew() {
      return new FuncionarioJDBC();
   }

   public Funcionario find(int id)
   {
     ResultSet rs = ....

     FuncionarioJDBC  f = new FuncionarioJDBC ();
     f.setId(rs.getInt("ID");
   }
}

class FuncionarioJDBC extends Funcionario
{
   protected void setId(int id) { super.setId(id) }

   public void salvar() {
      setId(geraId());
      super.salvar();
      insert into ....
   }
}

FuncionarioFactory fc = FuncionarioFactory.getInstance();
Funcionario f = fc.find(1);

// faz de conta que tem
f.setNome("Maria");
f.salvar();
Criado 28 de abril de 2005
Ultima resposta 2 de mai. de 2005
Respostas 51
Participantes 7