Hibernate 有时JPA存储库不会';不返回最新数据?
当从一个线程检索实体并在另一个线程中修改实体时,我看到了JPA的“奇怪”行为。 下面是我解释这个问题的示例代码。 典型情况:Hibernate 有时JPA存储库不会';不返回最新数据?,hibernate,jpa,spring-data-jpa,Hibernate,Jpa,Spring Data Jpa,当从一个线程检索实体并在另一个线程中修改实体时,我看到了JPA的“奇怪”行为。 下面是我解释这个问题的示例代码。 典型情况: 创建一个学生s1 获取/打印循环中s1的学生详细信息 更新s1详细信息 检查是否在循环中打印更新的学生详细信息 它仍然打印旧数据 据我所知,JPA具有与每个线程关联的持久性上下文(PC),因此在其中存储实体。线程看不到彼此的PC。我认为如果相应的线程在实体上创建/更新操作,PC会得到更新。 我认为JPA的这种行为打破了数据库隔离原则。GET不应该直接返回更新的数
- 创建一个学生s1
- 获取/打印循环中s1的学生详细信息
- 更新s1详细信息
- 检查是否在循环中打印更新的学生详细信息
- 它仍然打印旧数据
@Entity
public class Student {
@Id
private int id;
private String name;
private int score;
...
存储库
public interface StudentRepository extends JpaRepository<Student, Integer> {
}
public interface StudentRepository扩展了JpaRepository{
}
控制器
@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);
}
}
@RestController
@请求映射(“/api/student”)
公共班级学生控制员{
@自动连线
私立学校;私立学校;
私有最终静态记录器Logger=LoggerFactory.getLogger(StudentController.class);
@GetMapping
公众假期学生(){
while(true){
试一试{
睡眠(5000);
}捕捉(中断异常e){
e、 printStackTrace();
}
Student Student=studentRepository.findById(1).OrelsThrow(()->新的ResponseStatusException(HttpStatus.NOT_FOUND));
LOGGER.info(“获取学生{}”,student.toString());
}
}
@邮戳
公共响应性更新学生(@RequestParam(“score”)int score){
Student Student=studentRepository.findById(1).OrelsThrow(()->新的ResponseStatusException(HttpStatus.NOT_FOUND));
学生成绩(分数);
学生更新=studentRepository.save(学生);
info(“更新学生…{}”,已更新);
返回响应正确(已更新);
}
@PutMapping
公共响应创建学生(学生){
保存的学生=studentRepository.save(学生);
info(“已创建学生…{}”,已保存);
返回ResponseEntity.created(URI.create(“/api/student/”+saved.getId()).body(saved);
}
}
将循环更改为此
@GetMapping
public void getStudent() {
while (true) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
entityManager.clear();
Student student = studentRepository.findById(1).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
LOGGER.info("Get student {}",student.toString());
}
}
基本上,findById的结果被附加到当前事务,查询只被调用一次(即使它在循环中)。要再次命中数据库,应分离实体。您可以通过调用entityManager.clear()来分离实体。您可以在更新之前添加@Transactional并查看发生了什么。无更改。我认为@Transactional将处理是否提交/回滚工作单元代码>,我认为JPA使代码变得复杂且无法维护。另外,如果不使用
flush()
,则clear()
可能会导致随机错误。我在这个问题上的观点更多的是JPA行为的“为什么”。JPA是数据库和应用程序之间的中介。我认为它应该反映DB的现状。
@GetMapping
public void getStudent() {
while (true) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
entityManager.clear();
Student student = studentRepository.findById(1).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
LOGGER.info("Get student {}",student.toString());
}
}