java - son - transaccional spring boot
Ninguna sesión de Hibernate está vinculada al hilo, y la configuración no permite la creación de una no transaccional aquí (8)
Obtengo esta excepción cuando llamo a un método DAO que usa SessionFactory.getCurrentSession()
. La clase DAO está anotada con @Transactional
y también tengo <tx:annotation-driven/>
declarado en el archivo de configuración de contexto de la aplicación.
Puedo llamar a mis métodos DAO que realizan consultas HQL, pero cada vez que llamo a un método DAO que primero obtiene la sesión de Hibernate, entonces me encuentro con esta excepción:
SEVERE: Failed to save the object.
org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
at org.springframework.orm.hibernate3.SpringSessionContext.currentSession(SpringSessionContext.java:63)
at org.hibernate.impl.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:622)
at gov.noaa.ncdc.cmb.persistence.dao.GenericDaoHibernateImpl.getCurrentSession(GenericDaoHibernateImpl.java:56)
at gov.noaa.ncdc.cmb.persistence.dao.GenericDaoHibernateImpl.saveOrUpdate(GenericDaoHibernateImpl.java:187)
Tengo el siguiente archivo de configuración de contexto de la aplicación:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:flex="http://www.springframework.org/schema/flex"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/flex
http://www.springframework.org/schema/flex/spring-flex-1.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<!-- load values used for bean properties -->
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<value>applicationContext.properties</value>
</property>
</bean>
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<!-- DataSource where objects will be persisted -->
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="username" value="${datasource.username}" />
<property name="password" value="${datasource.password}" />
<property name="url" value="${datasource.url}" />
<property name="driverClassName" value="${datasource.driver}" />
</bean>
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<!-- Factory bean for Hibernate Sessions -->
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<bean id="hibernateSessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="annotatedClasses">
<list>
<value>gov.noaa.ncdc.cmb.esrl.domain.entity.EsrlDailyAvg</value>
<value>gov.noaa.ncdc.cmb.esrl.domain.entity.EsrlObservations</value>
<value>gov.noaa.ncdc.cmb.esrl.domain.entity.EsrlStation</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.show_sql">false</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.use_sql_comments">true</prop>
<prop key="hibernate.jdbc.batch_size">50</prop>
<prop key="hibernate.query.substitutions">true 1, false 0</prop>
<prop key="hibernate.max_fetch_depth">6</prop>
<prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddlauto}</prop>
<prop key="hibernate.cache.use_second_level_cache">${hibernate.use_second_level_cache}</prop>
</props>
</property>
</bean>
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<!-- Transaction Manager bean -->
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="hibernateSessionFactory" />
</bean>
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<!-- enable the configuration of transactional behavior based on annotations -->
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<tx:annotation-driven transaction-manager="transactionManager" />
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<!-- DAO for ESRL Station objects -->
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<bean id="esrlStationDao" class="gov.noaa.ncdc.cmb.esrl.domain.dao.EsrlStationDaoHibernateImpl">
<property name="sessionFactory" ref="hibernateSessionFactory" />
<property name="persistentClass" value="gov.noaa.ncdc.cmb.esrl.domain.entity.EsrlStation" />
</bean>
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<!-- DAO for ESRL Observations objects -->
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<bean id="esrlObservationsDao" class="gov.noaa.ncdc.cmb.esrl.domain.dao.EsrlObservationsDaoHibernateImpl">
<property name="sessionFactory" ref="hibernateSessionFactory" />
<property name="persistentClass" value="gov.noaa.ncdc.cmb.esrl.domain.entity.EsrlObservations" />
</bean>
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<!-- DAO for ESRL daily average objects -->
<!-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -->
<bean id="esrlDailyAvgDao" class="gov.noaa.ncdc.cmb.esrl.domain.dao.EsrlDailyAvgDaoHibernateImpl">
<property name="sessionFactory" ref="hibernateSessionFactory" />
<property name="persistentClass" value="gov.noaa.ncdc.cmb.esrl.domain.entity.EsrlDailyAvg" />
</bean>
</beans>
La clase DAO genérica (a partir de la cual se amplía el DAO que se está utilizando en mi programa) se ve así:
package gov.noaa.ncdc.cmb.persistence.dao;
import gov.noaa.ncdc.cmb.persistence.entity.PersistentEntity;
import java.io.Serializable;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Example;
/**
* This class is an implementation of GenericDao<T, PK> using Hibernate.
*/
public class GenericDaoHibernateImpl<T extends PersistentEntity<PK>, PK extends Serializable>
implements GenericDao<T, PK>
{
private SessionFactory sessionFactory;
static private Log log = LogFactory.getLog(GenericDaoHibernateImpl.class);
private Class<T> persistentClass;
/**
* Can be used within subclasses as a convenience method.
*
* @param criterionList the criteria to find by
* @return the list of elements that match the specified criteria
*/
protected List<T> findByCriteria(final List<Criterion> criterionList)
{
Criteria criteria = getCurrentSession().createCriteria(persistentClass);
for (Criterion criterion : criterionList)
{
criteria.add(criterion);
}
return criteria.list();
}
protected String getCanonicalPersistentClassName()
{
return persistentClass.getCanonicalName();
}
/**
* Gets the current Hibernate Session object.
*
* @return
*/
protected Session getCurrentSession()
{
return sessionFactory.getCurrentSession();
}
/*
* This method only provided for interface compatibility. Not recommended for use with large batches
* (this is an inefficient implementation, and it''s somewhat difficult to perform batch operations with Hibernate).
*
* (non-Javadoc)
* @see gov.noaa.ncdc.cmb.persistence.dao.GenericDao#batchInsert(java.util.Collection)
*/
@Override
public int[] batchInsert(final Collection<T> entityCollection)
{
int[] updateCounts = new int[entityCollection.size()];
int i = 0;
for (T entity : entityCollection)
{
try
{
saveOrUpdate(entity);
updateCounts[i] = 1;
i++;
}
catch (Exception ex)
{
clear();
throw new RuntimeException(ex);
}
}
flush();
clear();
return updateCounts;
}
/*
* This method only provided for interface compatibility. Not recommended for use with large batches
* (this is an inefficient implementation, and it''s somewhat difficult to perform batch operations with Hibernate).
*
* (non-Javadoc)
* @see gov.noaa.ncdc.cmb.persistence.dao.GenericDao#batchUpdate(java.util.Collection)
*/
@Override
public int[] batchUpdate(final Collection<T> entityCollection)
{
return batchInsert(entityCollection);
}
/**
* Completely clear the session. Evict all loaded instances and cancel all pending saves, updates and deletions. Do
* not close open iterators or instances of ScrollableResults.
*/
public void clear()
{
getCurrentSession().clear();
}
/*
* (non-Javadoc)
* @see gov.noaa.ncdc.cmb.persistence.dao.GenericDao#delete(gov.noaa.ncdc.cmb.persistence.entity.PersistentEntity)
*/
@Override
public void delete(final T persistentObject)
{
getCurrentSession().delete(persistentObject);
}
/*
* (non-Javadoc)
* @see gov.noaa.ncdc.cmb.persistence.dao.GenericDao#findAll()
*/
@Override
public List<T> findAll()
{
return getCurrentSession().createQuery("from " + persistentClass.getName()).list();
}
/**
* Finds a collection of entity objects which match to the example instance, minus any specified properties which should be excluded from the matching.
*
* @param exampleInstance
* @param excludeProperty
* @return
*/
public List<T> findByExample(final T exampleInstance,
final String[] excludeProperty)
{
Criteria criteria = getCurrentSession().createCriteria(persistentClass);
Example example = Example.create(exampleInstance);
if (excludeProperty != null)
{
for (String exclude : excludeProperty)
{
example.excludeProperty(exclude);
}
}
criteria.add(example);
return criteria.list();
}
/*
* (non-Javadoc)
* @see com.sun.cloud.lifecycle.core.persistence.dao.GenericDao#findById(java.io.Serializable)
*/
@Override
public T findById(final PK id)
{
return (T) getCurrentSession().load(persistentClass, id);
}
/**
* Force this session to flush. Must be called at the end of a unit of work, before commiting the transaction and
* closing the session (depending on flush-mode, Transaction.commit() calls this method).
*
* Flushing is the process of synchronizing the underlying persistent store with persistable state held in memory.
*/
public void flush()
{
getCurrentSession().flush();
}
/*
* (non-Javadoc)
* @see gov.noaa.ncdc.cmb.persistence.dao.GenericDao#saveOrUpdate(gov.noaa.ncdc.cmb.persistence.entity.PersistentEntity)
*/
@Override
public T saveOrUpdate(final T entity)
{
try
{
entity.setUpdatedDate(new Date());
getCurrentSession().saveOrUpdate(entity);
return entity;
}
catch (Exception ex)
{
String errorMessage = "Failed to save the object.";
log.error(errorMessage, ex);
throw new RuntimeException(errorMessage, ex);
}
}
/**
* Setter for the persistentClass property.
*
* @param persistentClass
*/
public void setPersistentClass(final Class<T> persistentClass)
{
this.persistentClass = persistentClass;
}
/**
* Property setter.
*
* @param sessionFactory
*/
public void setSessionFactory(final SessionFactory sessionFactory)
{
this.sessionFactory = sessionFactory;
}
}
Mi aplicación obtiene el DAO desde el contexto de la aplicación:
// load the Spring application context, get the DAOs
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(new String[] { "dailyAveragingApplicationContext.xml" });
esrlDailyAvgDao = (EsrlDailyAvgDao) applicationContext.getBean("esrlDailyAvgDao");
esrlObservationsDao = (EsrlObservationsDao) applicationContext.getBean("esrlObservationsDao");
Y la excepción se encuentra cuando intento guardar una entidad:
esrlDailyAvgDao.saveOrUpdate(esrlDailyAvg);
La clase DAO en sí usa la anotación transaccional:
@Transactional
public class EsrlDailyAvgDaoHibernateImpl
extends GenericDaoHibernateImpl<EsrlDailyAvg, Long>
implements EsrlDailyAvgDao
El seguimiento de pila de excepción se ve así:
SEVERE: Failed to save the object.
org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
at org.springframework.orm.hibernate3.SpringSessionContext.currentSession(SpringSessionContext.java:63)
at org.hibernate.impl.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:622)
at gov.noaa.ncdc.cmb.persistence.dao.GenericDaoHibernateImpl.getCurrentSession(GenericDaoHibernateImpl.java:56)
at gov.noaa.ncdc.cmb.persistence.dao.GenericDaoHibernateImpl.saveOrUpdate(GenericDaoHibernateImpl.java:187)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:309)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:196)
at $Proxy19.saveOrUpdate(Unknown Source)
at gov.noaa.ncdc.cmb.esrl.ingest.EsrlDailyAvgProcessor.main(EsrlDailyAvgProcessor.java:469)
¡Faltan <context:annotation-config />
de su contexto de primavera para que las anotaciones no se escaneen!
¿Tiene org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
configurado en org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
''s web.xml
(suponiendo que su aplicación es una aplicación web), o envolver las llamadas en consecuencia?
Hoy tuve el mismo problema. Mientras busco una solución aquí, cometí el error tonto de no importar
import org.springframework.transaction.annotation.Transactional;
sin saberlo, he importado
import javax.transaction.Transactional;
Después de cambiarlo todo funcionó bien.
Así que pensé en compartir, si alguien hace el mismo error.
Puede tener el @Transactional en la clase secundaria, pero debe anular cada uno de los métodos y llamar al método súper para que funcione.
Ejemplo:
@Transactional(readOnly = true)
public class Bob<SomeClass> {
@Override
public SomeClass getValue() {
return super.getValue();
}
}
Esto le permite configurarlo para cada uno de los métodos necesarios.
Siempre que se enfrente al siguiente error simplemente sígalo.
org.hibernate.HibernateException: ninguna sesión de Hibernate vinculada a thread, y la configuración no permite la creación de una no transaccional aquí en org.springframework.orm.hibernate3.SpringSessionContext.currentSession (SpringSessionContext.java:63)
Coloque una anotación @Transactional
para cada método de implementación de clases.
Tuve el siguiente error:
org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
at org.springframework.orm.hibernate3.SpringSessionContext.currentSession(SpringSessionContext.java:63)
Lo arreglé cambiando mi archivo de propiedades de hibernación
hibernate.current_session_context_class=thread
Mi código y archivo de configuración de la siguiente manera
session = getHibernateTemplate().getSessionFactory().getCurrentSession();
session.beginTransaction();
session.createQuery(Qry).executeUpdate();
session.getTransaction().commit();
en el archivo de propiedades
hibernate.dialect=org.hibernate.dialect.MySQLDialect
hibernate.show_sql=true
hibernate.query_factory_class=org.hibernate.hql.ast.ASTQueryTranslatorFactory
hibernate.current_session_context_class=thread
en el archivo de cofiguración
<properties>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
<prop key="hibernate.query.factory_class">${hibernate.query_factory_class}</prop>
<prop key="hibernate.generate_statistics">true</prop>
<prop key="hibernate.current_session_context_class">${hibernate.current_session_context_class}</prop>
</props>
</property>
</properties>
Gracias,
Ashok
después de agregar la propiedad:
<prop key="hibernate.current_session_context_class">thread</prop>
Obtengo la excepción como:
org.hibernate.HibernateException: createQuery is not valid without active transaction
org.hibernate.HibernateException: save is not valid without active transaction.
entonces creo que establecer esa propiedad no es una buena solución.
finalmente resuelvo el problema "Ninguna sesión de Hibernate vinculada al hilo":
1. <!-- <prop key="hibernate.current_session_context_class">thread</prop> -->
2.add <tx:annotation-driven />
a servlet-context.xml o dispatcher-servlet.xml
3.add @Transactional después de @Service y @Repository
@Transactional
esto agregando @Transactional
a la clase de implementación HIO de Hibernate / base (la clase padre que implementa el método saveOrUpdate () heredado por el DAO que uso en el programa principal), es decir, el @Transactional
necesita ser especificado en el real clase que implementa el método. Mi suposición era, en cambio, que si declaraba @Transactional
en la clase hija, entonces incluía todos los métodos heredados por la clase hija. Sin embargo, parece que la anotación @Transactional
solo se aplica a los métodos implementados dentro de una clase y no a los métodos heredados por una clase.