EntityManagerFactory

Opa! E aí pessoal!

Meu primeiro post aqui no forum.

Já agradeço a todos porque já resolvi muitos problemas com a ajuda do fórum, só vendo dúvidas já respondidas.

Bom, minha dúvida é a seguinte:

Estou fazendo um projeto em 4 camadas (Modelos, DAO, Controller e View) para Desktop.

Nas classes DAO, eu criei uma classe abstrata que cotém todos os recursos comuns as DAOs de cada modelo.

Criei nessa classe abstrata um atributo estatico EntityManagerFactory que já instanciado.

Isso é errado? Meu chefe falou pra usar uma classe singleton para gerenciar a Factory, axei desnecessário.

Se alguem puder dar opiniões, eu agradeço. Estou aprendendo ainda.

[]'s

public abstract class AbstractDAO<T> {

    protected Class<T> entityClass;
    private static EntityManagerFactory EMF = Persistence.createEntityManagerFactory("EleicaoUrnaPU");

    public AbstractDAO(Class<T> entityClass) {
        this.entityClass = entityClass;
    }

    protected final EntityManager getEntityManager() {
        return EMF.createEntityManager();
    }

    public abstract void create(T entity);

    public abstract void edit(T entity) throws NonexistentEntityException, Exception;

    public abstract void destroy(Integer id) throws IllegalOrphanException, NonexistentEntityException;

    public T find(Integer id) {
        EntityManager em = getEntityManager();
        try {
            return em.find(entityClass, id);
        } finally {
            em.close();
        }
    }

    public List<T> findAll() {
        return find(true, -1, -1);
    }

    public List<T> findRange(int maxResults, int firstResult) {
        return find(false, maxResults, firstResult);
    }

    private List<T> find(boolean all, int maxResults, int firstResult) {
        EntityManager em = getEntityManager();
        try {
            CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
            cq.select(cq.from(entityClass));
            Query q = em.createQuery(cq);
            if (!all) {
                q.setMaxResults(maxResults);
                q.setFirstResult(firstResult);
            }
            return q.getResultList();
        } finally {
            em.close();
        }
    }

    public int getCount() {
        EntityManager em = getEntityManager();
        try {
            CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
            Root<T> rt = cq.from(entityClass);
            cq.select(em.getCriteriaBuilder().count(rt));
            Query q = em.createQuery(cq);
            return ((Long) q.getSingleResult()).intValue();
        } finally {
            em.close();
        }
    }
}

Se sua aplicação for multi usuário, você vai ter problemas de concorrência no EntityManagerFactory, já que você fecha o mesmo (tornando ele inválido) em diversos métodos do seu DAO. O mesmo poderia ser feito apenas em um ContextListener.

Além disso se o EntityManager for criado pelos seus DAOs, como você vai gerenciar o escopo de uma transação que usa mais de um DAO?

[quote=esmiralha]Se sua aplicação for multi usuário, você vai ter problemas de concorrência no EntityManagerFactory, já que você fecha o mesmo (tornando ele inválido) em diversos métodos do seu DAO. O mesmo poderia ser feito apenas em um ContextListener.

Além disso se o EntityManager for criado pelos seus DAOs, como você vai gerenciar o escopo de uma transação que usa mais de um DAO?[/quote]

Obrigado por responder.

Então como eu disse, sou inexperiente.

Usando o design pattern singleton, para gerenciar a Factory resolveria?
O objeto factory num deveria ser o mesmo para todo o sistema?

onde eu o utilizo para criar e destruir os objetos EntityManager que gerenciariam as transações?

Cara, em primeiro lugar, me desculpa. Eu li seu código muito rápido e falei uma grande bobagem sobre ele. Você está fechando o EntityManager nos seus DAOs e não o EntityManagerFactory… Desculpe por te confundir mais ainda…

Refazendo o meu comentário:

Você pode usar um Singleton para encapsular o EntityManagerFactory. Dessa forma, você poderá demarcar a transação no seu Controller e chamar métodos dos DAOs como parte da mesma transação.

[quote=esmiralha]Cara, em primeiro lugar, me desculpa. Eu li seu código muito rápido e falei uma grande bobagem sobre ele. Você está fechando o EntityManager nos seus DAOs e não o EntityManagerFactory… Desculpe por te confundir mais ainda…

Refazendo o meu comentário:

Você pode usar um Singleton para encapsular o EntityManagerFactory. Dessa forma, você poderá demarcar a transação no seu Controller e chamar métodos dos DAOs como parte da mesma transação.[/quote]

Ah blz…
Mas a minha duvida é a seguinte: da forma que coloquei na minha classe AbstractDAO, um atributo estatico EntityManagerFactory, teria alguma desvantagem com relação ao uso de um singleton?

Imagine que você tem que salvar Turma e seus Alunos. Supondo que você tem dois DAOs: TurmaDao e AlunoDao, como você faria para que TurmaDao e AlunoDao executassem sob uma mesma transação? Você precisa demarcar o início e fim da transação. Se ela tem um escopo que se estende além uma única classe, normalmente isso é feito de forma programática (código Java) ou declarativa (anotações) numa camada de Serviço ou então por um mecanismo ortogonal como um Aspecto.

Para que isso funcione, é preciso que todo mundo referencie o mesmo EntityManagerFactory. Se ele for um atributo private no AbstractDao, ele não poderá ser usado por uma classe de Serviço ou por um Aspecto…

[quote=esmiralha]Imagine que você tem que salvar Turma e seus Alunos. Supondo que você tem dois DAOs: TurmaDao e AlunoDao, como você faria para que TurmaDao e AlunoDao executassem sob uma mesma transação? Você precisa demarcar o início e fim da transação. Se ela tem um escopo que se estende além uma única classe, normalmente isso é feito de forma programática (código Java) ou declarativa (anotações) numa camada de Serviço ou então por um mecanismo ortogonal como um Aspecto.

Para que isso funcione, é preciso que todo mundo referencie o mesmo EntityManagerFactory. Se ele for um atributo private no AbstractDao, ele não poderá ser usado por uma classe de Serviço ou por um Aspecto…[/quote]

Desculpe insistir, mas você reparou que o atributo factory é estático?

Além disso criei um método getEntityManager() na AbstractDAO, esse método retorna um EntityManager criado pela mesma factory que é estática em AbstractDAO.

Pode ser falta de conhecimento da minha parte, mas e se o mesmo método do DAO puder ser usado em diferentes contextos transacionais? Posso inserir uma Turma e querer comitar a transação. Ou inserir uma Turma, 10 Alunos, 1 Professor e aí sim comitar a transação. Como você teria esse controle com o código que você tem até agora?

Não sei. E como eu teria com o singleton?

Não sei. E como eu teria com o singleton?[/quote]

Não afeta suas transações deixar o EMF como atributo estático, pois qualquer DAO que fizer uma transação, estará se comunicando o mesmo EMF.

PS: Tbm gostaria de saber os benefícios de ter o EMF como Singleton :lol:

Não sei. E como eu teria com o singleton?[/quote]

Não afeta suas transações deixar o EMF como atributo estático, pois qualquer DAO que fizer uma transação, estará se comunicando o mesmo EMF.

PS: Tbm gostaria de saber os benefícios de ter o EMF como Singleton :lol: [/quote]

Valew j0nny.

Mais alguém tem algo a dizer sobre isso??? Alguém é contra???

[]'s agradeço a todos!

Não sei. E como eu teria com o singleton?[/quote]

Não afeta suas transações deixar o EMF como atributo estático, pois qualquer DAO que fizer uma transação, estará se comunicando o mesmo EMF.

PS: Tbm gostaria de saber os benefícios de ter o EMF como Singleton :lol: [/quote]

E como você demarcaria as transações, Jonny?

Não sei. E como eu teria com o singleton?[/quote]

Não afeta suas transações deixar o EMF como atributo estático, pois qualquer DAO que fizer uma transação, estará se comunicando o mesmo EMF.

PS: Tbm gostaria de saber os benefícios de ter o EMF como Singleton :lol: [/quote]

E como você demarcaria as transações, Jonny?[/quote]

Ex:
Tenho um objeto Session que encapsula um EntityManager (que está ligado ao EMF blábláblá);
Passo esse Session no construtor de PessoaDao e ProdutoDao, por exemplo;
Antes de dar um save em cada dao, inicio a transação, só então salvo nos daos, aí então realizo o commit…

DI cara :lol:

Não sei. E como eu teria com o singleton?[/quote]

Não afeta suas transações deixar o EMF como atributo estático, pois qualquer DAO que fizer uma transação, estará se comunicando o mesmo EMF.

PS: Tbm gostaria de saber os benefícios de ter o EMF como Singleton :lol: [/quote]

E como você demarcaria as transações, Jonny?[/quote]

Ex:
Tenho um objeto Session que encapsula um EntityManager (que está ligado ao EMF blábláblá);
Passo esse Session no construtor de PessoaDao e ProdutoDao, por exemplo;
Antes de dar um save em cada dao, inicio a transação, só então salvo nos daos, aí então realizo o commit…

DI cara :lol: [/quote]

Quem decide quando a transação começa e quando ela termina nesse exemplo, Jonny?

Explicando melhor:

PessoaDao

