Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why @Scheduled annotation doesn't work with @Transaction annotation. Spring Boot [duplicate]

I have a question: Why when we annotate method with @Scheduled and @Transaction, transaction doesn't work? I know that the @Scheduled call my class instead of proxy class that created by Spring, but can't understand this behavior.

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.transaction.annotation.Transactional;

@Service
public class UserServiceImpl implements UserService {

    @Override
    @Scheduled(fixedRateString = "${somestring}",initialDelayString = "${anotherstring}")
    @Transactional
    public void doSomething() {

        }
    }

I have two solutions of this problem:

  1. Call proxy from Scheduled method.

  2. Implement ConcurrentTaskScheduler and replace object of ScheduledMethodRunnable(that is with my class) with object of ScheduledMethodRunnable with proxy.

But this solutions is very inconvenient.

Can you explaim me why @Scheduled works like this?

Thank you!

like image 713
Artiom Saidanov Avatar asked Jul 27 '17 15:07

Artiom Saidanov


People also ask

How does @scheduled work in Spring?

We can turn any method in a Spring bean for scheduling by adding the @Scheduled annotation to it. The @Scheduled is a method-level annotation applied at runtime to mark the method to be scheduled. It takes one attribute from cron , fixedDelay , or fixedRate for specifying the schedule of execution in different formats.

Is @scheduled asynchronous?

Note that scheduled tasks don't run in parallel by default. So even if we used fixedRate, the next task won't be invoked until the previous one is done. Now this asynchronous task will be invoked each second, even if the previous task isn't done.

What is @transaction annotation in Spring boot?

The @Transactional annotation is metadata that specifies that an interface, class, or method must have transactional semantics; for example, "start a brand new read-only transaction when this method is invoked, suspending any existing transaction".


1 Answers

It happens because to process both annotations MAGIC is used.

I suppose there are several things happens:

  1. UserServiceImpl is created.
  2. @Scheduled annotation is processed and reference to bean is stored to invoke it at appropriate time.
  3. @Transactional annotation is processed. It create proxy which store reference to original bean. Original bean is replaced to proxy in application context.

If step 2 and 3 passed in different order then you had no problem.

I don't know how to control order in which annotation is processed. I don't even sure it is possible at all.

There is basically two solution.

  1. Use different kind of magic to process @Transaction. Default way is to create proxy object, but it is possible to instruct Spring to instrument current class.
  2. Split this to two class each of them will have method with only one annotation.

Example:

@Service
public class UserServiceImpl implements UserService {

    @Override
    @Transactional
    public void doSomething() {

    }
}

@Service
public class UserServiceScheduler {

    @Inject
    private UserService service;

    @Scheduled(fixedRateString = "${somestring}",initialDelayString = "${anotherstring}")
    public void doSomething() {
         service.doSomething();
    }
}

I'm personally recommend second approach.

like image 97
talex Avatar answered Oct 06 '22 00:10

talex