设置 Hibernate 通过查看数据库来获取下一个键
我正在开发一个基于网络的应用程序来取代基于桌面的应用程序。我需要让它们都在同一个数据库上工作。对于基于 Web 的应用程序,我使用 GWT 和 Hiberate(使用 Gilead),在 Tomcat 7.0 上运行。 SQL 服务器是 MSSQL 2000。
我收到异常:
com.microsoft.sqlserver.jdbc.SQLServerException:违反主键约束“PK_CallLog”。无法在对象“CallLog”中插入重复的键。
要获得异常,我执行以下步骤:
- 使用旧应用程序添加通话记录
- 使用新应用程序添加通话记录(使用休眠)。
看来 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
我想尽可能避免更改数据库,我不想破坏旧的数据库应用。
I am developing a web based application to replace a desktop based one. I need to have them both work on the same database. For the web-based application I am using GWT and Hiberate(with Gilead), running on Tomcat 7.0. The SQL server is MSSQL 2000.
I am getting the exception:
com.microsoft.sqlserver.jdbc.SQLServerException: Violation of PRIMARY KEY constraint 'PK_CallLog'. Cannot insert duplicate key in object 'CallLog'.
To get the exception I do the following steps:
- Add a call record with the old application
- Add a call record with the new application (using hibernate).
It seems that hibernate is using its own cache and not looking at the database to figure out what the next primary key should be.
Is there a way to force hibernate to get the next key by looking at the database?
This is the mapping for the call record:
<?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>
This is the method I am using to add a new call:
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();
}
Schema for the call log:
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
I would like to avoid changing the database as much as possible, I don't want to break the older application.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
是的,增量生成器使用内部缓存,并且不适合在多个进程可以同时访问数据库时使用。
假设您无法更改数据库和旧应用程序,并且旧应用程序也使用类似于增量的 id 生成策略,则唯一可能的解决方案是使用改进的增量类似的策略。
有两种可能的选项:
在保存每个
调用
之前发出查询以手动确定最大 id 值并手动分配标识符看看
IncrementGenerator
并实现类似的而不缓存值请注意,如果多个事务在完全相同的时刻保存
Call
,这些策略仍然容易受到可能的 id 冲突的影响。如果您可以更改数据库和旧应用程序,请查看 dlamblin 的建议。
Yes,
increment
generator uses internal cache and is not intended to be used when multiple processes can access a database simultaneosly.Assuming that you cannot change the database and the legacy application, and that legacy application uses
increment
-like id generation strategy too, the only possible solution would be to use an improvedincrement
-like strategy.There are two possible options:
Issue a query to determine max id value manually before saving each
Call
and assign identifier manuallyTake a look at the
IncrementGenerator
and implement the similar one without caching the valueNote that these strategies are still vulnerable to possible id collision if multiple transactions save
Call
at exactly the same moment.If you can change the database and the legacy application, look at the dlamblin's suggestion.
您需要更改数据库的定义以自动递增该 id,然后告诉 hibernate 不要分配 id 本身并将其留给数据库。
hibernate 类:
或者使用此处记录的内容,让生成器查看数据库:
You will either need to change your database's definition to auto-increment that id, and then tell hibernate to not assign the id itself and leave that to the database.
hibernate class:
Or go with something like what's documented here for the generator to look at the database: