Como corrigir org.hibernate.LazyInitializationException - não foi possível inicializar o proxy - nenhuma sessão


188

Eu recebo a seguinte exceção:

Exception in thread "main" org.hibernate.LazyInitializationException: could not initialize proxy - no Session
    at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:167)
    at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:215)
    at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:190)
    at sei.persistence.wf.entities.Element_$$_jvstc68_47.getNote(Element_$$_jvstc68_47.java)
    at JSON_to_XML.createBpmnRepresantation(JSON_to_XML.java:139)
    at JSON_to_XML.main(JSON_to_XML.java:84)

quando tento ligar das principais linhas a seguir:

Model subProcessModel = getModelByModelGroup(1112);
System.out.println(subProcessModel.getElement().getNote());

Eu implementei o getModelByModelGroup(int modelgroupid)método primeiro assim:

public static Model getModelByModelGroup(int modelGroupId, boolean openTransaction) {

    Session session = SessionFactoryHelper.getSessionFactory().getCurrentSession();     
    Transaction tx = null;

    if (openTransaction) {
        tx = session.getTransaction();
    }

    String responseMessage = "";

    try {
        if (openTransaction) {
            tx.begin();
        }
        Query query = session.createQuery("from Model where modelGroup.id = :modelGroupId");
        query.setParameter("modelGroupId", modelGroupId);

        List<Model> modelList = (List<Model>)query.list(); 
        Model model = null;

        for (Model m : modelList) {
            if (m.getModelType().getId() == 3) {
                model = m;
                break;
            }
        }

        if (model == null) {
            Object[] arrModels = modelList.toArray();
            if (arrModels.length == 0) {
                throw new Exception("Non esiste ");
            }

            model = (Model)arrModels[0];
        }

        if (openTransaction) {
            tx.commit();
        }

        return model;

   } catch(Exception ex) {
       if (openTransaction) {
           tx.rollback();
       }
       ex.printStackTrace();
       if (responseMessage.compareTo("") == 0) {
           responseMessage = "Error" + ex.getMessage();
       }
       return null;
    }
}

e conseguiu a exceção. Em seguida, um amigo me sugeriu que sempre testasse a sessão e obtenha a sessão atual para evitar esse erro. Então eu fiz isso:

public static Model getModelByModelGroup(int modelGroupId) {
    Session session = null;
    boolean openSession = session == null;
    Transaction tx = null;
    if (openSession) {
        session = SessionFactoryHelper.getSessionFactory().getCurrentSession(); 
        tx = session.getTransaction();
    }
    String responseMessage = "";

    try {
        if (openSession) {
            tx.begin();
        }
        Query query = session.createQuery("from Model where modelGroup.id = :modelGroupId");
        query.setParameter("modelGroupId", modelGroupId);

        List<Model> modelList = (List<Model>)query.list(); 
        Model model = null;

        for (Model m : modelList) {
            if (m.getModelType().getId() == 3) {
                model = m;
                break;
            }
        }

        if (model == null) {
            Object[] arrModels = modelList.toArray();
            if (arrModels.length == 0) {
                throw new RuntimeException("Non esiste");
            }

            model = (Model)arrModels[0];

            if (openSession) {
                tx.commit();
            }
            return model;
        } catch(RuntimeException ex) {
            if (openSession) {
                tx.rollback();
            }
            ex.printStackTrace();
            if (responseMessage.compareTo("") == 0) {
                responseMessage = "Error" + ex.getMessage();
            }
            return null;        
        }
    }
}

mas ainda assim, obtenha o mesmo erro. Tenho lido muito sobre esse erro e encontrei algumas soluções possíveis. Uma delas era definir lazyLoad como false, mas não tenho permissão para fazer isso, por isso me sugeriram controlar a sessão

Respostas:


93

O que há de errado aqui é que sua configuração de gerenciamento de sessões está definida para fechar a sessão quando você confirma a transação. Verifique se você tem algo como:

<property name="current_session_context_class">thread</property>

na sua configuração.

Para superar esse problema, você pode alterar a configuração do factory de sessão ou abrir outra sessão e apenas pedir esses objetos carregados com preguiça. Mas o que eu sugeriria aqui é inicializar essa coleção lenta no próprio getModelByModelGroup e chamar:

Hibernate.initialize(subProcessModel.getElement());

quando você ainda está na sessão ativa.

E uma última coisa. Um conselho amigável. Você tem algo parecido com isto em seu método:

for (Model m : modelList) {
    if (m.getModelType().getId() == 3) {
        model = m;
        break;
    }
}

Por favor, insted este código apenas filtre os modelos com id de tipo igual a 3 na instrução de consulta apenas algumas linhas acima.

Um pouco mais de leitura:

configuração de fábrica de sessões

problema com a sessão fechada


1
Obrigado! Eu resolvi meu problema usando openSession () em vez de getCurrentSession () como um dos links que você deu me sugeriu isso, mas agora eu tenho medo, se é errado fazê-lo
Blerta Dhimitri

