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);
}
}