7

I've scheduled a job with annotation @Scheduled, which was supposed to process data and save it to database using spring data jpa. The save method was invoked without any exceptions but there was no insert to database. In the same annotated method I invoked findAll method which worked fine and fetched data. What could be a reason?

@Repository
public interface PossibleOfferLinkRepository extends PagingAndSortingRepository<PossibleOfferLink,   Long> {
}


@Configuration
@ComponentScan
@EnableAutoConfiguration
@Import({Scheduler.class})
@EntityScan(basePackages="model_package")
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }


}

@EnableScheduling
@ConditionalOnProperty(value= "property_name")
public class Scheduler {

...
    @Scheduled(fixedRate=100000)
    public void scheduleCrawlerJob() throws MalformedURLException {
            Iterable<PossibleOfferLink> links = repo.findAll();
            PossibleOfferLink link = repo.save(new PossibleOfferLink(new URL("...")));
    }

}

Maven

        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>1.1.8.RELEASE</version>
        </parent>
    <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-jpa</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-batch</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>com.h2database</groupId>
                <artifactId>h2</artifactId>
                <version>1.4.182</version>
            </dependency>
            <dependency>
                <groupId>com.google.guava</groupId>
                <artifactId>guava</artifactId>
                <version>${guava.version}</version>
            </dependency>
            <!-- Test -->
            <dependency>
                <groupId>org.easytesting</groupId>
                <artifactId>fest-assert</artifactId>
                <version>${easytesting.version}</version>
            </dependency>
        </dependencies>
Xstian
  • 8,184
  • 10
  • 42
  • 72
Maniek Stasz
  • 347
  • 3
  • 13
  • try to add @Transactional on `scheduleCrawlerJob()` – Xstian Oct 29 '14 at 08:35
  • I've tried it but there is still no insert. Should I set some ordering? I use spring boot. – Maniek Stasz Oct 29 '14 at 08:39
  • I think that there is a transaction pending .. in debug mode try to perform a flush() on entitymanager. Anyway can you add more details? (configuration, etc ) – Xstian Oct 29 '14 at 08:43
  • Do you use transaction manager? – Xstian Oct 29 '14 at 09:09
  • I think that spring boot configures transaction manager for spring data jpa. I can autowire it, but when I invoke `entityManager.flush()` after save in `scheduleCrawlerJob()` with `@Transactional` annotation I get `javax.persistence.TransactionRequiredException: no transaction is in progress` – Maniek Stasz Oct 29 '14 at 09:18
  • Yes, this exception confirms doubt of mine. You must configure the transaction manager. – Xstian Oct 29 '14 at 09:32

3 Answers3

7

Finally i solved this issue in this way :

  1. Added @EnableTransactionManagement in Application class and also added PlatformTransactionManager Bean. Check the bellow code:

    @Autowired
    private EntityManagerFactory entityManagerFactory;
    
    @Bean
    public PlatformTransactionManager transactionManager()
    {
        return new JpaTransactionManager(entityManagerFactory);
    }
    

    Here is the imports :

    import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement;

  2. In my scheduler code added bellow code:

    @Scheduled(fixedRate = 60 *10*1000)
    @Transactional(propagation=Propagation.REQUIRES_NEW)
    public void reportCurrentTime() {
    doDatabaseTransaction();
    }
    

Here is the imports :

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

Hope this will solve your problem :)

Md. Sajedul Karim
  • 6,749
  • 3
  • 61
  • 87
5

Your problem is due to there aren't Transaction in progress to commit.

Cause:

javax.persistence.TransactionRequiredException: no transaction is in progress

Here an example on how to configure transaction manager by annotation. Here the reference

@Configuration
@EnableTransactionManagement
public class PersistenceJPAConfig{

   @Bean
   public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(){
      ...
   }

