Announcement Announcement Module
Collapse
No announcement yet.
No transaction in transactional service called from @PostConstruct Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • No transaction in transactional service called from @PostConstruct

    Dear friends!
    For some reason transaction doesn't start when transactional service method is called from @PostConstruct method.
    Following are the context and the components:
    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <beans ...>
    
        <context:component-scan base-package="com.bla"/>
    
        <bean id="c3p0DataSource"
              class="com.mchange.v2.c3p0.ComboPooledDataSource"
              destroy-method="close">...</bean>
    
        <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
            <property name="sessionFactory" ref="sessionFactory"/>
        </bean>
    
        <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
            <property name="dataSource" ref="c3p0DataSource"/>
            <property name="cacheProvider" ref="cacheProvider"/>
            <property name="hibernateProperties">
                <props>
                    <prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop>
                </props>
            </property>
            <property name="annotatedPackages">
                <list>...</list>
            </property>
        </bean>
    </beans>
    Code:
    @Component
    public class Bootstraper {
    
       @Autowired
        private HeatCacheService heatCacheService;
    
        @PostConstruct
        public void bootstrap() {
                    heatCacheService.heatCache();
        }
    }
    Code:
    @Service
    @Transactional
    public class HeatCacheService {
    
        @Autowired
        private HeatCacheDAO heatCacheDAO;
    
    
        @Transactional(readOnly = true)
        public void heatCache() {
            this.heatCacheDAO.heatCache();
        }
    }
    Code:
    @Repository
    public class HeatCacheDAO extends AbstractDAO {
      
        public void heatCache() {
            loadEntity(SomeEntity.class);
            ...
        }
    
        private void loadEntity(Class clazz) {
            Session session = sessionFactory.getCurrentSession();
            session.createCriteria(clazz).list();
        }
    }
    That's all, simple as that.
    The exception I get is "No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here", which means that no transaction exists when calling sessionFactory.getCurrentSession().
    If I open the transaction manully (using TransactionTemplate in the @PostConstruct method) it works like charm, but I want to stay with declarative transactions as much as possible.

    Thank you in advance.
    Baruch.

  • #2
    In the @PostConstruct (as with the afterPropertiesSet from the InitializingBean interface) there is no way to ensure that all the post processing is already done, so (indeed) there can be no Transactions. The only way to ensure that that is working is by using a TransactionTemplate.

    Comment


    • #3
      That doesn't sound good, actually. Isn't "afterPropertiesSet" means that the bean is fully configured and ready to fly?

      Comment


      • #4
        No. It means that your bean is created and all the properties have been injected, it doesn't mean that all the other BeanPostProcessor already did their respective jobs.

        Comment


        • #5
          So, how can I be sure that when my business logic kicks in (without @PostConstruct, new user request, for example), the BeanPostProcessors already did their respective jobs?

          Comment


          • #6
            If the ApplicationContext is finished and your application is up and running everything happend. All the Bean(Factory)PostProcessors have done their respective jobs. It is/can only be an issue from within @PostConstruct methods.

            Comment


            • #7
              This sounds promissing. How can I get a callback from application contex upon finishing the bootstrap?

              Comment


              • #8
                You can register an ApplicationListener and listen for ContextRefreshedEvent.

                Comment


                • #9
                  Marten, thanks so much for your help!

                  Comment


                  • #10
                    @PostInitialize

                    For those, who might be interested, @PostInitialize support attached.
                    Methods, annotated with this annotation will be run upon full ApplicationContext initialization. Enjoy.

                    Comment


                    • #11
                      I have same problem with @Transactional and @PostConstruct method.

                      jbaruch, thanks a lot for your example.
                      Marten, thank you for your explanation.

                      Comment


                      • #12
                        what worked for me

                        I tried the PostInitializer stuff and it didn't work for me.

                        What I ended up doing was put that class in using xml and set the constructor arg with the sessionfactory. Then I called my init method inside the constructor.

                        <bean class="net.tkz.league.service.LeagueService">
                        <constructor-arg ref="league.dao.sessionFactory"/>
                        </bean>


                        public LeagueService(SessionFactory sessionFactory) {
                        this.sessionFactory = sessionFactory;
                        init();
                        }

                        Comment


                        • #13
                          Thanks and a fix

                          thanks, excellent solution!
                          there was one glitch, though, since using method.isAnnotationPresent() and method.getAnnotation() does not apply for super types and so, for some proxies, the annoation wasn't detected. Attached a fix for that.

                          Comment


                          • #14
                            Well, since the main purpose of PostInitializers is doing transactional work, it will aways be proxy, so your patch is critical. Nice and clean, thanks.

                            Comment


                            • #15
                              cosmetic

                              cool,
                              just for ease of use (one download rather than two), I'm re-posting the complete patched version, with the exclusion of a debug print that mistakenly infiltrated the code previously.

                              Comment

                              Working...
                              X