Настройка Hibernate для получения следующего ключа, глядя на базу данных - PullRequest
1 голос
/ 17 ноября 2011

Я занимаюсь разработкой веб-приложения для замены настольного. Мне нужно, чтобы они оба работали в одной базе данных. Для веб-приложения я использую 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

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

Ответы [ 2 ]

0 голосов
/ 17 ноября 2011

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

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

Есть два возможных варианта:

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

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

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

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

0 голосов
/ 17 ноября 2011

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

[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>        
...