7

我的项目正在使用带有spring事务管理器的hibernate,我的数据库是postgres(可能无关紧要)。

我正在尝试读取大的 xml 文件并从中构造对象(对象不大,但数量很大)并将它们插入数据库。

如果碰巧我的一个对象违反了数据库约束,整个过程就会停止。如何跳过违反数据库约束的那些?或者将他们的 id 或其他任何内容记录到日志文件中?

问题更新:

我一直在浏览 SO,发现对于批量插入,最好建议使用无状态会话,但我仍然遇到同样的问题并且插入停止:

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 并插入数据库的代码的相关部分,为简单起见,假设我正在插入电影:

//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。为了避免这种情况,是否真的有必要在插入之前进行选择?

更新 2:

如果我使用StatelessSession而不是会话,我会得到大约 20 次插入,然后处理会无限期地停止,没有任何异常或任何东西。

我假设数字 20 是因为我正在与 tomcat 合并连接并拥有maxActive="20".

赏金更新:

我真的很想看到有人提供解决方案(如果可能,没有防御选择)。使用 statelessSession 或只是会话。

4

6 回答 6

4

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

对于某些事情,特别是独特的约束,您需要执行“防御性”选择以查看是否存在冲突,或者维护已插入的内存中的一组值。

于 2012-05-25T23:47:46.637 回答
4

我认为不可能完全验证某些东西以保证插入成功。在某些情况下,无论您做什么,其他人都可以在验证和插入之间将某些内容插入数据库,从而导致违反约束。

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

于 2012-06-04T05:12:10.427 回答
2

要从 xml 文件中插入大量对象,您应该考虑使用 spring batch。参数 skip-limit 允许您在停止批处理之前告诉您的 xml 中有多少错误行。还要检查 skip-policy 和 skippable-exception;请参阅Spring 文档中的配置步骤

如果您不想使用 spring 批处理,只需使用简单的 try catch 即可让您的过程继续到最后。

于 2012-05-25T23:54:54.350 回答
2

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

于 2012-06-01T02:53:12.757 回答
1

我认为Affe在插入前采用防御性选择的正确方法。

您可以考虑在每次插入之前使用保存点,如果抛出异常,则回滚到保存点。创建保存点会产生性能开销。完成后需要释放保存点。

请参阅AbstractTransactionStatus.setSavepoint()或者如果您有权访问 JDBC 连接池setSavepoint rollback(Savepoint)releaseSavepoint(Savepoint)

于 2012-06-01T00:14:02.283 回答
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();
}
于 2013-08-08T05:21:51.210 回答