0

I have inherited a Spring Java codebase where it seems pretty much every method from the business service down to the low level DAOs are tagged with @Transactional. It has some severe performance issues that I noticed are mitigated somewhat when certain annotations are changed from @Transactional(readOnly=false) to @Transactional(readOnly=true). It also seems to have periodic calls to EntityManager.flush() that can't be explained except that certain objects do not get written to the DB without them.

My hunch is that the original developers are misusing/overusing transactions, but I'm not sure of the best approach to clean this up. I would appreciate advice from those more well-versed in Spring Transactions than me.

A reduced example of just one segment of the code follows. There are others much more complex than this with 5-6 levels of nested transactions.

// MVC Controller REST Service
@Controller
@RequestMapping("/service")
public class Group {
    @Inject private GroupService groupService;

    public @ResponseBody Object update(@RequestBody Group group) {
        return groupService.update(group);
    }
}

// Business service
@Service
public class GroupService {
    @Inject private GroupDAO groupDao;
    @Inject private AuditService auditService;

    @Transactional(readOnly=false)
    public Group update(Group group) {
        Group groupToUpdate = groupDao.get(group.getId());
        // Do magic
        groupDao.persist(groupToUpdate); // Shorthand to call EntityManager.persist()
        auditService.logUpdate(group);
        return groupToUpdate;
    }
}

// DAO
@Repository
public class GroupDAO extends AbstractDAO {
    @Transactional(readOnly=true)
    public Group get(Long id) {
        return entityManager.find(Group.class,id);
    }
}

// Auditing service
@Component
public class AuditService {
    @Inject AlertDAO alertDao;

    @Transactional(readOnly=false)
    public void logUpdate(Object o) {
        Alert alert = alertDao.getFor(o);
        // Do magic
        alertDao.update(alert);
        alertDao.flush() // Shorthand for EntityManager.flush() but WHY???
    }
}

// DAO
@Repository
public class AlertDAO extends AbstractDAO {
    @Transactional(readOnly=true)
    public Alert getFor(Object forObj) {
        // Magic here
        return entityManager.find(Alert.class,foundId);
    }

    @Transactional(readOnly=false)
    public void update(Alert a) {
        // Magic here
        entityManager.merge(a);
    }
}
  • 2
    Only service should be transactional. http://stackoverflow.com/questions/1079114/where-does-the-transactional-annotation-belong – JEY Mar 24 '16 at 14:56
  • Also have a look at posting to codereview.stackexchange.com – C_B Mar 24 '16 at 14:56
  • 4
    unless some of them are marked with propagation REQUIRES_NEW then "nesting" transactions really shouldn't have significant impact. The AOP will just say "Yup, there's already one running, carry on." (don't have an answer because you didn't actually ask any questions. My advice would be the concept of 'nesting' them in isolation is probably not the root cause of your performance issues.) – Affe Mar 24 '16 at 14:58
  • @user6109884 Although I see transactional annotations, none of them are creating a new transaction. They are all in same transaction. Atleast, looking at the posted code, I agree with Affe and I don't suspect this as a root cause for your perf issues. – Madhusudana Reddy Sunnapu Mar 24 '16 at 15:14
  • @Affe point is correct, you can start transaction at some point with REQUIRES_NEW and at next step you make sure by using MANDATORY so if there is another developer called nested method with no transaction he will know that he has to deal with transaction. Also at the moment I can not think of any use case but nested methods can have different RollBack levels. Otherwise I wouldnt use – HRgiger Mar 24 '16 at 16:35

1 Answers1

0

Given that the question is "how to clean up transaction annotations?" the answer would be - based on the above comments;

  1. Do not use transaction annotations in DAOs, only in Services (@Components)
  2. Make sure DAOs are only called through the service-layer.
jon martin solaas
  • 459
  • 1
  • 4
  • 14