Jpa - dao

25 respostas
D

Estou em um grande projeto, com um modelo de desenvolvimento totalmente componentizado, mesmo porque existe 4 consultorias no mesmo projeto.

Uma das atividades nossas será criar uma camada de persistência para a base relacional, que possui cerca de 120 tabelas, altemente relacionadas.

Por questões técnicas, achei melhor não utilizar os relacionamentos entre as entidades (ejb/jpa) e criar classes que mapeiam puramente uma tabela (incluindo pks, fks e outros campos).

Portanto terei um único DAO para fazer o CRUD dessas entidades, implementado como um EJB Stateless Session Bean, para que os sistemas acessem facilmente este componente, para manipular os dados desejados.

Bom, a questão é que quero isolar das outras camadas a necessidade de conhecer JPA e todas as características dele, como por exemplo o update e delete dos objetos MANAGED.

Quero apenas fornecer alguns métodos: create, update, delete, findByPK, e os desenvolvedores não precisam se preocupar se o objeto é MANAGED, DETTACHED, etc.

Nos meus testes, fazer um simples merge no update, funciona, porém, um simples delete de um objeto NEW, não funciona. Ou terei de fazer um find e deletar, ou então um merge.

Enfim. Alguem implementou uma solução destas? Como resolveu, sem complicar muito?

25 Respostas

A

Um dia vou chegar a desenvolver um sistema desse, parabens daniel pelo conhecimento

Falow

L

Daniel;

Tenho uma infra aqui na empresa mais ou menos como vc descreveu, porem eu optei por construir um ancestral para cadastramento basico.

Este ancestral faz parte de uma hierarquia de classes nossas.

Fornecemos para o modelo de domínio um ancestral de entidade que tem algumas operações basicas.

No caso optamos por denominar nao DAO, abolimos este conceito aqui porque no nosso entender, o DAO praticamente é o Entity manager - Inclusive essa era uma opinião que queria ter com alguns experts.

Portanto nossos serviço ancestral de CRUD, encapsula o Entity Manager e cuida das operacoes. Bem como prove alguns outros serviços específicos do nosso negócio.

Sendo assim os desenvolvedores tem apenas que estender nossa entidade ancestral e o trabalho de mapeamento das entidades com as anotações JPA, alem de estender o nosso ancestral de seviços que implementar suas regras de negócio.

tem funcionado legal.

Inclusive aceito criticas e sugestoes…

D

Entendo, mas me preocupa mais a questão técnica da implementação com JPA mesmo.

Quero ter um método update e um delete, que não tenha problema em receber um objet NEW, MANAGED ou DETTACHED, mas que cuide da operação em sim, independente do estado.

A

Prq vc não usa ejb-ql?
Vc esta fazendo um DAO parametrizado? tipo DAO<E>?
Eu não gosto desta abordagem prq por ex, utilizando hibernate o metodo load devolve um proxy, o que inviabiliza a serialização para envio em um contexto onde não existe esta sessão.

D

EJB-QL, SQL, X-QL, não é esta a questão.

A questão é. Precio de uma classe que faça o CRUD dos objetos do meu modelo de negócio. Porém não quero que quem utilize esta camada de persistência tenha que saber que é JPA, ou Hibernate, Banan Caramelada, mas tem apenas que saber que deve passar um objeto do modelo para criar/atualizar/remover ou então buscar.

Não quero passar aos desenvolvedores a preocupação com NEW/MANAGED/DETTACHED, etc.

Meu código hoje:

@Stateless(name="BaseTransacional")
public class BaseTransacionalBean implements BaseTransacional {
    @PersistenceContext(unitName="Nome_do_PU")
    private EntityManager em;
    
    public void create( Object entity ) {
        em.persist(entity);
        em.flush();
    }
    
    public &lt;T extends Object&gt; T update( T entity ) {
        T t = em.merge(entity);
        em.flush();
        return t;
    }
    
    public void delete( Object entity ) {
        em.remove(entity);
        em.flush();
    }

    public &lt;T extends Object&gt; List&lt;T&gt; findAll(Class&lt;T&gt; clazz) {
        return em.createNamedQuery(clazz.getSimpleName()+".findAll").getResultList();
    }
    
    public &lt;T extends Object&gt; T find( Class&lt;T&gt; clazz, Object pk ) {
        return em.find( clazz, pk );
    }
}

Mas ele ainda não me atende.

L

danieldestro:
Entendo, mas me preocupa mais a questão técnica da implementação com JPA mesmo.

Quero ter um método update e um delete, que não tenha problema em receber um objet NEW, MANAGED ou DETTACHED, mas que cuide da operação em sim, independente do estado.

Pois é, eu presumimo o seguinte:

