I am preparing notification system for API which I've build before.
Basically I have an aspect which listens on projectRepository.save
method. What I want to achieve is check project status in an entity which is a parameter for save method with original status from database record. What I have notice is that when I search for the DB record by id it returns cached value so it is always the same as the object which is in save method even if database still have old value. Can I force Spring Data Jpa to return database record instead of cached entity?
@Aspect
@Component
@RequiredArgsConstructor
public class NotificationAspect {
private final UserService userService;
private final ProjectRepository projectRepository;
private final NotificationService notificationService;
@Pointcut("execution(* *com.stars.domain.project.ProjectRepository.save(..))")
public void projectSavePointcut() {}
@Before("projectSavePointcut()")
public void sendNotificationOnStatusChange(JoinPoint joinPoint) {
if(joinPoint.getArgs().length > 0 && joinPoint.getArgs()[0] instanceof Project) {
Project projectToUpdate = (Project) joinPoint.getArgs()[0];
Optional<Project> oldProject = projectRepository.findById(projectToUpdate.getProjectId());
if(oldProject.isPresent() && !oldProject.get().getStatus().equals(projectToUpdate.getStatus())) {
notificationService.saveNotification(
MessageFormat.format("Project: {} status has been changed from: {} to: {}",
projectToUpdate.getName(),
oldProject.get().getStatus(),
projectToUpdate.getStatus()),
List.of(userService.getUser(projectToUpdate.getCreatedBy())));
}
}
}
}
This line always returns true even if database record has different value.
oldProject.get().getStatus().equals(projectToUpdate.getStatus())
CodePudding user response:
I can think of two ways.
First, if you're interested only in status
field, you can create a custom native query in a repository, which will bypass EntityManager
, for example like this:
@Query("SELECT p.status FROM projects p WHERE p.id = :id", nativeQuery = true)
String getProjectStatusById(@Param("id") String projectId);
Second looks like a bad idea, but it should work - you can make the entity manager's cache detach all managed entities, so it will be forced to make a DB call again.
For this inject EntityManager
in your aspect bean and call its .clear()
method right before calling projectRepository.findById
method.