0

In principle I have more or less the following code

@Component
public class SomeService {
  @Autowired
  private SettingsService settingsService;

  @Autowired
  private OtherService otherService;

  @Transactional
  public void storeData(Data d) {
    settingsService.set("setting1", d.getSetting1());
    settingsService.set("setting2", d.getSetting2());
    settingsService.set("setting3", d.getSetting3()); // no effect

    // do something with otherService
  }

  @Transactional
  public void storeData2(Data d) {
    settingsService.set("setting1", d.getSetting1());
    settingsService.set("setting3", d.getSetting3());
    settingsService.set("setting2", d.getSetting2()); // no effect

    // do something with otherService
  }

  @Transactional
  public void storeData3(Data d) {
    settingsService.set("setting1", d.getSetting1());
    settingsService.set("setting2", d.getSetting2()); // no effect
    // settingsService.set("setting3", d.getSetting3());

    // do something with otherService
  }

  @Transactional
  public void storeData4(Data d) {
    settingsService.set("setting1", d.getSetting1());
    settingsService.set("setting2", d.getSetting2());
    settingsService.set("setting3", d.getSetting3());
    settingsService.set("setting3", "doesn't matter"); // works but hacky

    // do something with otherService
  }

  // @Transactional
  public void storeData5(Data d) {
    settingsService.set("setting1", d.getSetting1());
    settingsService.set("setting2", d.getSetting2());
    settingsService.set("setting3", d.getSetting3()); // works but no TX :(

    // calls to otherService go boom because there is no TX
  }
}

Transaction management is enabled in the main configuration via a simple @EnableTransactionManagement.

Both SettingsService and OtherService store some stuff in the same relational database.

Now when I call some of the storeData methods, it is always the last call to settingsService that has no effect, i.e., the changes are not persisted in the database (see storeData, storeData2 and storeData3). If I execute the last line twice (or try to persist some bogus data), everything works but this is obviously a hack (storeData4).

Curiously in all cases the calls to OtherService work with everything being persisted as it should.

SettingService's set method can be either annotated with @Transactional or not; the described faulty behavior does not change. It is only when I remove @Transactional from SomeService's storeData method that all calls to settingsService have an effect (storeData5). However I really want that transaction (and I need it for dealing with otherService).

So the behavior above shows that the services calls work in principle but not all at the same time. I debugged everything and all variables are set to the correct values. I have no idea what the reason for this behavior could be and how to debug it any further.

musiKk
  • 14,751
  • 4
  • 55
  • 82

1 Answers1

0

I had similar transactional loss (although not identical) until I realised the scoping of the @Transactional annotation as found during runtime.

I'll cut a long story short: Promote the @Transactional annotation to your calling client. That prevents multiple transactions taking place which in our test environment was causing random chaos.

So in your case whatever is calling your service methods, add @Transactional to it. Not guaranteeing it will fix your specific problem, but it did for us.

jmkgreen
  • 1,633
  • 14
  • 22
  • What if you consider `SomeService` as calling client of `SettingsService`? `SomeService` is on the "outer edge" of the service layer so to speak. I cannot reasonably move the declared transaction further out. – musiKk Mar 13 '14 at 14:57
  • Services take part in transactions. They do not begin or end them. The responsibility of beginning and ending transactions is that of controllers (aka interactors). Controllers call services (which may in turn call over services as needed) and make objects change by calling their methods. Data is pulled in from a database, changed, then written back. The transaction begins at the controller, and is completed at the controller, not within a service. – jmkgreen Mar 13 '14 at 15:02
  • [These Answers](http://stackoverflow.com/questions/1079114/spring-transactional-annotation-best-practice) and others seem to disagree. Regardless, transactions have to work, no matter where I start them. I don't want to avoid the problem (I already have a hacky workaround for that), I want to solve it. Maybe this is just a symptom for some other issue. I just don't know what it could be. – musiKk Mar 13 '14 at 15:26