2
Não, provavelmente está bom. Mas leia um pouco mais para poder controlar totalmente suas sessões e transações. É muito importante conhecer o básico, porque todas as tecnologias de nível superior, como Spring, Hibernate e mais, operam com o mesmo conceito.
Goroncy

179

Se você usar o Spring, marcar a classe como @Transactional , o Spring tratará do gerenciamento de sessões.

@Transactional
public class MyClass {
    ...
}

Usando @Transactional , muitos aspectos importantes, como a propagação de transações, são tratados automaticamente. Nesse caso, se outro método transacional for chamado, o método terá a opção de ingressar na transação em andamento, evitando a exceção "no session".

AVISO Se você usar @Transactional, esteja ciente do comportamento resultante. Consulte este artigo para obter armadilhas comuns. Por exemplo, as atualizações para entidades são mantidas, mesmo se você não chamar explicitamentesave


21
Não posso exagerar a importância dessa resposta. Eu seriamente recomendo tentar esta opção primeiro.
Sparkyspider

6
Observe também que você precisa adicionar @EnableTransactionManagementà sua configuração para permitir transações. " se outro método transacional for chamado, o método terá a opção de ingressar na transação em andamento " esse comportamento é diferente para as diferentes formas de implementação das transações, ou seja, proxy de interface versus proxy de classe ou tecelagem AspectJ. Consulte a documentação .
Erik Hofer

1
Deveríamos entender que a Transactionalanotação do Spring é recomendada, não apenas para modificar transações, mas também para acessar apenas as transações?
Stephane

8
Eu seriamente recomendo usar esta anotação na parte superior da classe apenas para teste. O código real deve marcar cada método como transação na classe separadamente. A menos que todos os métodos da classe exijam conexão aberta com a transação com o banco de dados.
M1ld 6/12

1
Não é seguro colocar @Transactional (readOnly = true) em vez de apenas @Transactional?
Hamedz 02/06/19

105

Você pode tentar definir

<property name="hibernate.enable_lazy_load_no_trans">true</property>

em hibernate.cfg.xml ou persistence.xml

O problema a ter em mente com esta propriedade está bem explicado aqui


8
Você também pode explicar seu significado?
Mohit Kanwar

2
Também estou curioso para saber o que isso faz. Corrigiu o problema que eu estava tendo, mas gostaria de entender o porquê.
Hassan

3
para persistence.xml:<property name="hibernate.enable_lazy_load_no_trans" value="true"/>
ACV


6
NÃO USE ESTA PROPRIEDADE, SE A SPRING GERIR AS SUAS OPERAÇÕES, ESSA PROPRIEDADE LEVARÁ À EXPLOSÃO DAS OPERAÇÕES, SIMPLESMENTE A PRIMAVERA DESLIGARÁ O APLICATIVO
Youans

54

A melhor maneira de lidar com issoLazyInitializationException é usar a JOIN FETCHdiretiva:

Query query = session.createQuery(
    "from Model m " +
    "join fetch m.modelType " +
    "where modelGroup.id = :modelGroupId"
);

De qualquer forma, NÃO use os seguintes antipadrões, conforme sugerido por algumas das respostas:

Às vezes, uma projeção de DTO é uma escolha melhor do que buscar entidades e, dessa forma, você não receberá nenhuma LazyInitializationException.


Como posso identificar qual chamada está tendo problemas? Estou achando difícil identificar a ligação. Existe alguma maneira ? Para fins de teste que usei FetchType=EAGER, mas essa não é a solução correta, certo?
Shantaram Tupe

Basta usar o log. E EAGER é ruim, sim.
Vlad Mihalcea

Em seguida, você deve usar DTOs ou inicializar todas as associações antes de sair do @Transactionalserviço.
Vlad Mihalcea

2
Devemos defender as melhores práticas empresariais, mas não a solução rápida.
etlds 22/02

21

Eu estava recebendo o mesmo erro de um para muitos relacionamentos na anotação abaixo.

@OneToMany(mappedBy="department", cascade = CascadeType.ALL)

Alterado como abaixo, após adicionar fetch = FetchType.EAGER, funcionou para mim.

@OneToMany(mappedBy="department", cascade = CascadeType.ALL, fetch=FetchType.EAGER)

26
Sim, pode corrigi-lo, mas agora você está carregando toda a árvore de dados. Isto terá impactos desempenho negativo na maioria dos casos
astro8891


9

Esta exceção, devido a quando você ligar session.getEntityById(), a sessão será fechada. Portanto, você precisa anexar novamente a entidade à sessão. A solução Or Easy é apenas configurar default-lazy="false" para o seu entity.hbm.xmlou, se você estiver usando anotações, adicionar @Proxy(lazy=false)à sua classe de entidade.


5

Eu encontrei o mesmo problema. Acho que outra maneira de corrigir isso é que você pode alterar a consulta para ingressar buscar seu elemento do modelo da seguinte maneira:

Query query = session.createQuery("from Model m join fetch m.element where modelGroup.id = :modelGroupId")

4

Isso significa que o objeto que você está tentando acessar não está carregado, portanto, escreva uma consulta que faça uma busca por junção do objeto que você está tentando acessar.

Por exemplo:

Se você estiver tentando obter o ObjectB do ObjectA, em que o ObjectB é uma chave estrangeira no ObjectA.

Inquerir :

SELECT objA FROM ObjectA obj JOIN FETCH obj.objectB objB

3

Existem várias boas respostas aqui que tratam desse erro em um escopo amplo. Encontrei uma situação específica com o Spring Security que tinha uma solução rápida, embora provavelmente não ótima.

Durante a autorização do usuário (imediatamente após o login e a autenticação), eu estava testando uma entidade do usuário para uma autoridade específica em uma classe personalizada que estende o SimpleUrlAuthenticationSuccessHandler.

Minha entidade de usuário implementa UserDetails e possui um conjunto de funções carregadas preguiçosamente que lançou a exceção "org.hibernate.LazyInitializationException - não pôde inicializar o proxy - sem sessão". Alterar esse conjunto de "fetch = FetchType.LAZY" para "fetch = FetchType.EAGER" corrigiu isso para mim.



2

Enfrentou a mesma exceção em diferentes casos de uso.

insira a descrição da imagem aqui

Caso de uso: tente ler dados do banco de dados com projeção DTO.

Solução: use o método get em vez de carregar .

Operação genérica

public class HibernateTemplate {
public static Object loadObject(Class<?> cls, Serializable s) {
    Object o = null;
    Transaction tx = null;
    try {
        Session session = HibernateUtil.getSessionFactory().openSession();
        tx = session.beginTransaction();
        o = session.load(cls, s); /*change load to get*/
        tx.commit();
        session.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return o;
}

}

Classe de persistência

public class Customer {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "Id")
private int customerId;

@Column(name = "Name")
private String customerName;

@Column(name = "City")
private String city;

//constructors , setters and getters

}

Interface CustomerDAO

public interface CustomerDAO 
     {
   public CustomerTO getCustomerById(int cid);
     }

Classe de Objeto de Transferência de Entidade

public class CustomerTO {

private int customerId;

private String customerName;

private String city;

//constructors , setters and getters

}

Classe de fábrica

public class DAOFactory {

static CustomerDAO customerDAO;
static {
    customerDAO = new HibernateCustomerDAO();
}

public static CustomerDAO getCustomerDAO() {
    return customerDAO;
}

}

DAO específico da entidade

public class HibernateCustomerDAO implements CustomerDAO {

@Override
public CustomerTO getCustomerById(int cid) {
    Customer cust = (Customer) HibernateTemplate.loadObject(Customer.class, cid);
    CustomerTO cto = new CustomerTO(cust.getCustomerId(), cust.getCustomerName(), cust.getCity());
    return cto;
}

}

Recuperando Dados: Classe de Teste

CustomerDAO cdao = DAOFactory.getCustomerDAO();
CustomerTO c1 = cdao.getCustomerById(2);
System.out.println("CustomerName -> " + c1.getCustomerName() + " ,CustomerCity -> " + c1.getCity());

Dados atuais

insira a descrição da imagem aqui

Consulta e Saída Gerada pelo Sistema Hibernate

Hibernação: selecione customer0_.Id como Id1_0_0_, customer0_.City como cidade2_0_0_, customer0_.Name como Name3_0_0_ do CustomerLab31 customer0_ onde customer0_.Id =?

CustomerName -> Cody, CustomerCity -> LA


1

Se você estiver usando o Grail'sFramework, é simples resolver uma exceção de inicialização lenta usando a Lazypalavra-chave em um campo específico na Classe de Domínio.

Por exemplo:

class Book {
    static belongsTo = [author: Author]
    static mapping = {
        author lazy: false
    }
}

Encontre mais informações aqui


1

No meu caso, um extraviado session.clear()estava causando esse problema.


1

Isso significa que você está usando JPA ou hibernar em seu código e executando operações de modificação no DB sem fazer a transação da lógica de negócios. A solução mais simples para isso é marcar seu código @Transactional



-2

você também pode resolvê-lo adicionando lazy = false ao seu arquivo * .hbm.xml ou pode iniciar seu objeto no Hibernate.init (Object) ao obter o objeto do db


10
geralmente adicionar lazy = false não é uma boa ideia. por isso preguiçoso é verdadeiro por padrão
MoienGK

O OP disse claramente de antemão que ele não tem permissão para fazer isso.
GingerHead #

-2

Faça as seguintes alterações no servlet-context.xml

    <beans:property name="hibernateProperties">
        <beans:props>

            <beans:prop key="hibernate.enable_lazy_load_no_trans">true</beans:prop>

        </beans:props>
    </beans:property>
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.