Na camada de apresentacao, o objeto pode ser apenas new e detached, pois fica fora do contexto de persistencia, entao para as operacoes CRUD

Ceate - o o objeto é criado na camada de apresentacao, e passado para o EJB de serviço para gavação, neste caso temos um método inserir que encapsula a chamada do entitymanager.persist (aqui paira uma dúvida arquitetural, nao seria melhor ter um métudo de serviço que receba todos os atributos do objeto de dominio e este sim crie o objeto e persiste?)

Retrieve - São disponibilizados métodos de consulta e recuperacao do objeto, neste caso executam queries (que podem ser dinamicqas ou named queries) e tbem o método entitymanager.find

Update - os objetos de alguma forma sao recuperados da cmada de persistencia pelo Retrieve alteradoe e enviados para o Servço EJB, com a chamada de um método atualizar, o ancestral encapsula o retorno deste objeto pra o contexto de persistencia, praticamente encapsula a chamada do entitymanager.merge

Delete
, é passado o objeto que desejamos excluir.

Para cada caso é implementada regras de negócio específica dos objetos que herdam os ancestrais.

Ainda assim gostaria de opiniões sobre essa abodagem.

A

isso não funciona?

if(!entityManager.contains(obj)) {
    //Deixa o objeto tipo lazy load (não vai ao banco aqui)  
    obj = entityManager.getReference(Pessoa.class, chavePrimaria) ;
}

entityManager.remove(obj);
D

Correto!

Método que recebe os atributos? Para que? Posso usar a OO e não preciso criar 120 métodos de inserção.

Ok. O objeto retornado pelo EM é retornado pelo método find.

Eu não queria fazer um find, para então fazer um update. Queria simplesmente passar o objeto e atualizar no BD… mas isso pode ser ruim, pois existem casos que só preciso atualizar um campo, e não o objeto todos, e isso pode comprometer minha base. Então, pelo jeito, preciso fazer um find, atualizar o obeto e (se necessário) fazer merge.

Em teoria deveria ser isto, mas to vendo que preciso dar um find antes. Quero evitar este passo.

D

Abdon:
isso não funciona?

if(!entityManager.contains(obj)) { //Deixa o objeto tipo lazy load (não vai ao banco aqui) obj = entityManager.getReference(Pessoa.class, chavePrimaria) ; } entityManager.remove(obj);

Considere 120 classes (entities) no meu modelo. Quero simplificar, ou terei que implementar algum mecanismo mais efetivo.

L

Perfeito!!!

e alem disso se fizer um merge e por algum motivo esse objeto nao estiver persistido, ele vai ser inserido, em alguns casos pode ser um comportamento indesejado.

D

Foi o que percebi nos testes.
Como fazer um UPDATE APENAS se o registro existir?

R

danieldestro:

Nos meus testes, fazer um simples merge no update, funciona, porém, um simples delete de um objeto NEW, não funciona. Ou terei de fazer um find e deletar, ou então um merge.

Daniel, delete em entities no estado new nunca irá funcionar mesmo. Ctr+C/Ctrl+V do meu blog:

Não tem como você criar uma query antes da atualização para verificar se determinado objeto já foi persistido? Se não foi, o update no dao não chama merge e retorna.
Ou então só verificar se a entidade tem um pk? Se você estiver utilizando geração de pk, um novo objeto não teria pk e, nesse caso, o merge não seria chamado.

R

Olha só, eu e o fagnão respondemos quase a mesma coisa na mesma hora. 8)

D

Acho que meu update teria que ser algo assim:

