Настройка Hibernate для получения следующего ключа путем просмотра базы данных

Я разрабатываю веб-приложение для замены настольного. Мне нужно, чтобы они оба работали в одной базе данных. Для веб-приложения я использую GWT и Hiberate (с Gilead), работающие на Tomcat 7.0. Сервер SQL - MSSQL 2000.

Я получаю исключение:

com.microsoft.sqlserver.jdbc.SQLServerException: нарушение ограничения PRIMARY KEY «PK_CallLog». Не удается вставить повторяющийся ключ в объект «CallLog».

Чтобы получить исключение, я делаю следующие шаги:

  1. Добавить запись звонка со старым приложением
  2. Добавьте запись звонка с новым приложением (используя спящий режим).

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

Это сопоставление для записи вызова:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hib.....dtd">

<hibernate-mapping>
    <class name="com.asi.shared.Call" table="CallLog">
        <id name="id" column="callid">
            <generator class="increment"/>
        </id>        
        <property name="caller"/>
        <property name="callDate" column="calldate"/>
        .... other props ....
        <property name="checkOut" column="checkout"/>
        <many-to-one name="customer" class="com.asi.shared.Customer"
                     column="customerid" not-found="ignore"/>
    </class>
</hibernate-mapping>

Это метод, который я использую для добавления нового вызова:

public Integer saveCall(Call call){
    DebugLog.print("HelpDeskServiceImpl.saveCall(call)");
    Session session = gileadHibernateUtil.getSessionFactory().getCurrentSession();
    session.beginTransaction();
    check(session);
    session.saveOrUpdate(call);
    session.getTransaction().commit();
    return call.getId();
}

Схема журнала звонков:

if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[CallLog]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)
drop table [dbo].[CallLog]
GO

CREATE TABLE [dbo].[CallLog] (
    [callid] [int] NOT NULL ,
    [caller] [varchar] (50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
    [calldate] [datetime] NULL ,
    .... other columns ....
    [checkout] [varchar] (5) COLLATE SQL_Latin1_General_CP1_CI_AS NULL 
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO

Я хотел бы максимально избегать изменения базы данных, я не хочу ломать старое приложение.


person Angel    schedule 17.11.2011    source источник


Ответы (2)


Да, генератор increment использует внутренний кеш и не предназначен для использования, когда несколько процессов могут одновременно обращаться к базе данных.

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

Возможны два варианта:

  • Выполните запрос для определения максимального значения идентификатора вручную перед сохранением каждого Call и назначьте идентификатор вручную.

  • Взгляните на IncrementGenerator и реализовать аналогичный без кэширования значения

Обратите внимание, что эти стратегии по-прежнему уязвимы для возможного конфликта идентификаторов, если несколько транзакций сохраняют Call в один и тот же момент.

Если вы можете изменить базу данных и устаревшее приложение, посмотрите на предложение dlamblin.

person axtavt    schedule 17.11.2011
comment
Я выбрал первую стратегию. Его будут использовать максимум около 20 человек, поэтому я не думаю, что это будет проблемой, и в конечном итоге устаревшее приложение будет прекращено, поэтому я верну его обратно. Спасибо за помощь! - person Angel; 18.11.2011

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

[callid] [int] NOT NULL IDENTITY(1, 1), % or (10000, 1) or use the highest id.

спящий класс:

@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private int callid;

Или используйте что-то вроде того, что задокументировано здесь, чтобы генератор просматривал базу данных:

    <id name="id" column="callid">
        <generator class="hilo">
          <param name="table">CallLog</param>
          <param name="column">callid</param>
        </generator>
    </id>        
person dlamblin    schedule 17.11.2011
comment
Я пытался использовать hilo, но он генерирует 11-значное отрицательное число, а не просто берет следующий самый высокий идентификатор. Все равно, спасибо за помощь. - person Angel; 18.11.2011
comment
@Angel Тогда sequence или native могли быть тем, что вы ищете. Он выбирает уникальный идентификатор, который не будет выбирать устаревшее приложение. Важно ли значение идентификационного номера? Ожидаете ли вы, что сможете заказывать звонки по id? - person dlamblin; 18.11.2011