I see 'strange' behavior of JPA while retrieving entity from one thread and while its modified in another. Following is my sample code to explain the issue. Typical scenario:
- create a student s1
- get/print student details for s1 in loop
- update s1 details
- check whether updated student details are printed in loop
- It still prints old data
As I understand that JPA has persistence context(PC) associated with each thread and hence stores entities in it. Thread's can't see each other's PC. I think PC gets updated if corresponding thread does create/update operation on the entity. I think this behavior of JPA breaks database isolation principle. Shouldn't GET be returning updated data out of the box ? OR Am I missing anything here ?
Log
2021-05-28 07:13:03.503 INFO 7425 --- [nio-8080-exec-2] com.example.demo.StudentController : Created student... Student{id=1, name='Omkar', score=50}
2021-05-28 07:14:22.272 INFO 7425 --- [nio-8080-exec-4] com.example.demo.StudentController : Get student Student{id=1, name='Omkar', score=50}
2021-05-28 07:14:27.280 INFO 7425 --- [nio-8080-exec-4] com.example.demo.StudentController : Get student Student{id=1, name='Omkar', score=50}
2021-05-28 07:14:32.282 INFO 7425 --- [nio-8080-exec-4] com.example.demo.StudentController : Get student Student{id=1, name='Omkar', score=50}
2021-05-28 07:14:33.748 INFO 7425 --- [nio-8080-exec-5] com.example.demo.StudentController : Updating student... Student{id=1, name='Omkar', score=75}
2021-05-28 07:14:37.284 INFO 7425 --- [nio-8080-exec-4] com.example.demo.StudentController : Get student Student{id=1, name='Omkar', score=50}
2021-05-28 07:14:42.288 INFO 7425 --- [nio-8080-exec-4] com.example.demo.StudentController : Get student Student{id=1, name='Omkar', score=50}
Entity
@Entity
public class Student {
@Id
private int id;
private String name;
private int score;
...
Repository
public interface StudentRepository extends JpaRepository<Student, Integer> {
}
Controller
@RestController
@RequestMapping("/api/student")
public class StudentController {
@Autowired
private StudentRepository studentRepository;
private final static Logger LOGGER = LoggerFactory.getLogger(StudentController.class);
@GetMapping
public void getStudent() {
while (true) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Student student = studentRepository.findById(1).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
LOGGER.info("Get student {}",student.toString());
}
}
@PostMapping
public ResponseEntity<Student> updateStudent(@RequestParam("score") int score) {
Student student = studentRepository.findById(1).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
student.setScore(score);
Student updated = studentRepository.save(student);
LOGGER.info("Updating student... {}", updated);
return ResponseEntity.ok(updated);
}
@PutMapping
public ResponseEntity<Student> createStudent(Student student) {
Student saved = studentRepository.save(student);
LOGGER.info("Created student... {}", saved);
return ResponseEntity.created(URI.create("/api/student/" + saved.getId())).body(saved);
}
}