JPA: get-use-close do EntityManager e carregamento lento

9

A IBM sugere que a melhor prática para usar o EntityManagers é obter / usar / fechar. Se o EntityManager não estiver fechado, há uma chance de que o mesmo EntityManager possa ser usado por mais de um thread, o que resultará no seguinte erro:

<openjpa-2.1.2-SNAPSHOT-r422266:1179900 fatal general error> org.apache.openjpa.persistence.PersistenceException: Multiple concurrent threads attempted to access a single broker. By default brokers are not thread safe; if you require and/or intend a broker to be accessed by more than one thread, set the openjpa.Multithreaded property to true to override the default behavior. 

Se você carregar um objeto que tenha uma coleção OneToMany mapeada como fetch = LAZY desta forma:

public T find(Object id) {
    T t = null;
    EntityManager em = getEm();
    t = em.find(type, id);
    em.close();
    return t;
}

EntityManager getEm() {
    if(this.em ==null || !this.em.isOpen()) {
        this.em = emf.createEntityManager();            
    }
    return this.em;
}

A coleção sempre será nula e quando alguém chamar o getter, o EntityManager será fechado. A coleção só é carregada se a busca for EAGER, mas isso resulta em uma junção SQL toda vez que é lenta.

Portanto, é um erro "Vários encadeamentos" ou openjpa.Multithreaded = true, que é uma prática incorreta ou lenta devido à junção do SQL a cada momento, mesmo que a coleta não seja necessária. Existe alguma maneira de fazê-lo corretamente, por isso é rápido com o Lazy fetch e feito apenas com as melhores práticas?

    
por Maxim Suponya 19.10.2012 в 08:15
fonte

4 respostas

3

Ok, aqui está minha conclusão depois de dois dias de pesquisa sobre o assunto. Para aplicativos que não podem confiar em serem implantados em um servidor Java EE e não podem garantir acesso único a threads (por exemplo, aplicativos da web no tomcat), a melhor prática será abrir, usar e fechar gerenciadores de entidades no escopo do método do objeto DAO.

Isso significa que o carregamento lento não funcionará fora do DAO. Para contorná-lo, em métodos que localizam uma entidade por id, todas as coleções precisam ser buscadas chamando size () na coleção para acionar a busca antes que o gerenciador de entidades seja fechado. Isso fará com que o método find retorne um objeto totalmente carregado, mesmo que a busca seja preguiçosa.

Para métodos que retornam coleções de objetos, como a pesquisa, será muito lento carregar totalmente cada entidade no resultado da pesquisa para que os resultados sejam retornados como estão sem filhos. Sempre que uma das entidades no resultado da pesquisa precisar ser visualizada, ela deverá ser carregada individualmente por meio desse método que obtém objetos totalmente carregados.

    
por Maxim Suponya 23.10.2012 / 12:01
fonte
2

ok, sem usar o Java EE, você pode criar um pool simples de EntityManagers. Eu uso um StackKeyedObjectPool (do Apache Commons Pool) e criar novos EntityManagers quando eu precisar deles. Eu tenho uma interface de empréstimo / retorno e o pool cria automaticamente novo objeto conforme necessário. Consulte o link

    
por Zagrev 22.10.2012 / 18:43
fonte
0

Na verdade, você deve conseguir que o Websphere injete o gerenciador de entidades para você.

@PersistenceContext(unitName = "<whatever>")
private EntityManager em;

onde você precisa acessar os dados. O servidor de aplicativos manipulará os problemas de encadeamento para você, já que cada bean processará apenas uma única solicitação e essa solicitação estará em um único encadeamento.

    
por Zagrev 19.10.2012 / 17:11
fonte
0

Eu criei o projeto GitHub fornecendo vida útil do EntityManager automanaged no contêiner de servlet (tomcat). O contexto de solicitação http com encadeamento único recupera a mesma instância e o EM é fechado automaticamente no final da solicitação http. Isso fornece uma abstração simples para scripts servlet e jsp sem clichê explícito try-catch-finally.
link

    
por Whome 22.07.2014 / 21:58
fonte