Spring Scheduler - When there is a cyclic dependency, scheduled method doesn't start in transaction
Asked Answered
M

1

6

I am working on an application which is using Spring 3, Hibernate and JPA. I have two classes as follows:

@Component
class Manager {
    @Autowired
    Util util;
}

and

@Component
class Util {
    @Autowired
    Manager manager;

    @Scheduled(fixedDelay = 1 * 60 * 1000)
    @Transactional(propagation = Propagation.REQUIRED)
    public void scheduledMethod(){
        // Need to update the database in a transaction
    }
}

The relevant part from application context is as follows:

    <context:component-scan base-package="packageName" />
    <tx:annotation-driven />
    <bean id="entityManagerFactory"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
       <property name="persistenceUnitName" value="defaultPU" />
       <property name="dataSource" ref="dataSource" />
    </bean>
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
       <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>
    <task:annotation-driven executor="executor" scheduler="scheduler"/>
    <task:executor id="executor"
       pool-size="10"
       queue-capacity="10000"
       rejection-policy="CALLER_RUNS"/>
    <task:scheduler id="scheduler" pool-size="10"/>

With this configuration, I get the following exception

javax.persistence.TransactionRequiredException: no transaction is in progress
        at org.hibernate.ejb.AbstractEntityManagerImpl.flush(AbstractEntityManagerImpl.java:978)
        at sun.reflect.GeneratedMethodAccessor88.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:601)
        at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:365)
        at com.sun.proxy.$Proxy43.flush(Unknown Source)
        at sun.reflect.GeneratedMethodAccessor88.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:601)
        at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:240)
        at com.sun.proxy.$Proxy43.flush(Unknown Source)

If I remove the autowiring of Manager class from Util class, it works fine. Also, while debugging I found that the scheduled method starts executing even if there is some error in the application context file.

For some legacy reasons, I cannot avoid the cyclic dependency. Can someone help why this exception occurs in case of cyclic dependency?

Mckoy answered 24/7, 2013 at 10:4 Comment(1)
It seems that scheduled postprocessor processes bare bean even if it's supposed to fire after AOP proxy creator(because postprocessors are ordered). I think you can send a bug to Spring JIRA, at least this behavior is poorly documentated. As a workaround you can fire transactional method from a separate bean with scheduled method or use TransactionTemplate instead if transactional.Foreordination
G
0

You can achive this using @PostConstruct

@Component
class Manager {

    Util util;

    public void setUtil(Util util) {
        this.util =  util;
    }
}


@Component
class Util {
    @Autowired
    Manager manager;

    @PostConstruct
    public void init(){
        manager.setUtil(this);

    }

    @Scheduled(fixedDelay = 1 * 60 * 1000)
    @Transactional(propagation = Propagation.REQUIRED)
    public void scheduledMethod(){
        // Need to update the database in a transaction
    }
}
Goalie answered 3/8, 2013 at 15:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.