public &lt;T extends Object&gt; T update( T entity ) { if( em.contains(entity) ) { return em.merge( entity ); // embora acho que isso é desnecessário } .... /// aaarrghhh }

Não, não tem jeito… sem causar um tremendo overhead ou efeitos colaterais no sistema.

Melhor eu documentar dizendo que o desenvolvedor DEVE fazer um find, para então fazer um update.

Cara, às vezes acho que JPA pode ter sido pior que o modo que o Hibernate tratava das coisas.

D

seufagner:
nao cara… como seria um comportamento indesejado?
se ele nao existe, ele é persistido no proximo flush() ou commit()… enquanto isso ele apenas é managed… e pode sofrer rollback

se ele existe, é executado uma busca ao objeto… se encontrar ele compara todos os atributos e os que foram modificados sao sobrescritos com o estado do objeto atual… e é só isso, nao tem nada além.

Sim, indesejado.
Eu só quero um UPDATE, não INSERT.

P

Sinceramente eu não entendi muito da descrição (tá detalhada mas eu achei confusa) mas vamos a alguns general advices:

  • Utilize Domain Driven Design e deixe as outras Camadas lidarem com objetos de negócio que não têm nada de JPA/DAO/whatever

  • Para consultas utilize a dupla Repository/Specification, implementadas por DAO/QueryObject respectivamente e instanciadas através de factories suas ou via IoC

  • Para atualizações assuma um padrão e o publique. Se você precisa lidar com objetos que atravessam tiers (Camadas Físicas) ou que por algum motivo precisam ser dettached assuma que todos os objetos que saem do seu controle (da sua Camada/Layer) são dettached. Pode ser ineficiente para alguns casos mas criar um padrão é crucial para projetos grandes

  • Caso opte por um padrão “todo objeto pe dettached e precisa ser atualizado manualmente” e você opte por DataMapper ao invés de ActiveRecord coloque métodos que atualizam no seu Repository

  • Dentro da sua Camada use o que quiser, desde que não vaze conhecimento sobre o que está utilizando para fora

A

danieldestro:
Estou em um grande projeto, com um modelo de desenvolvimento totalmente componentizado, mesmo porque existe 4 consultorias no mesmo projeto.

Uma das atividades nossas será criar uma camada de persistência para a base relacional, que possui cerca de 120 tabelas, altemente relacionadas.

Sei não hein? Nunca vi componentes com 120 entidades e 1 DAO. Ou seu sistema não é componentizado ou vc não deveria ter só 1 DAO.

O ideal é que cada componente possua uma quantidade reduzida de entidades fortemente acopladas e que cada componente possua seu próprio DAO.

Beba direto da fonte…

http://www.hibernate.org/328.html

D

Existem casos e casos.

Estou num grande projeto, onde 4 consultorias desenvolverão módulos diferentes do sistema.

Haverá um componente de persistência para a base de dados deles.

É “impossível” termos o domínio desse modelo de 120 entidades, mesmo porque cada empresa terá domínio apenas de parte do todo.

Portanto algumas decisões foram tomadas (ex: classes desacopladas e sem relacionamentos).

Além do mais o meu único “DAO” fornecerá meios para CRUD apenas.

No caso de buscas específicas, provavelmente serão criados mais DAOs para o propósito.

Obrigado pelo link. Lê-lo-ei! hehehehehe…

P

Destro isso é um caso de Componentes horizontais. Geralmente para este tipo de componente nós utilizamos algo pronto, como o Hibernate, apenas customizado, então se você está tendo problemas possivelmente a escolha da formação horizontal não foi adequada.

Do ponto de vista de Component-based Design vocês deveriam repartir o domínio e deixar os componentes responsáveis pelos pedaços e se comunicando para executar os casos de uso. Cada empresa, equipe, etc. poderia ficar responsável por um ou mais componentes, talvez utilizando guidelines e frameworks horizontais mas ainda ssim presas a um domínio.

Resumindo: Eu (olhando de longe) separaria os componentes por domínio e adotaria guidelines para conceitos horizontais (Persistência, interface, etc.)

D

Não é que estou tendo um problema. Eu só queria um feedback da experiência de vocês com casos como este.

Como adotei JPA para a persistência, eu queria saber uma boa prática para o caso. Acabei adotando uma solução aqui, que até o momento, na minha prova de conceito, funciona!

Só com testes mais abrangentes (uso, carga, stress) a solução se provará adequada ou não. Vou providenciar estes testes ainda.

Como o próprio cliente pediu um projeto para criar esta camada de persistência, para que seja reutilizado por todas as consultorias, não daria para quebrar isso em módulos.

Deixar de lado os relacionamentos foi uma decisão tomada para favorecer o desenvolvimento, sem adicionar complexidade na utilização destas classes persistentes nos diferentes módulos, onde o domínio é mais restrito.

C

Seus problemas acabaram !!!

http://code.google.com/p/dataslim/

Ainda não testei, mas achei a solução muito boa e limpa. Estarei testando no proximo findi.

Abraços,

Carlos MacLeod

R

iai, Daniel.

Fala aí se funcionou a parada, plz.

[]´s

L



http://www.nabble.com/Re:-Dúvidas-sobre-JPA-p15185421.html

S

apenas com uma operacao nao tem como. tu tens que executar um find() ou getReference() e depois dar um merge(), caso ele exista…

S

Perfeito!!!

e alem disso se fizer um merge e por algum motivo esse objeto nao estiver persistido, ele vai ser inserido, em alguns casos pode ser um comportamento indesejado.

nao cara… como seria um comportamento indesejado?
se ele nao existe, ele é persistido no proximo flush() ou commit()… enquanto isso ele apenas é managed… e pode sofrer rollback

se ele existe, é executado uma busca ao objeto… se encontrar ele compara todos os atributos e os que foram modificados sao sobrescritos com o estado do objeto atual… e é só isso, nao tem nada além.

Criado 25 de julho de 2007
Ultima resposta 25 de jul. de 2007
Respostas 25
Participantes 11