savePessoa(Pessoa pessoa) {

// abre transacao
// persiste Pessoa
// comita transação

}

ProdutoDao

saveProduto(Produto produto) {

// abre transacao
// persiste Produto
// comita transação

}

Se eu preciso de uma transação onde tenho que alterar atomicamente uma Pessoa e um Produto, como faço?

Não sei. E como eu teria com o singleton?[/quote]

Não afeta suas transações deixar o EMF como atributo estático, pois qualquer DAO que fizer uma transação, estará se comunicando o mesmo EMF.

PS: Tbm gostaria de saber os benefícios de ter o EMF como Singleton :lol: [/quote]

E como você demarcaria as transações, Jonny?[/quote]

Ex:
Tenho um objeto Session que encapsula um EntityManager (que está ligado ao EMF blábláblá);
Passo esse Session no construtor de PessoaDao e ProdutoDao, por exemplo;
Antes de dar um save em cada dao, inicio a transação, só então salvo nos daos, aí então realizo o commit…

DI cara :lol: [/quote]

Quem decide quando a transação começa e quando ela termina nesse exemplo, Jonny?[/quote]

Depende da sua lógica…

Não sei. E como eu teria com o singleton?[/quote]

Não afeta suas transações deixar o EMF como atributo estático, pois qualquer DAO que fizer uma transação, estará se comunicando o mesmo EMF.

PS: Tbm gostaria de saber os benefícios de ter o EMF como Singleton :lol: [/quote]

E como você demarcaria as transações, Jonny?[/quote]

Ex:
Tenho um objeto Session que encapsula um EntityManager (que está ligado ao EMF blábláblá);
Passo esse Session no construtor de PessoaDao e ProdutoDao, por exemplo;
Antes de dar um save em cada dao, inicio a transação, só então salvo nos daos, aí então realizo o commit…

DI cara :lol: [/quote]

Quem decide quando a transação começa e quando ela termina nesse exemplo, Jonny?[/quote]

Depende da sua lógica…[/quote]

E voc6e acha que os DAOs são o melhor lugar para se colocar lógica?

Não sei. E como eu teria com o singleton?[/quote]

Não afeta suas transações deixar o EMF como atributo estático, pois qualquer DAO que fizer uma transação, estará se comunicando o mesmo EMF.

PS: Tbm gostaria de saber os benefícios de ter o EMF como Singleton :lol: [/quote]

E como você demarcaria as transações, Jonny?[/quote]

Ex:
Tenho um objeto Session que encapsula um EntityManager (que está ligado ao EMF blábláblá);
Passo esse Session no construtor de PessoaDao e ProdutoDao, por exemplo;
Antes de dar um save em cada dao, inicio a transação, só então salvo nos daos, aí então realizo o commit…

DI cara :lol: [/quote]

Quem decide quando a transação começa e quando ela termina nesse exemplo, Jonny?[/quote]

Depende da sua lógica…[/quote]

E voc6e acha que os DAOs são o melhor lugar para se colocar lógica?
[/quote]

Claro que não, é por isso que injeto (DI) minha session nos daos, para em outra classe que cuida da sua lógica, se iniciar a transação, passar esse session para os daos, salvar e então comitar.

E quando vc configura seu framework de DI que escopo você usa para o objeto Session que encapsula o EntityManagerFactory?

O EntityManagerFactory deve ser aberto apenas uma vez em toda a aplicação. Da forma como está seu código, será criado um EntityManagerFactory para cada DAO que você tiver.

Retire a criação do EntityManagerFactory de dentro do DAO. Você pode, por exemplo, criar uma classe JPAUtil da seguinte forma:

[code]public class JPAUtil {
private static EntityManagerFactory emf;

static {
    emf = Persistence.createEntityManagerFactory("EleicaoUrnaPU");
}

public static EntityManager createEntityManager() {
    return emf.createEntityManager();
}

// Chame esse método quando sua aplicação se encerrar. Se for web, você pode escrever um listener pra fazer isso.
public static void closeEntityManagerFactory() {
    emf.close();
}

}[/code]
Sobre as transações, nunca abra e feche transações dentro do DAO. Para isso, escreva seus DAOs de forma que recebam um EntityManager já criado (via construtor por exemplo), pois alguma transação aberta por ele pode já estar em andamento.

O ponto é esse que o Aires descreveu, Jonny. O código original tinha diversos problemas além de “devo usar um Singleton ou não?”. Ao meu ver, introduzir um Singleton para gerenciar o EntityManagerFactory seria um ganho perante o código original.

Não tem nada de mágico em DI. É mais sofisticado que uma AbstractFactory que externaliza os nomes das implementações em um arquivo e usa reflexão para instancia-las, mas não é mágica.