java - management - La forma correcta de autoaumentar una sesión de Hibernate en una prueba de primavera JUnit Transaction
spring transaction propagation (2)
Esta pregunta es similar a la anterior. Estoy tratando de @Autowire
a Hibernate Session en una de mis pruebas Spring-JUnit-Transactional pero @Autowire
esta excepción:
java.lang.IllegalStateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional ...
Aquí está mi clase JUnit:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"/applicationContext.xml"})
@TransactionConfiguration(transactionManager="transactionManager")
@Transactional
public class MyTest {
@Qualifier("session")
@Autowired
private Session session;
@Test
public void testSomething() {
session.get(User.class, "[email protected]");
}
}
Todo funciona bien si yo @Autowire
una SessionFactory
y obtengo mi Session
programáticamente (en lugar de definirla en Spring XML) así:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"/applicationContext.xml"})
@TransactionConfiguration(transactionManager="transactionManager")
@Transactional
public class MyTest{
@Qualifier("sessionFactory")
@Autowired
private SessionFactory sessionFactory;
@Test
public void testSomething() {
Session session = SessionFactoryUtils.getSession(sessionFactory, false);
session.get(User.class, "[email protected]");
}
}
Sin embargo, puedo hacer que mi ejemplo original funcione si defino mi Session
en mi Spring XML con <aop:scoped-proxy />
como sigue:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
">
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
...
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation"><value>classpath:/hibernate.cfg.xml</value></property>
<property name="configurationClass">
<value>org.hibernate.cfg.AnnotationConfiguration</value>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
<property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="session" class="org.springframework.orm.hibernate3.SessionFactoryUtils" factory-method="getSession" scope="prototype">
<constructor-arg ref="sessionFactory" />
<constructor-arg value="false" />
<!-- This is seems to be needed to get rid of the ''No Hibernate Session'' error'' -->
<aop:scoped-proxy />
</bean>
</beans>
Mi pregunta es: ¿Por qué es necesario <aop:scoped-proxy />
dado que solo debería haber un contexto de transacción limitado por hilos en mi prueba unitaria? ¿Cuál es la forma correcta de definir mi bean Hibernate Session
?
Creo que la forma "correcta" es la inyección de SessionFactory
y la recuperación de la sesión mediante programación. El motivo por el que recibe la excepción es por el comportamiento documentado de SessionFactoryUtils.getSession()
:
Obtenga una sesión de Hibernate para la SessionFactory dada. Conoce y devolverá cualquier sesión existente existente vinculada al hilo actual, por ejemplo cuando se utiliza HibernateTransactionManager. De lo contrario, creará una nueva sesión, si "allowCreate" es verdadero.
Como nada ha vinculado una sesión a la transacción actual, falla.
Mi sugerencia sería utilizar HibernateTemplate
: defina uno en su contexto y conéctelo automáticamente a su prueba. HibernateTemplate
tiene la mayoría de las mismas operaciones que una Sesión de guerra, pero hace la sesión para usted. Deberías poder hacer lo siguiente:
hibernateTemplate.get(User.class, "[email protected]");
SessionFactoryUtils.getSession () es tan bueno como cualquier otra forma de obtener la sesión. Hace lo mismo que HibernateDaoSupport.getSession ().
La razón por la que necesita un alcance de proxy es debido a la oportunidad. Sin el proxy de alcance parece que está inyectando la Sesión antes de que comience la prueba y, por lo tanto, antes de que comience la transacción y así se obtienen los errores.
Al agregar el proxy con alcance, envía un proxy a la sesión e inyecta eso para que no inyecte la sesión por adelantado (antes de que comience la transacción) sino que solo la recupera y realiza llamadas una vez que la prueba se está ejecutando, cuando realmente necesita hacer una llamar contra eso.