   @Bean
   public PlatformTransactionManager transactionManager(){
      JpaTransactionManager transactionManager = new JpaTransactionManager();
      transactionManager.setEntityManagerFactory(
       entityManagerFactoryBean().getObject() );
      return transactionManager;
   }

}

Add on scheduleCrawlerJob @Transactional

@Scheduled(fixedRate=100000)
@Transactional
public void scheduleCrawlerJob() throws MalformedURLException {
        Iterable<PossibleOfferLink> links = repo.findAll();
        PossibleOfferLink link = repo.save(new PossibleOfferLink(new URL("...")));
}
Xstian
  • 8,184
  • 10
  • 42
  • 72
  • Why insert is invoked when I call `repo.save(...)` in any `@Controller`? Without configuring transaction manager. – Maniek Stasz Oct 29 '14 at 09:57
  • [See this topic](http://stackoverflow.com/questions/23118789/spring-mvc-controller-shouldnt-be-transactional-why), so the transaction will opened from controller. – Xstian Oct 29 '14 at 09:59
  • 'CRUD methods on repository instances are transactional by default' means that by default they run in propagation REQUIRED, meaning they join an ongoing transaction and if none is present run in their own transaction - this means that `repo.save` should be in transaction when its called either from controller or other components. – Maniek Stasz Oct 29 '14 at 10:12
  • 1
    Yes, you're right :) .. but the issue is when scheduled this action. Scheduled does not begins any transaction and for this reason there aren't commits on save method (does not find transaction open). – Xstian Oct 29 '14 at 10:15
  • I mean if transaction is opened from controller, transaction manager should be configured already. – Maniek Stasz Oct 29 '14 at 10:18
  • So, I suspect @EnableAutoConfiguration and Spring-Boot they create the transactions.. [see this link](http://docs.spring.io/spring-boot/docs/1.1.8.RELEASE/api/org/springframework/boot/autoconfigure/EnableAutoConfiguration.html). But when the trigger is scheduled the transaction will not open by default. Can you make the level logging at DEBUG to check when the transaction will open? – Xstian Oct 29 '14 at 10:40
  • The @EnableAutoConfiguration annotation, exclusively own of Spring Boot. What does? According with a part of its API: "Enable auto-configuration of the Spring Application Context, attempting to guess and configure beans that you are likely to need. Auto-configuration classes are usually applied based on your classpath and what beans you have defined". I suggest read the page for more details. Simply, it gives you all what you need always, for example dataSource and transactionManager beans. [Reference](http://manueljordanelera.blogspot.it/2014/06/springbootpersistencetransaction.html) – Xstian Oct 29 '14 at 10:43
  • I know it, that is why I didn't know why you want me to configure transaction manager again. – Maniek Stasz Oct 29 '14 at 10:48
  • both your class (Application and Scheduler) are under the same root? .. com.your.fqn.Application and com.your.fqn.etc1.etc2.Scheduler ? EnableAutoConfiguration should perform a scan of classes where to apply a proxy strategy to use transactions. – Xstian Oct 29 '14 at 11:08
  • Yes they are. In the log there is nothing about persisting entity. This is end of log: `DEBUG [pool-2-thread-1] Resolving associations for [pl.edu.agh.offerseeker.commons.model.PossibleOfferLink#2056] DEBUG [pool-2-thread-1] Done materializing entity [pl.edu.agh.offerseeker.commons.model.PossibleOfferLink#2056] DEBUG [pool-2-thread-1] Releasing JDBC connection DEBUG [pool-2-thread-1] Released JDBC connection` – Maniek Stasz Oct 29 '14 at 11:13
0

I got such issue solved with wrapping the body of scheduleCrawlerJob() to a separate method annotated with @Transactional of another class and creating a method annotated with @Async that is called from scheduleCrawlerJob()

So overall chain of calls would look like:

Scheduled -> Async -> Transactional

P.S. don't forget to add @EnableAsync to the configuration

Panz0r
  • 105
  • 1
  • 10