Concorrência de Leitura no Hibernate

Estou desenvolvendo uma aplicação Java + JSF(Primefaces) + Hibernate, sendo que em determinado momento quando faço um acesso simultâneo com outro usuário ao mesmo registro ocorre um erro de [could not inspect JDBC autocommit mode]. Este erro já identifique que é devido ao tratamento no meu DAO, onde ao concluir a leitura do registro o primeiro processo fecha a sessão, sendo que o outro processo ainda está em execução. Alguém poderia me ajudar nesta solução? Como tratar esta concorrência de leitura no hibernate?

Não trabalhe com autocommit direto e implemente o FilterOpenSessionInView onde o commit e roolback são dados corretamente em um só ponto

<property name="hibernate.connection.autocommit">false</property>

`
@WebFilter(filterName = “conexaoFilter”)
public class FilterOpenSessionInView extends DelegatingFilterProxy implements
Serializable {

private static final long serialVersionUID = 1L;
private static SessionFactory sf;

@Override
public void initFilterBean() throws ServletException {
	sf = HibernateUtil.getSessionFactory();
}

@Override
public void doFilter(ServletRequest servletRequest,
		ServletResponse servletResponse, FilterChain chain)
		throws IOException, ServletException {
	
	BasicDataSource springDataSource = (BasicDataSource) ContextLoaderListenerCaixakiUtils.getBean("springDataSource");
	DefaultTransactionDefinition def = new DefaultTransactionDefinition();
	PlatformTransactionManager transactionManager = new DataSourceTransactionManager(springDataSource);
	TransactionStatus status = transactionManager.getTransaction(def);
	
	try {

		servletRequest.setCharacterEncoding("UTF-8");

		HttpServletRequest request = (HttpServletRequest) servletRequest;
		HttpServletResponse response = (HttpServletResponse) servletResponse;
		HttpSession sessao = request.getSession();
		Entidade userLogadoSessao = (Entidade) sessao.getAttribute("userLogadoSessao");

		if (userLogadoSessao != null) {
			UtilFramework.getThreadLocal().set(userLogadoSessao.getEnt_codigo());
		}

		sf.getCurrentSession().beginTransaction();
		chain.doFilter(servletRequest, servletResponse);
		sf.getCurrentSession().getTransaction().commit();
		transactionManager.commit(status);
		
		servletResponse.setCharacterEncoding("UTF-8");
		servletResponse.setContentType("text/html; charset=UTF-8");

	} catch (Exception e) {
		
		transactionManager.rollback(status);
		
		e.printStackTrace();

		if (sf.getCurrentSession().getTransaction().isActive()) {
			sf.getCurrentSession().getTransaction().rollback();
		}

	} finally {

		if (sf.getCurrentSession().isOpen()) {
			if (sf.getCurrentSession().beginTransaction().isActive()) {
				sf.getCurrentSession().clear();
			}
			sf.getCurrentSession().close();
		}
	}
}

}

`

Oi, Alexfe! Eu já não estava utilizando o autocommit e também não estou utilizando o Spring. Para confirmar, veja se esta abordagem que me passou realmente resolveria o problema de concorrência na leitura dos dados. Vou te passar mais informações sobre o problema:

Segue minha configuração do Hibernate.cfg.xml

<?xml version="1.0" encoding="UTF-8"?>
    <!-- Conexão BD -->
    <property name="hibernate.connection.driver_class">org.postgresql.Driver</property>
    <property name="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</property>        
    <property name="hibernate.connection.url">jdbc:postgresql://localhost:5432/dosiero_dsv</property>
    <property name="hibernate.connection.username">xxxxx</property>
    <property name="hibernate.connection.password">xxx</property>

    <!-- Pool Conexoes -->
     <property name="hibernate.hbm2ddl.auto">none</property>
    <property name="hibernate.connection.autocommit">false</property>
    <property name="hibernate.format_sql">false</property> 
    <property name="hibernate.show_sql">false</property>
    <property name="current_session_context_class">thread</property>
    <property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>
    <property name="hibernate.c3p0.acquire_increment">5</property>
    <property name="hibernate.c3p0.min_size">5</property>
    <property name="hibernate.c3p0.max_size">20</property>
    <property name="hibernate.c3p0.max_statements">50</property>   
    <property name="hibernate.c3p0.timeout">1800</property>
      <property name="hibernate.c3p0.idle_test_period">3000</property>

    <mapping class="_model.vo.Usuario" />
    <mapping class="_model.vo.Cliente" />
    <mapping class="_model.vo.Unidade" />
    <mapping class="_model.vo.Classe" />
    <mapping class="_model.vo.Descritor" />
    <mapping class="_model.vo.Modulo" />
    <mapping class="_model.vo.Perfil" />
    <mapping class="_model.vo.Pagina" />
    <mapping class="_model.vo.ClienteModulo" />
    <mapping class="_model.vo.Acervo" />
    <mapping class="_model.vo.AcervoDescritor" />
    <mapping class="_model.vo.PerfilPagina" />
    <mapping class="_model.vo.Documento" />
