2012-03-22 15 views
7

Estoy probando mi DAO, pero no funcionó. El siguiente error:Se produjo un error en la inyección de dependencias de autocableado al usar @Transactional

Tests in error: 
    testAccountOperations(com.tsekhan.rssreader.dao.HibernateControllerTest): Error creating bean with name 'com.tsekhan.rssreader.dao.HibernateControllerTest': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: com.tsekhan.rssreader.dao.HibernateController com.tsekhan.rssreader.dao.HibernateControllerTest.hibernateController; nested exception is java.lang.IllegalArgumentException: Can not set com.tsekhan.rssreader.dao.HibernateController field com.tsekhan.rssreader.dao.HibernateControllerTest.hibernateController to $Proxy25 

Mi DAO:

@Service 
@Scope("singleton") 
public class HibernateController extends HibernateDaoSupport { 

    @Autowired 
    public SessionFactory sessionFactory; 

    @Transactional 
    public void addAcount(Account account) { 
     sessionFactory.getCurrentSession().saveOrUpdate(account); 
    } 
} 

Mi prueba para este DAO:

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration("classpath:/applicationContext.xml") 
public class HibernateControllerTest { 

    @Autowired 
    HibernateController hibernateController; 

    private Set<Channel> getTestChannelList(String channelLink) { 
     Channel testChannel = new Channel(); 
     testChannel.setSourceLink(channelLink); 
     Set<Channel> testChannelList = new HashSet<Channel>(); 
     testChannelList.add(testChannel); 
     return testChannelList; 
    } 

    private Account getTestAccount(String accountLogin, String channelLink) { 
     Account testAccount = new Account(); 
     testAccount.setAccountLogin(accountLogin); 
     testAccount.setChannelList(getTestChannelList(channelLink)); 
     return testAccount; 
    } 

    @Test 
    public void testAccountOperations() { 
     hibernateController 
       .addAcount(getTestAccount("test_login", "test_link")); 
    } 
} 

Mi applicationContext.xml:

<?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:context="http://www.springframework.org/schema/context" 
    xmlns:tx="http://www.springframework.org/schema/tx" 
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
     http://www.springframework.org/schema/beans/spring-beans-3.1.xsd 
     http://www.springframework.org/schema/tx 
     http://www.springframework.org/schema/tx/spring-tx-3.1.xsd 
     http://www.springframework.org/schema/context 
     http://www.springframework.org/schema/context/spring-context-3.1.xsd" 
     default-autowire="byName"> 

    <!-- Enabling spring-transaction annotations --> 
    <tx:annotation-driven transaction-manager="transactionManager"/> 

    <!-- Enabling annotation-driven configurating --> 
    <context:annotation-config /> 

    <!-- Creation of transaction manager --> 

    <bean id="transactionManager" scope="singleton" 
     class="org.springframework.orm.hibernate3.HibernateTransactionManager"> 
     <property name="sessionFactory" ref="sessionFactory"/> 
    </bean> 

    <bean id="sessionFactory" scope="singleton" 
     class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"> 
     <property name="configLocation" value="classpath:/hibernate.cfg.xml"/> 
     <property name="configurationClass"> 
      <value>org.hibernate.cfg.AnnotationConfiguration</value> 
     </property> 
    </bean> 
    <!-- 
    A Spring interceptor that takes care of Hibernate session lifecycle. 
    --> 
    <bean id="hibernateInterceptor" 
      class="org.springframework.orm.hibernate3.HibernateInterceptor"> 
     <property name="sessionFactory"> 
      <ref bean="sessionFactory"/> 
     </property> 
    </bean> 

    <bean name="employeeDAO" scope="prototype" 
     class="com.tsekhan.rssreader.dao.HibernateController" /> 

    <!-- Searching for hibernate POJO files in package com.tsekhan.rssreader.web --> 
    <context:component-scan base-package="com.tsekhan.rssreader.web" /> 
    <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" /> 
    <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" /> 

</beans> 

Noto que si comenta @Transactional en DAO, bean se crea correctamente. ¿Lo que pasa?

+0

BTW, en mi humilde opinión para las clases DAO es mejor utilizar la anotación '@ Repository' en lugar de' @ Service'. –

Respuesta

22

En primer lugar, es realmente malo darle un nombre que termine en Controller a un DAO, es muy confuso, Controller y DAO tienen todos juntos un propósito diferente.

Cuando agrega @Transactional a un servicio o clase dao, para que la primavera funcione en una transacción necesita crear un proxy de esa clase, es una especie de envoltorio donde antes de la ejecución de la clase proxiada (clase en consideración cuál es el proxy) el resorte de método inicia la transacción y después de la ejecución en caso de que no haya excepciones, se puede hacer en primavera a través de AOP y Anotaciones. Para describir en el código.

public class OriginalDaoImpl implements OriginalDao extends DaoSupport { 

    public void save(Object o){ 
     manager.save(o); 
    } 
} 

public class ProxyDaoImpl implements OriginalDao { 

    private OriginalDao originalDaoImpl; //instance of OriginalDaoImpl 
    public void save(Object o){ 
     try{ 
      transaction.start(); 
      originalDaoImpl.save(o); 
      transaction.commit(); 
     }catch(Exception e){ 
      transaction.rollback(); 
     }finally{ 
      //clean up code 
     } 
    } 
} 

Como ve, esta no es una implementación exacta sino un código básico, cómo la transacción funciona mágicamente para usted. El punto clave es su interfaz OriginalDao, que hace que esta inyección sea sencilla, ya que OriginalDaoImpl y ProxyDazzle implementan la misma interfaz. Por lo tanto, pueden intercambiarse, es decir, el sustituto del original. Este proxy dinámico se puede crear en java mediante el proxy dinámico de Java. Ahora, la pregunta de qué pasa si tu clase no implementa una interfaz, es más difícil que suceda el reemplazo. Una de las bibliotecas CGLIB hasta donde yo sé, ayuda en tal escenario, mediante el cual genera una subclase dinámica para la clase en consideración y en el método reemplazado realiza la magia como se describió anteriormente, llamando al super.save(o) para delegar en el código original.

Ahora al problema de la inyección.

  1. Cree la interfaz y haga que su dao implemente eso y la primavera adoptará por defecto el proxy JDK, ya que se está comportando ahora.
  2. proxy-target-class="true" Añadir atributo a <tx:annotation-driven transaction-manager="transactionManager"/>

En lo que respecta a excepción se está lanzando ya que está a la espera de frijol inyectada a ser de tipo 'HibernateController', pero no lo es.

Para su referencia puede consultar los enlaces a continuación.

  1. 10.5.6 Using @Transactional
  2. Spring AOP Doc

Espero que esto ayude !!!!!.

+2

Quizás deba aclarar que no necesita 'proxy-target-class =" true "' cuando se inyectan interfaces. También 'proxy-target-class =" true "' romperá '@ Transactional' en las interfaces. Además de eso: +1. – alexkasko

+0

@ alx3apps sí, eso no hizo clic en mi mente, bien, usted agregó –

+0

, gran respuesta y buena explicación – cowls

0

Si está utilizando Spring MVC, asegúrese de escanear clases de controlador específicas solo en servlet context file. De lo contrario, escaneará 2 veces y la transacción no estará disponible en el contexto de la aplicación.

Cuestiones relacionadas