2013-03-04 3 views
0

Ситуация:Hibernate параллелизм проблема при чтении/обновлении

У нас есть веб-приложение (Java EE-Spring-Hibernate), который генерирует PDF со штрих-кодом. Штрих-код содержит идентификатор, который создается во время выполнения. У нас есть служба Java, которая использует Hibernate для проверки наличия идентификатора для текущей даты, если он не существует, создается запись. Если он существует, он увеличивается и обновляется. Существует одна строка в день, содержащая следующие поля: id, coverPageId, date

Итак, когда пользователь создает первый штрих-код дня, строка добавляется к текущей дате с помощью coverPageId = 1. Затем все последующие штрих-коды получат последний идентификатор из базы данных, увеличивают его и сохраняют строку с добавочным значением.

Проблема:

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

Пытались Решение:

Код, который выбирает текущий идентификатор, увеличивает его и обновляет строку это все выполняется тем же способом. Это метод внутри компонента Spring. Так как весенние бобы - это синглеты, я попытался сделать метод synchronized. Это не помогло.

Метод:

@Transactional(isolation=Isolation.SERIALIZABLE) 
public synchronized long getNextId(Date date) 
{ 
    List<CoverpageID> allCoverpageIds = this.getAllCurrentIds(date); 

    if(allCoverpageIds.size() == 0) 
    { 
     loggingService.warn(String.format("Found no existing ID for date '%tD', creating new record", date)); 
     System.out.println(String.format("Found no existing ID for date '%tD', creating new record", date)); 
     return this.createNewCoverpageIdRecord(new Date()); 
    } 
    else if(allCoverpageIds.size() == 1) 
    { 
     CoverpageID coverpageId = allCoverpageIds.get(0); 
     loggingService.debug(String.format("Found existing ID for date '%tD': %d, incrementing and persisting", date, coverpageId.getCoverPageId())); 
     System.out.println(String.format("Found existing ID for date '%tD': %d, incrementing and persisting", date, coverpageId.getCoverPageId())); 
     coverpageId.setCoverPageId(coverpageId.getCoverPageId() + 1); 
     dao.save(coverpageId); 
     loggingService.debug(String.format("Saved existing ID for date '%tD' with incremented ID: %d", date, coverpageId.getCoverPageId())); 
     return coverpageId.getCoverPageId(); 
    } 
    else 
    { 
     loggingService.warn(String.format("Found multiple records for date '%tD'", date)); 
     return -1; 
    } 
} 

private List<CoverpageID> getAllCurrentIds(Date date) 
{ 
    String exClause = "where date = :date"; 
    Map<String, Object> values = new HashMap<String, Object>(); 
    values.put("date", date); 
    return dao.getAllEx(CoverpageID.class, exClause, values); 
} 

private long createNewCoverpageIdRecord(Date date) 
{ 
    dao.save(new CoverpageID(new Date(), new Long(1))); 
    return 1; 
} 

CoverpageID.class (объект для штрих-кода):

@NamedQueries({ 
@NamedQuery(
    name = "getCoverPageIdForDate", 
    query = "from CoverpageID c where c.date = :date", 
    readOnly = true 
)}) 
@Entity 
public class CoverpageID 
{ 
private Long id; 
private Date date; 
private long coverPageId; 

public CoverpageID() {} 

public CoverpageID(Date date, Long coverPageId) 
{ 
    this.date = date; 
    this.coverPageId = coverPageId; 
} 

public void setId(Long id) 
{ 
    this.id = id; 
} 

@Id 
@GeneratedValue(strategy=GenerationType.AUTO) 
public Long getId() 
{ 
    return id; 
} 

@Temporal(TemporalType.DATE) 
public Date getDate() 
{ 
    return date; 
} 

public void setDate(Date date) 
{ 
    this.date = date; 
} 

public long getCoverPageId() 
{ 
    return coverPageId; 
} 

public void setCoverPageId(long coverPageId) 
{ 
    this.coverPageId = coverPageId; 
} 
} 

Кто-нибудь есть какие-либо другие идеи, как мы можем предотвратить эту проблему параллелизма случилось?

ответ

1

Я считаю, что это не имеет ничего общего с Hibernate. Даже если вы не используете Hibernate, вы все равно столкнетесь с такой проблемой.

Есть несколько вариантов для вас:

Рассматривает не используя Hibernate для связанных частей. Многие БД имеют возможности выполнять атомарную логику «вставить-если-отсутствует-обновить-если-существует».

ИЛИ

Если вы действительно хотите использовать спящий режим, вот что вам нужно:

  1. Убедитесь, что уникальное ограничение в БД, чтобы избежать дублирующихся записей
  2. В вашей логике программы, если вы обнаружили, что нет записи и попробуйте вставить, подготовьтесь к исключению дублирования данных/ограничений. В таком случае не считайте свою логику неудачной. Сделайте следующее обновление.

Что касается вашего попытался решения, используя экземпляр объекта для обработки действий вставки/обновления, ваш путь не будет работать.

Во-первых, если вы фактически сохраняете все идентификаторы в качестве состояния этого компонента, и если ваше приложение будет иметь только процесс, тогда оно будет работать. Ваш код будет в БД проверяться каждый раз, и соответственно вставлять/обновлять. Даже этот фрагмент кода синхронизирован, он не будет работать.Помните, что в разных сеансах БД изменения одного сеанса видимы только для других, когда транзакция действительно совершена. Таким образом, у вас будет такая ситуация

THREAD 1     THREAD 2 
------------------------------------------------- 
enter method 
check DB, no record found enter method (wait coz synchronized) 
insert record 
exit method 
          check DB, no record found (coz prev txn not commited yet) 
          insert record 
          exit method 
commit 
          commit 

См.? Вы все еще сталкиваетесь с той же проблемой.

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

+0

Даже если это не ошибка Hibernate, мой синхронизированный метод должен делать трюк, не так ли? Один экземпляр объекта (singleton), поэтому все вызовы проходят один и тот же метод объекта. И синхронизированный должен заставить всех абонентов ждать, если он обрабатывает звонок уже ... –

+0

Это не поможет. Я собираюсь обновить свой ответ. –

+0

И если я совершу транзакцию INSIDE, метод? –

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