我是NHibernate的初学者。我正在使用NHibernate 3.3和C Sharp。我将数据从文件插入到mysql数据库。我的文件有重复的条目。我有一个PERSON表,其主键为id,并自动递增为:
id | firstname | lastname | fullname | dob | city
现在我想避免重复保存到数据库,并希望从数据库中获取原始条目的id。
例如: 文件中有4条记录:
firstname|lastname|fullname|dob|city
-------------------------------------------
a|b|ab|1|c
c|d|cd|2|e
a|b|ab|1|c
f|g|fg|4|h
现在假设我已经使用NHibernate 3.3保存了前两个记录。人员表格如下:
id|firstname|lastname|fullname|dob|city
------------------------------------------
1|a|b|ab|1|c
2|c|d|cd|2|e
在我当前的实现中,与文件的第1行相同的第3行正在保存在表中。我不想这样做。我想避免在表格中重复输入。我也不想每次都执行查询来检查以前是否存在于数据库中的记录。此外,如果这样的重复条目已经在表中,我想填充其ID。对于这种情况,它应该是1.
如果有人可能建议我解决这个问题,那真的会很棒。
答案 0 :(得分:2)
您应该在字段上创建唯一索引:
CREATE UNIQUE INDEX "PERSON_idx" ON "PERSON"
USING btree (firstname, lastname, fullname, dob, city);
然后,每当您尝试插入一行,这些字段等于某个已存在的行时,数据库服务器将引发异常。
要获取冲突行的ID,在某些情况下,解析从数据库返回的错误文本是可行的,但我建议只在发生这种情况时从数据库中检索记录。
现在可能有两条通用路径来解决获取冲突行的问题。特别是,使用NHibernate的session.Save
不一定会因违反唯一约束而抛出异常,因为在提交事务之前可能不会发出INSERTs
。在这种情况下,很难猜出哪些行导致了错误(没有查看数据库日志)。
使用INSERTs
代替IStatelessSession
可以解决延迟ISession
的此问题,这将使INSERTs
立即发布(AFAIK)。然后就可以有这样的东西:
using (var tx = statelessSession.BeginTransaction())
{
foreach (var person in persons)
{
try
{
statelessSession.Insert(person);
}
catch (GenericADONetException e)
{
// Further check that it's really caused by violating the unique constraing (database-specific) and handling the situation
}
}
tx.Commit();
}
如果您需要使用ISession
或由于其他原因不喜欢此解决方案,您可以在插入行之前通过发出单个SELECT
来获取所有重复项:
var conflictingRows = session.Query<Person>().Where(p =>
(p.FirstName == persons[0].FirstName && p.LastName == persons[0].LastName && ...)
|| (p.FirstName == persons[1].FirstName && p.LastName == persons[1].LastName && ...)
...
|| (p.FirstName == persons[persons.Count - 1].FirstName && p.LastName == persons[persons.Count - 1].LastName && ...));
然后,可以在内存中轻松获取与要插入的记录对应的ID。你必须动态地构建这个表达式,但那是not a big deal either。
PS:由于您要求所有字段的组合都是唯一的,因此您可以完全删除id
列,并将所有字段用作复合主键。