7

我的项目使用hibernate与spring事务管理器和我的数据库是postgres(可能无关紧要)。处理数据库约束hibernate

我正在尝试读取大的XML文件,并构造出那些对象(对象不是很大,但金额是),并将它们插入到数据库中。

如果有机会,我的某个对象违反了数据库约束,整个过程就会停止。我怎样才能跳过那些违反数据库约束的?或者将他们的ID或其他日志记录到日志文件中?

问题更新:

我一直很浏览槽和发现,对于批量插入它的最好建议使用无状态会话,但我仍然得到同样的问题,插入停止:

May 26, 2012 4:45:47 PM org.hibernate.util.JDBCExceptionReporter logExceptions 
SEVERE: ERROR: duplicate key value violates unique constraint "UN_FK" 
    Detail: Key (fid)=(H1) already exists. 

下面是我的代码的相关部分,用于解析xml并插入到db中,为了简单起见,我们假设我正在插入电影:

//class field 
@Autowired 
private SessionFactory sessionFactory; 

@Override 
public void startDocument() throws SAXException { 
    session = sessionFactory.getCurrentSession(); 
} 

@Override 
public void endElement(String uri, String localName, String qName) throws SAXException { 
if (qName.equalsIgnoreCase("FILM")) { 
     movie.setCategory(category); 
     movie.setAdded(new Date()); 
     session.insert(movie); 
    } 
} 

我将这个属性设置为app-ctx hibernate.jdbc.batch_size为100.为了避免这种情况,在插入之前确实需要做select?

更新2:

如果我使用StatelessSession而不是会议上,我得到20个以防万一刀片和比无限期地处理停止无任何异常或任何东西。

我认为数字20是因为我与tomcat共享连接并且有maxActive="20"

赏金更新:

我真的很想看到有人提供的解决方案(不选择防守如果可能的话)。使用无状态会话或只是会话。

回答

4

大多数类型的约束条件,例如,如果某列可以为空或具有最大宽度,则可以使用Hibernate Validator进行检查。在尝试持久化之前,只需手动对对象执行验证即可。

对于某些事情,特别是独特的约束,您需要执行'defensive'select来查看是否存在冲突,或者维护一组已插入的内存值。

2

要从xml文件插入大量对象,应该考虑使用spring批处理。 参数skip-limit允许您在停止批处理之前,告知xml中有多少错误行。 也检查skip-policy和skippable-exception;参见Spring文档中的参考文献Configuring a Step

如果你不想使用spring批处理,只需使用一个简单的try catch,它将允许你的进程继续下去直到结束。

+0

一个简单的try catch语句不会做。一旦Hibernate抛出异常,会话状态就不一致,必须回滚事务,并关闭会话。而且,只有在错误记录持续存在很久之后,异常才会在刷新时被抛出。 –

+0

这就是为什么建议在这种情况下使用无状态Hibernate会话的原因。它可以防止不连贯的状态,并减少内存消耗(或者不必驱逐已处理的实体) –

2

你觉得为什么这一切都必须是一个大的交易?事实上,你所描述的一切都意味着你实际上在这里有很多交易。对于“出错”的对象,只需驱逐该实体并回滚事务。如果“FILM”元素定义了一个对象图,它会变得更加复杂一些,但这个想法是一样的。

4

我认为不可能完全验证某些东西,以保证插入成功。在某些情况下,不管你做什么,其他人都可以在验证和插入之间插入一些东西到数据库中,导致违反约束。

在大多数情况下,我只是推荐像处理其他任何异常一样处理异常。

0

虽然这是一个老问题,但我最近面临类似的情况,这就是我所做的。

我也使用了无状态会话,但这是我做了不同的事情。我发出了一个session.insert,它由于约束违反而成功或失败,违反了约束条件违反了ConstraintViolationException,引发它会给你插入失败的对象,做任何你想对失败对象所做的事情。除了事务处理还为每个插入使用了保存点(保存点更便宜并且不会对性能造成太大影响),所以当事务由于某些问题而失败时,直到最后一个保存点(在catch子句中)才执行提交。

我的代码看起来是这样的:

try { 
    session.connection().setSavePoint("lastSaved"); 
    session.insert(obj); 
} 
catch(ConstraintViolationException) { 
    log.error(obj.getUserId()); 
    .... 
} 
tx.commit(); 
.... 
catch(TransactionException e) { 
    tx.rollback(); 
}