Auto-notified dependency injection failed when using @Transactional

I am testing my DAO, but this did not work. The following error occurred:

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 

My DAO:

 @Service @Scope("singleton") public class HibernateController extends HibernateDaoSupport { @Autowired public SessionFactory sessionFactory; @Transactional public void addAcount(Account account) { sessionFactory.getCurrentSession().saveOrUpdate(account); } } 

My test for this 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")); } } 

My application context.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> 

Note that if you comment on @Transactional in the DAO, the bean is created correctly. What's happening?

+7
source share
2 answers

First of all, it’s very difficult to give a name ending in the controller, DAO, it is very confusing, Controller and DAO have different goals.

When you add @Transactional to a service or dao class, for spring, to make it work in a transaction, you need to create a proxy of this class, this is a kind of shell where, before executing the proxy class (the class that is proxied), the spring method starts the transaction and after execution, if no exceptions complete the transaction, this can be done in spring through AOP and Annotations. Describe in code.

 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 } } } 

As you can see, this is not an exact implementation, but the basis code of how the transaction magically works for you. The key point is the OriginalDao interface, which makes this injection easy, as OriginalDaoImpl and ProxyDaoImpl implement the same interface. Therefore, they can be interchanged, i.e. A proxy server that has the original. This dynamic proxy can be created in Java through the Java dynamic proxy. Now the question is that if your class does not implement an interface, it becomes harder for it to replace. One of the CGLIB libraries, as far as I know, helps in this scenario, due to which it generates a dynamic subclass for the class in question, and the overriden method does the magic as described above, calling super.save(o) delegate the source code.

Now to the problem of injection.

  • Create an interface and make your dao implemented, and spring will use the JDK proxy by default, as it behaves now.
  • Add proxy-target-class="true" attribute <tx:annotation-driven transaction-manager="transactionManager"/>

Regarding the exception, it throws, since it is expected that the entered bean will be of type "HibernateController", but it is not.

For reference, you can link to the links below.

Hope this helps !!!!!.

+22
source

If you use Spring MVC , be sure to scan specific controller classes in the servlet context file . Otherwise, it scans 2 times, and the transaction is not available in the application context.

0
source

All Articles