</session-factory>

Segue meu Web.xml

<?xml version="1.0" encoding="UTF-8"?> com.sun.faces.config.ConfigureListener primefaces.SUBMIT partial javax.faces.FACELETS_SKIP_COMMENTS true javax.faces.DEFAULT_SUFFIX .xhtml javax.faces.PARTIAL_STATE_SAVING true primefaces.THEME afterdark javax.faces.CONFIG_FILES /WEB-INF/faces-config.xml, /WEB-INF/faces-navigation.xml, /WEB-INF/faces-managed-beans.xml com.sun.faces.expressionFactory com.sun.el.ExpressionFactoryImpl primefaces.UPLOADER commons Resource Servlet org.primefaces.resource.ResourceServlet Faces Servlet javax.faces.webapp.FacesServlet 1 Faces Servlet *.jsf *.xhtml *.faces PrimeFaces FileUpload Filter org.primefaces.webapp.filter.FileUploadFilter thresholdSize 51200 PrimeFaces FileUpload Filter Faces Servlet 30 index.xhtml jpg image/jpeg

Segue a rotina de edição, que busca outras informações do objeto (Acervo) que tem uma lista de objetos (Documentos e Descritores) dentro dele. O problema ocorre exatamente quando outro usuário acessa esta mesma rotina simultaneamente e ambas tentam recuperar as informações dos subobjetos (Documentos e Descritores) abaixo em destaque:

public String editar() throws Exception
{
    acervo = acervoSel;
    if (acervo.getObjClasse().getClas_nr_fasecorrente()!=null && acervo.getObjClasse().getClas_nr_fasecorrente() > 0) {
        acervo.getObjClasse().setClas_ds_fasecorrente(acervo.getObjClasse().getClas_nr_fasecorrente().toString() + " " + acervo.getObjClasse().getClas_tx_fasecorrente() + "(s)");
    } else {
        acervo.getObjClasse().setClas_ds_fasecorrente(acervo.getObjClasse().getClas_tx_fasecorrente());
    }
    
    // --- Carrega subclasses do Acervo - Documentos e Descritores
    acervoManager = new AcervoManager();


   // ----------------------------------------------------------------------------------------------------------------
      ROTINA QUE INICIA A BUSCA AOS DADOS E GERA O PROBLEMA
   // ----------------------------------------------------------------------------------------------------------------
    acervo = acervoManager.carregaSubClassesDoAcervo(acervo);
   // ----------------------------------------------------------------------------------------------------------------

    
    // --- Identifica os descritores selecionados na lista de descritores.
    seldescritors = new ArrayList<Descritor>();
    if(acervo.getAcervoDescritors()!=null && acervo.getAcervoDescritors().size()>0) {
        for (AcervoDescritor lista : acervo.getAcervoDescritors()) {
            Descritor obj = new Descritor();
            obj = lista.getObjDescritor();
            seldescritors.add(obj);
        }
    }
    verificaAutoMovimentacao();
    return "/pages/acervo/cadacervodetalhe.xhtml";
}

Segue rotina destacada:

public Acervo carregaSubClassesDoAcervo(Acervo filtro) throws ControllerException {
    HibernateUtil.getSession();
    
    Acervo objAcervo = new Acervo();
    objAcervo=filtro;

    // --- Carrega Documento relacionados ao acervo
    
    List<Documento> documentos = new ArrayList<Documento>();
    List<AcervoDescritor> acervoDescritors = new ArrayList<AcervoDescritor>();

    try
    {
        // --- ROTINA PARA BUSCAR TODOS DOCUMENTOS RELACIONADOS AO ACERVO
        documentos = documentoDAO.allDocumentosAcervo(filtro);

        // --- ROTINA PARA BUSCAR TODOS OS DESCRITORES RELACIONADOS AO ACERVO
        acervoDescritors = acervoDescritorDAO.pesquisaAcervoDescritorPorAcervo(filtro);
        // --- ATRIBUINDO AS SUBCLASSES AO ACERVO
        objAcervo.setDocumentos(documentos);
        objAcervo.setAcervoDescritors(acervoDescritors);
    }
    catch (DaoException e) 
    {
        throw new ControllerException(e);
    } 
    finally
    {
         HibernateUtil.closeSession();
    }
    
    return objAcervo;
            
}

Agora os DAOS respectivos:

Dao para buscar os Documentos:

