0

I'm trying out the @Transactional annotation but I'm not sure what's exactly wrong, I have the following code

Controller

@RequestMapping(value = "/remove", method = RequestMethod.POST) void removeUserFlight(@RequestParam("flightId") Long flightId) throws Exception {
    int affectedRows = userFlightDao.removeFlightByFlightId(flightId, user.getId());
  throw new RuntimeException("Testing rollback?");
}

Repository

@Repository
public interface UserFlightDao extends CrudRepository<UserFlight, Long> {
    /** Named DELETE query defined in {@link UserFlight} */
    @Modifying @Transactional int removeFlightByFlightId(Long flightId, Long userId);
}

I was on the impression that the record will not get deleted if an exception gets thrown after the executing line. Could anybody please clarify what's wrong here?

edit: I also tried to put @Transactional on the Controller method, but it made no difference.

edit2: Tried to move the logic to a service like suggested below by Ralph, it worked fine. I still have no clue why @Transactional on the controller method directly doesn't work. I'll consider this solved for now but I'm still confused.

prettyvoid
  • 3,446
  • 6
  • 36
  • 60
  • Have you tried just placing `@Transactional` on controller and removing it in DAO? – Chaitanya Oct 19 '15 at 10:57
  • 3
    You are throwing an exception after the transaction committed, how should any thing be rolled back. – M. Deinum Oct 19 '15 at 10:59
  • 1
    see @M.Deinum comment above. In general it is a good idea to move transnational behavior on a higher abstraction level (service) so that you could use the same repository method from various services each having it's own transaction logic (e.g. combining several repositories into one service method and committing when everything is OK). – S. Pauk Oct 19 '15 at 11:06
  • @SergeyPauk You can't have `@Modifying` without `@Transactional` (it throws a `TransactionRequiredException`) but yea I agree with you that it's better to have `@Transactional` at the service layer. – prettyvoid Oct 19 '15 at 11:12
  • No you need a transaction not both `@Modifying` and `@Transactional` on the same method. The important part here is the transaction. – M. Deinum Oct 19 '15 at 11:14
  • @M.Deinum I need `@Modifying` because I call a `@NamedQuery` that executes a delete statement. If I remove `@Modifying` I get `org.hibernate.hql.internal.QueryExecutionRequestException: Not supported for DML operations [DELETE FROM.....` – prettyvoid Oct 19 '15 at 11:21
  • 1
    Please read... Yes you need `@Modifying` no you don't need `@Transactional` on the **same** method. You need a transaction which should be started in the service (i.e. before even calling this method). – M. Deinum Oct 19 '15 at 11:23
  • @M.Deinum Ok thanks for clarifying :) – prettyvoid Oct 19 '15 at 11:25
  • You can also refer links to understand different propagation mode http://stackoverflow.com/questions/8490852/spring-transactional-isolation-propagation/32223597#32223597 – NIrav Modi Oct 19 '15 at 13:24

1 Answers1

2

You must throw the Exception within the transaction. (If you throw the exception after the transaction is already committed, then it stays committed).

By default the transaction get committed at at leaving the method where it has been started.

For your Testcase: add an intermediate Service with Transaction annotation, and then throw the exception within that service method.

@Service
public class IntermediateService() {
    @Autowired
    UserFlightDao userFlightDao;

    @Transactional //maybe try propagation Requires_new
    public void doMyTest() {
        userFlightDao.removeFlightByFlightId(...);
        throw new RuntimeException("Testing rollback?");
    }
}
Ralph
  • 118,862
  • 56
  • 287
  • 383
  • Thanks but could you explain the difference between having the `removeFlightByFlightId` call inside the controller method directly and the service method? Both of them threw the exception after the call, no? – prettyvoid Oct 19 '15 at 11:10
  • 1
    Your controller method isn't transactional, the service method is. Major difference on where the transaction starts and ends.. – M. Deinum Oct 19 '15 at 11:13
  • I tried annotating the controller method with `@Transactional` but I got the same behavior. – prettyvoid Oct 19 '15 at 11:15
  • I am not 100% sure, but I belive it is important too use "right" `@Transactional`! - use: `org.springframework.transaction.annotation.Transactional` but not `javax.transaction.Transactional` – Ralph Oct 19 '15 at 11:21
  • I'm using springs annotations. I just tried to remove @Transactional from the DAO and have it only at the Controller level, and I got `TransactionRequiredException: Executing an update/delete query`. This leads me to believe that the controller annotation is not being picked up by spring. That's probably my problem. Any idea why it's not getting picked up? I'll try to move everything to a service like you suggested and will report back soon. Thanks :) – prettyvoid Oct 19 '15 at 11:28
  • Works as intended inside a `Service` like you described. If anybody have a guess on why it doesn't work when I annotate the controller method with `@Transactional` please share with us. – prettyvoid Oct 19 '15 at 12:46