ThreadLocal состояние гонки с - PullRequest
       13

ThreadLocal состояние гонки с

0 голосов
/ 05 ноября 2019

У меня небольшая проблема с состоянием гонки.

public class MyClass1 {
    static ThreadLocal<MyInterface> threads = new ThreadLocal();

    static MyInterface getThread(){
       return threads.get();
    }    

    static String saveId(String name){
       return getThread.saveId());
    }    
}

public interface MyInterface {
     String saveId(String name);
}

public class MyClass2 implements MyInterface {

    MyDao dao;

    public String saveId(String name){

    Session s= dao.getCurrentSession();
    String id =  (String)s.save(new MyObject(name));
    return id;

   }

}

И моя проблема, как лучше выбрать исключение состояния гонки, когда я пытаюсь сохранить Объект в БД, когда я использую singleton и hibernate?

Я думаю, что это правильное решение

private volatile String id;

public synchronized String saveId(String name){

        Session s= dao.getCurrentSession();
        id =  (String)s.save(new MyObject(name));
        return id;   
   }

Я не прав?

PS Мое второе предположение о коде:

    public String saveId(String name){

    Session s= dao.getCurrentSession();
    String id = s.createQuery("select id from MyObject where name= :name order by id desc").setParameter("name", name).uniqueResult();

    if (!StringUtils.isEmpty(id)) {
     id = makeId(name);
    }

    return id; 
   }

 public String makeId(String name) {

   String id = s.createQuery("select id from MyObject where name= :name order by id desc").setParameter("name", name).uniqueResult();

        if (!StringUtils.isEmpty(id)) {

            synchronized (MyClass2.class){
            id =  (String)s.save(new MyObject(name));
            s.flush();
            }
        } 
     return id;
}

1 Ответ

2 голосов
/ 05 ноября 2019

Использование ThreadLocal или syncronized не поможет создать 100% твердый раствор. Есть только 3 надежных способа сделать это

  1. Наличие уникального ограничения на базу данных и ID чтения при ошибке
  2. Выполнение UPSERT вместо INSERT (в основном происходит ОБНОВЛЕНИЕ при сбое INSERT) и вернуть идентификатор. Требуется дополнительный SELECT для идентификатора.
  3. Выполнение INSERT с предложением WHERE для предотвращения INSERT имени уже существует. Требуется дополнительный SELECT для идентификатора.

Второе и третье возможно только с простым SQL-запросом. Второе зависит от поддержки вашей базы данных. Первый вариант всегда должен работать.

Уникальное ограничение на БД

  1. Измените вашу таблицу ALTER TABLE MyObject ADD CONSTRAINT UQ_MYOBJECT_NAME UNIQUE(name);
  2. Тогда ваш метод должен выполнить save и при ошибке прочитать строку из БД.

    public String saveId(String name){
      Session s= dao.getCurrentSession();
      try {
        return (String) s.save(new MyObject(name));
      } catch (PersistenceException pe) {
        return s.createQuery("SELECT x FROM MyObject X where x.name=:name").setParameter("name", name).getSingleResult().getId(); 
      }
    }
    
  3. Вам не нужны вещи ThreadLocal, так как в вашем методе saveId нет ничего общего между потоками, и вы можете безопасно повторно использовать один экземпляр между потоками (у каждого будет свой Session и т. д.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...