public List<Documento> allDocumentosAcervo(Acervo filtro) throws DaoException {
    List<Documento> paginas = new ArrayList<Documento>();

    try {
        StringBuffer sql = new StringBuffer("");
        sql.append("select res1 from Documento as res1 ");
        sql.append("where objAcervo.id = :codfiltro ");
        Query query = HibernateUtil.getSession()
                .createQuery(sql.toString());
        query.setParameter("codfiltro", filtro.getId());
        paginas = findMany(query);
    } catch (Exception e) {
        throw new DaoException(e);
    }

    return paginas;
}

Dao para buscar os Descritores:

public List<AcervoDescritor> pesquisaAcervoDescritorPorAcervo(Acervo filtro) throws DaoException {
    List<AcervoDescritor> filtrodescritors = new ArrayList<AcervoDescritor>();

    try {

        StringBuffer sql = new StringBuffer("");
        sql.append("select res1 from AcervoDescritor res1 ");
        sql.append("where res1.objAcervo.id = :codfiltro ");
        Query query = HibernateUtil.getSession()
                .createQuery(sql.toString());
        query.setParameter("codfiltro", filtro.getId());
        filtrodescritors = findMany(query);

    } catch (Exception e) {
        throw new DaoException(e);
    }

    return filtrodescritors;
}

Quando o primeiro processo sai do DAO a sessão e fechada e ocorre o erro, já que o outro processo ainda esta em andamento.

Apr 13, 2016 5:43:20 PM com.sun.faces.lifecycle.InvokeApplicationPhase execute
WARNING: #{acervoView.editar()}: exception.ControllerException: exception.DaoException: org.hibernate.exception.GenericJDBCException: could not inspect JDBC autocommit mode
javax.faces.FacesException: #{acervoView.editar()}: exception.ControllerException: exception.DaoException: org.hibernate.exception.GenericJDBCException: could not inspect JDBC autocommit mode
at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:118)
at org.primefaces.application.DialogActionListener.processAction(DialogActionListener.java:45)
at javax.faces.component.UICommand.broadcast(UICommand.java:315)
at javax.faces.component.UIData.broadcast(UIData.java:1108)
at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:790)
at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:1282)

Muito obrigado pela ajuda!

Olhando logo de cara estou achando estranho o seu getSession, vc tem que trabalhar com transações

HibernateUtil.getCurrentSession().beginTransaction(); /*processa tudo que tem pra fazer */ HibernateUtil..getCurrentSession().getTransaction().commit();

Como eu estava apenas lendo um registro achei que não fosse necessário colocar em transações. Fiz a alteração, mas o erro continua. Segue minha classe HibernateUtil.java

public class HibernateUtil {
private static HibernateUtil instance = null;
private static SessionFactory sessionFactory;
private static ServiceRegistry serviceRegistry;
private static Session hibernateSession = null;
private static final ThreadLocal localSession = new ThreadLocal();
private static final ThreadLocal localTx = new ThreadLocal();

private HibernateUtil() throws DaoException {
    try {

        Configuration configuration = new Configuration();
        configuration.configure();
        serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
        sessionFactory = configuration.buildSessionFactory(serviceRegistry);
        
    } catch (Exception e) {
        throw new DaoException(e);
    }

}

public static HibernateUtil getInstance() throws DaoException {

    if (instance == null) {
        instance = new HibernateUtil();
    }
    return instance;
}

public static SessionFactory getSessionFactory() {
    return sessionFactory;
}

public static void beginTransaction() {
    //--hibernateSession.beginTransaction();
    localTx.set(getSession().beginTransaction());
}

public static void commitTransaction() {
    //--hibernateSession.getTransaction().commit();
    if (localTx.get() != null)
        localTx.get().commit();

}

public static void rollbackTransaction() {
    // --hibernateSession.getTransaction().rollback();
    if (localTx.get() != null)
       localTx.get().rollback();

}

public static void closeSession() {
    if(hibernateSession.isOpen()) {
        hibernateSession.close();
    }
}

public static Session getSession() {
    try
    {
        if (hibernateSession == null || !hibernateSession.isOpen()) {
            hibernateSession = getSessionFactory().openSession();
            localSession.set(hibernateSession);
        } 
    } catch (Exception e) {
        System.out.println(e);
        throw new RuntimeException(e);
    }
    
    return hibernateSession;
}

}

Segue o método com a implementação das transações:

