Search code examples
springtransactionalcglib

when add @Transactional cannot initialize dao bean


In a dao of a legacy project I add a new method, then I wrote a unit test to test the new method

FooDAO dao = new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml"}).getBean("FooDAO");  
@Test
public void test_getUnenabledArtisanIdsByReverseTime(){
    String reverseTimeStr = "2016-02-18 19:00";
    List<String> artisanIds = new ArrayList<>();
    artisanIds.add("1");
    artisanIds.add("2");
    dao.getUnenabledArtisanIdsByReverseTime(artisanIds, reverseTimeStr);
}

it failed, throw below exception

org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here

Then I add below configuration in applicationContext.xml

<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" />

and add @Transactional above the new method

@Transactional
public List<String> getUnenabledArtisanIdsByReverseTime(List<String> artisanIds, String reverseTimeStr) 

Then execute unit test again, failed too, but it's another exception

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'FooDAO' defined in file [/home/zhuguowei/workspace/myapp/WebContent/WEB-INF/classes/com/foo/artisan/repository/FooDAO.class]: 
Initialization of bean failed; nested exception is org.springframework.aop.framework.AopConfigException: 
Could not generate CGLIB subclass of class [class com.foo.artisan.repository.FooDAO]: 
Common causes of this problem include using a final class or a non-visible class; nested exception is net.sf.cglib.core.CodeGenerationException: java.lang.ClassCastException-->java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:527)

very strange, the dao is public and not final class

@Component
public class FooDAO extends BaseDaoHiber4Impl<Foo>

public BaseDaoHiber4Impl() {
    this.entryClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}

So what's the reason of this problem and how could to solve this?


Solution

  • A cglib proxy works by creating a new class, extending the target class, and overriding each method.

    When the proxy is created, your constructor fails :

    (ParameterizedType) getClass().getGenericSuperclass()
    

    getClass() is now the proxy, and getGenericSuperclass() is the current class (FooDAO), which is not a ParameterizedType

    Possible workaround :

    1. It looks like you are trying to reimplement spring-data : give it a try instead :-)
    2. Don't use cglib proxies : Define an interface for your DAO, make FooDAO implement it, and inject this interface instead of your class.
    3. Handle the case where FooDAO is extended : iterate through all getGenericSuperclass() to find the first ParameterizedType instance