2011-02-27 11 views
10

En mi proyecto utilizo Hibernate con la demarcación de transacciones programáticas. Cada vez que en mis métodos de servicio escribo algo similar a esto.Configuraciones del administrador de transacciones de Hibernate en Spring

Session session = HibernateUtil.getSessionFactory().openSession(); 
session.beginTransaction(); 
.. perform operation here 
session.getTransaction().commit(); 

Ahora voy a refactorizar mi código con gestión declarativa de transacciones. Lo que tengo ahora ...

<context:component-scan base-package="package.*"/> 

    <bean id="mySessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> 
    <property name="configLocation" value="classpath:hibernate.cfg.xml"></property> 
    <property name="configurationClass"> 
     <value>org.hibernate.cfg.AnnotationConfiguration</value> 
    </property> 
    </bean> 

    <tx:annotation-driven transaction-manager="txManager"/> 


    <bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> 
    <property name="sessionFactory"> 
     <ref local="mySessionFactory"/> 
    </property> 
    </bean> 

clase de servicio:

@Service 
public class TransactionalService { 

    @Autowired 
    private SessionFactory factory; 

    @Transactional 
    public User performSimpleOperation() { 
     return (User)factory.getCurrentSession().load(User.class, 1L); 
    } 
} 

y simple prueba -

@Test 
public void useDeclarativeDem() { 
    FileSystemXmlApplicationContext ctx = new FileSystemXmlApplicationContext("spring-config.xml"); 
    TransactionalService b = (TransactionalService)ctx.getBean("transactionalService"); 
    User op = b.performSimpleOperation(); 
    op.getEmail(); 

Cuando trato de obtener el correo electrónico del usuario fuera del método transaccional, tengo Lazy excepción de inicialización, el correo electrónico es mi caso es una cadena simple. Hibernate ni siquiera realiza la consulta sql, hasta que llamo a cualquier getter en mi POJO.

1) ¿Qué estoy haciendo mal aquí?

2) ¿Es este enfoque válido?

3) ¿Puede sugerir algún proyecto de opensources que funcione en spring/hibernate con la configuración basada en anotaciones?

actualización

Por alguna razón si reemplazo getCurrentSession con openSession este código funciona bien. ¿Alguien puede explicarlo por favor?

Gracias

+0

¿El correo electrónico de un usuario está en otra tabla? –

+0

cadena simple en la misma tabla – user12384512

Respuesta

10

Ok, finalmente me di cuenta de cuál era el problema. En el código anterior utilicé load en lugar de get. Session.load en realidad no llegó a los datos.Esa es la razón por la que recibo la excepción de inicialización diferida fuera del método @Transactional

Si uso openSession en vez de getCurrentSession, la sesión se abre fuera del contenedor de alcance del telescopio. Como resultado de la sesión no estaba cerca y que me permite leer las propiedades del objeto fuera del método @Transactional

2

La razón de hibernación no realiza ninguna consultas SQL hasta que llame a los captadores es porque creo que el fetchType se establece en perezoso. Para solucionar este problema, necesitará cambiar la fetchType a EAGER en su POJO:

@Entity 
@Table(name = "user") 
public class User { 

    /*Other data members*/ 

    @Basic(fetch = FetchType.EAGER) 
    private String email; 

} 

Yo personalmente nunca tuve que especificar los tipos básicos a tener un fetchType EAGER así que no estoy del todo seguro de por qué su configuración requiere esto. Si solo está en sus pruebas, podría deberse a la forma en que configuró sus pruebas JUnit. Se debe tener algo como esto en la declaración de clase:

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration({"/test-app-config.xml"}) 
@Transactional 
public class UserServiceTest { 

} 

En cuanto a un buen recurso siempre encuentro SpringByExample para ser útil.

EDITAR

así que no estoy del todo seguro de lo que está mal con su configuración. Difiere de la forma en que tengo la mía configurada, así que aquí está mi configuración típica con la esperanza de que ayude. El hibernate.transaction.factory_class podría ser una propiedad clave que te hace falta. También utilizo el AnnotationSessionFactoryBean:

<!-- DataSource --> 
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" 
    p:driverClassName="com.mysql.jdbc.Driver" 
    p:url="jdbc:mysql://localhost/dbname" 
    p:username="root" 
    p:password="password"/> 

<!-- Hibernate session factory --> 
<bean id="sessionFactory" 
    class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean" 
    p:dataSource-ref="dataSource"> 
    <property name="packagesToScan" value="com.beans"/> 
    <property name="hibernateProperties"> 
     <props> 
      <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop> 
      <prop key="hibernate.show_sql">true</prop> 
      <prop key="hibernate.transaction.factory_class">org.hibernate.transaction.JDBCTransactionFactory</prop> 
     </props> 
    </property> 
</bean> 

<!-- Transaction Manager --> 
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> 
    <property name="sessionFactory"> 
     <ref bean="sessionFactory" /> 
    </property> 
</bean> 

<tx:annotation-driven transaction-manager="transactionManager"/> 
+0

Gracias por la respuesta, en realidad no hay una inicialización lenta en ningún campo del usuario, y por supuesto no especifiqué la estrategia de búsqueda para una cadena simple. Creo que el problema está en la configuración de primavera – user12384512

+0

¿Podría publicar el rastreo de pila de su LazyInitializationException? Parece extraño que no haya alguna forma de inicialización lenta si esa es la excepción que está recibiendo. org.hibernate.LazyInitializationException –

+0

ERROR - no ha podido inicializar de proxy - ninguna sesión org.hibernate.LazyInitializationException: no se pudo inicializar de proxy - ninguna sesión \t en org.hibernate.proxy.AbstractLazyInitializer.initialize (AbstractLazyInitializer.java:132) \t en org.hibernate.proxy.AbstractLazyInitializer.getImplementation (AbstractLazyInitializer.java:174) \t en org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke (JavassistLazyInitializer.java:190) – user12384512

0

De la documentación de Hibernate https://docs.jboss.org/hibernate/orm/4.2/manual/en-US/html/ch11.html#objectstate-loading:

Tenga en cuenta que la carga() lanzará una excepción irrecuperable si no hay una base de datos de fila coincidente . Si la clase se asigna con un proxy, load() solo devuelve un proxy no inicializado y no llega a la base de datos hasta que invoque un método del proxy. Esto es útil si desea crear una asociación a un objeto sin cargarlo realmente de la base de datos. También permite que se carguen varias instancias como un lote si se define el tamaño del lote para la asignación de clase.

De la documentación anterior en su caso bean se asigna con el proxy de primavera por lo que la carga solo devuelve. Por lo tanto, necesita usar get() en lugar de load() que siempre golpea la base de datos.

Cuestiones relacionadas