Как правильно использовать Spring Data Jpa и гибернацию в многопоточной среде?

#java #multithreading #hibernate #spring-data-jpa

#java #многопоточность #гибернация #spring-data-jpa

Вопрос:

Я использую Spring Data Jpa с гибернацией. При многопоточности у меня есть синхронизированный блок, который сохранит некоторые объекты. Если объект не существует, создайте вместо него новый.

 synchronized block
Student student = studentRepo.findByName(name); //student do have unique constraints on db
if(student==null){
  student = new Student();
  student.setName(name);
  student = studentRepo.save(student);
}
//outsize sync block
//logic to handle students
  

Поведение, которое я хочу иметь, заключается в том, что когда поток 1 создал ученика, поток 2 должен иметь возможность находить созданного ученика вместо создания дублированного ученика с тем же именем, что в результате приведет к уникальному нарушению.

Я знаю studentRepo.save , что объект student не правильно фиксирует объект student в db, но разве этот объект не будет сохранен в памяти, а Hibernate сначала выполнит поиск в памяти, а затем, если он не найден, он перейдет в db? Я тоже пробовал saveAndFlush , он также получает то же поведение с ошибками. Реальный объект, который я использую, более сложный, он соединяется с другими.

Поправьте меня, если я ошибаюсь. Теперь я просто хочу знать, как сделать так, чтобы сохраненный студент был доступен между разными потоками, чтобы мой код не пытался вставить дублирующиеся данные в базу данных.

Поток 1 завершил сохранение и выводит блок синхронизации, затем поток 2 вводит блок, когда выполняется поток 2 findByName , я хочу, чтобы поток 2 получил созданного ученика.

Есть ли какие-либо дополнительные настройки, которые мне нужно сделать, чтобы добиться такого поведения? Я использую Spring boot, все по умолчанию, только что настроенный источник данных. Также приведенный выше код имеет аннотацию @Bean singleton, и этот класс обрабатывает только бизнес-логику.

Комментарии:

1. Второй поток не увидит сохраненного ученика до тех пор, пока транзакция не будет зафиксирована (если ваши транзакции используют обычно уровень изоляции READ_COMMITTED по умолчанию). Использование синхронизированного блока в любом случае неправильно, поскольку серверные приложения обычно выполняются на нескольких серверах. Просто сделайте транзакцию как можно короче, перехватите исключение нарушения ограничений и повторите транзакцию или сообщите о проблеме пользователю, который, таким образом, повторит попытку.

2. Я надеюсь, что это упрощенный пример, потому что уникальное ограничение на имя учащегося действительно неверно. В том же классе у меня был ученик с тем же именем, что и у меня, и мое имя не является обычным.

3. это всего лишь пример кода, который я составил, просто чтобы показать в качестве примера. Даже если я попытаюсь сделать транзакцию короткой, она все равно получит то же исключение, saveAndFlush правильно зафиксирует изменения в db. Что касается самой изоляции, какой из них предпочтительнее использовать в моем случае? Также распространение?

4. saveAndFlush НЕ будет передавать ваши данные в базу данных. Он будет выполнять операции записи, но данные будут зафиксированы только в конце транзакции.

5. Наоборот, фиксация сохранит и сбросит 🙂