public Acervo carregaSubClassesDoAcervo(Acervo filtro) throws ControllerException {
    HibernateUtil.getSession();
    
    Acervo objAcervo = new Acervo();
    objAcervo=filtro;

    // --- Carrega Documento relacionados ao acervo
    
    List<Documento> documentos = new ArrayList<Documento>();
    List<AcervoDescritor> acervoDescritors = new ArrayList<AcervoDescritor>();

    try
    {
        HibernateUtil.beginTransaction();
        /*processa tudo que tem pra fazer */
        // --- ROTINA PARA BUSCAR TODOS DOCUMENTOS RELACIONADOS AO ACERVO
        documentos = documentoDAO.allDocumentosAcervo(filtro);
        // --- ROTINA PARA BUSCAR TODOS OS DESCRITORES RELACIONADOS AO ACERVO
        acervoDescritors = acervoDescritorDAO.pesquisaAcervoDescritorPorAcervo(filtro);
        HibernateUtil.commitTransaction();
        // --- ATRIBUINDO AS SUBCLASSES AO ACERVO
        objAcervo.setDocumentos(documentos);
        objAcervo.setAcervoDescritors(acervoDescritors);
    }
    catch (DaoException e) 
    {
        throw new ControllerException(e);
    } 
    finally
    {
         HibernateUtil.closeSession();
    }
    
    return objAcervo;

Veja o erro que está gerando agora. Neste caso os 2(dois) computadores que acessaram o mesmo registro retornaram a tela de erro. Note nas linhas abaixo que ele dispara os dois processos que são interrompidos no passo 2, exatamente quando ele está dentro das transações.

INFO: Server startup in 6442 ms
Ocorreu outro tipo de exceção: exception.DaoException: org.hibernate.SessionException: Session is closed!
Ocorreu outro tipo de exceção: exception.DaoException: org.hibernate.exception.GenericJDBCException: could not extract ResultSet
passo 1
passo 1.2
passo 2
passo 1
passo 1.2
passo 2
Apr 14, 2016 2:58:07 PM com.sun.faces.lifecycle.InvokeApplicationPhase execute
WARNING: #{acervoView.editar()}: org.hibernate.TransactionException: nested transactions not supported
javax.faces.FacesException: #{acervoView.editar()}: org.hibernate.TransactionException: nested transactions not supported
at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:118)
at org.primefaces.application.DialogActionListener.processAction(DialogActionListener.java:45)

pode remover esses ThreadLocal isso não da certo

Abaixo está o Hibernate utitl quer eu uso em todos os projetos

`public class HibernateUtil implements Serializable {

private static SessionFactory sessionFactory = buildSessionFactory();

private static SessionFactory buildSessionFactory() {
    try {
        if (sessionFactory == null ) {
            sessionFactory = (new Configuration()).configure()
                    .buildSessionFactory();
        }
        return sessionFactory;

    } catch (Exception e) {
        e.printStackTrace();
        throw new ExceptionInInitializerError(
                "Erro ao criar conexгo SessionFactory");
    }
}

public static SessionFactory getSessionFactory() {
    return sessionFactory;
}

public static Session getCurrentSession() {
    return getSessionFactory().getCurrentSession();
}

public static Session openSession() {
    if (sessionFactory == null)
        buildSessionFactory();
    return sessionFactory.openSession();
}

}`

Oi Alexfe! A coisa aqui está desigual. Java 10 x 1. De qualquer forma quero te agradecer a ajuda! Fiz uma alteração no meu HibernateUtil e acho que o problema foi superado.

Segue a classe modificada:

public class HibernateUtil {
private static HibernateUtil instance = null;
private static SessionFactory sessionFactory;
private static ServiceRegistry serviceRegistry;
private static Session hibernateSession = null;
private static final ThreadLocal session = new ThreadLocal();

private HibernateUtil() throws DaoException {
    try {

        Configuration configuration = new Configuration();
        configuration.configure();
        serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
        sessionFactory = configuration.buildSessionFactory(serviceRegistry);
    } catch (Exception e) {
        throw new DaoException(e);
    }

}

public static HibernateUtil getInstance() throws DaoException {

    if (instance == null) {
        instance = new HibernateUtil();
    }
    return instance;
}

public static SessionFactory getSessionFactory() {
    return sessionFactory;
}

public static void beginTransaction() {
    hibernateSession.beginTransaction();
}

public static void commitTransaction() {
    hibernateSession.getTransaction().commit();
}

public static void rollbackTransaction() {
    hibernateSession.getTransaction().rollback();
}

public static void closeSession() {
    hibernateSession = (Session) session.get();
    if(hibernateSession.isOpen()) {
        hibernateSession.close();
    }
    session.set(hibernateSession);
}

public static Session getSession() {
    hibernateSession = (Session) session.get();
    try
    {
        if (hibernateSession == null || !hibernateSession.isOpen()) {
            hibernateSession = getSessionFactory().openSession();
        } 
    } catch (Exception e) {
        System.out.println(e);
        throw new RuntimeException(e);
    }
    session.set(hibernateSession);
    
    return hibernateSession;
}

}