Настройване на 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-подобна стратегия.

Има два възможни варианта:

  • Издайте заявка, за да определите ръчно максималната стойност на id, преди да запазите всеки Call и да зададете идентификатор ръчно

  • Разгледайте IncrementGenerator и имплементирайте подобен, без да кеширате стойността

Обърнете внимание, че тези стратегии все още са уязвими за възможен сблъсък на идентификатори, ако множество транзакции запазят Call точно в един и същи момент.

Ако можете да промените базата данни и наследеното приложение, вижте предложението на dlamblin.

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

Ще трябва или да промените дефиницията на вашата база данни за автоматично увеличаване на този id, и след това да кажете на hibernate да не присвоява самото id и да остави това на базата данни.

[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