1

Мне было любопытно, являются ли методы весеннего jparepository потокобезопасными, а затем я прочитал статью о стеке (Is a Spring Data (JPA) Repository thread-safe? (aka is SimpleJpaRepository thread safe)). Оттуда я понял, что методы репозитория являются потокобезопасными, а затем я сделал один POC для проверки безопасности потока. Я сделал один репозиторий, говоря, что FormRepository выполняет операции CRUD для объекта form, который расширяет JpaRepository. Из DAO я просто вызвал 100 потоков, создающих объект формы, и вручную установил свой идентификатор, а затем сохранил объект «форма».Что касается Spring JpaRepository метод безопасности потока

Ниже приведен код для справки: -

@Repository 
public interface FormRepository extends JpaRepository<Tbldynamicform, Long>  { 

Tbldynamicform save(Tbldynamicform tblform); 

@Query("SELECT max(tblform.formid) FROM Tbldynamicform tblform") 
Optional<Integer> findMaxId(); 

} 
......End of Repository above and start of DAO below... 

@Component 
public class DynamicFormDAO implements DynamicFormDAO { 

@Inject 
private FormRepository formRepository; 

public void testThreadSafety() throws Exception { 
    List<Callable<Integer>> tasks = new ArrayList<>(100); 
    for (int i = 0; i < 100; i++) { 

     tasks.add(() -> { 
      try { 

       Tbldynamicform tbldynamicform = new Tbldynamicform();//Set all the required fields for form 
       if (tbldynamicform.getFormid() == null) 
        tbldynamicform.setFormid(findFormID()); 
       Tbldynamicform form = formRepository.save(tbldynamicform); 
       return form.getFormid(); 
      } catch (Exception e) { 
       e.printStackTrace(); 
      } 
      return null; 
     }); 
    } 
    ExecutorService executor = Executors.newFixedThreadPool(100); 
    executor.invokeAll(tasks); 

} 

private int findFormID() throws Exception { 
    Optional<Integer> id = formRepository.findMaxId(); 
    if (id != null && id.isPresent() && id.get() != null) { 
     int generatedId = id.get().intValue(); 
     return ++generatedId; 
    } 
    return 0; 
} 
} 

Когда я делаю это, я предполагаю, что вещи должны работать хорошо, потому что методы формы хранилищ являются поточно, но каким-то образом я получаю SQL dataintegrityviolationexception несколько раз в журналах, в результате чего происходит сбой нескольких записей. Ниже приведена ошибка для справки: -

org.springframework.dao.DataIntegrityViolationException: не удалось выполнить инструкцию; SQL [n/a]; ограничение ["ПЕРВИЧНЫЙ КЛЮЧ НА PUBLIC.TBLDYNAMICFORM (FORMID)"; SQL заявление: вставки в Tbldynamicform (ClientID, copyfromexisting, CreationDate, formdesc, formmode, FormName, formtemplate, formtitle, procutype, статус, FormID) значений (,, ...

Это сделал?? мне кажется, что это проблема безопасности потоков или какой-то другой проблемы? По моему мнению, все объекты tbldynamicform, которые я создал в моем dao, будут оставаться в стеке стека. Только formRepository будет находиться в хранилище кучи и если методы formrepository являются потокобезопасными, 100 записей должны быть вставлены в базу данных без каких-либо проблем.

Если я делаю setId и сохраняю в синхронизированном блоке, все работает нормально, но это не мое намерение и не требуется, если методы репозитория являются потокобезопасными.

Эксперты, любая помощь, пожалуйста?

ответ

3

Ваша задача сохранения не является атомарной - два потока могут получить один и тот же максимальный идентификатор, прежде чем один из них сохранит новый объект.

И даже если метод сохранения в хранилище является потокобезопасным, он не поможет.

MaxId является потокобезопасным, сохранение является потокобезопасным, но ваш метод внутри runnable каждого потока не является потокобезопасным.

+0

Да, я тоже думал так же, потому что, поскольку я также задал свой вопрос, что если я поместил save и findformId в синхронизированный блок, все будет хорошо. –

0

Проблема возникает из-за того, как вы извлекаете последний идентификатор с помощью findFormID(), он не работает в параллельном контексте.

Что делать, если два потока задают ID одновременно? Они будут получать один и тот же идентификатор и создать два объекта с одинаковым идентификатором. вот ваша проблема.

Некоторые интегрированные решения для сгенерированных идентификаторов уже существуют, и вы не должны пытаться реализовать свои собственные, если не знаете, что делаете.

+0

«Что делать, если два потока задают ID одновременно» Поскольку findFormID синхронизирован, только один поток сможет получить идентификатор. Но проблема в том, что save и setformid не являются атомарными. –

1

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

Смежные вопросы