2010-08-12 50 views
3

我有一个程序逐行读取文本文件,并从每行创建一个Hibernate实体对象,并将它们保存。我有几个这样的文本文件要处理,每个文件都有大约30万行。我发现我目前的执行速度非常慢,而且我想知道是否有任何事情可以改进。快速批量保存Hibernate的方法?

我的主要方法,通过线处理文本文件行,像这样:

// read the file line by line 
FileInputStream fileInputStream = new FileInputStream(new File(fileName)); 
InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream); 
BufferedReader bufferedReader = new BufferedReader(inputStreamReader); 
int lineCount = 0; 
String line = bufferedReader.readLine(); 
while (line != null) 
{ 
    // convert the line into an Observations object and persist it 
    convertAndPersistObservationsLine(line); 

    // if the number of lines we've processed has built up to the JDBC batch size then flush 
    // and clear the session in order to control the size of Hibernate's first level cache 
    lineCount++; 
    if (lineCount % JDBC_CACHE_SIZE == 0) 
    { 
     observationsDao.flush(); 
     observationsDao.clear(); 
    } 

    line = bufferedReader.readLine(); 
} 

的convertAndPersistObservationsLine()方法只是拆分文本行成标记,创建一个新的实体对象,从数据填充实体的领域令牌,然后通过调用Hibernate的Session.saveOrUpdate()方法的DAO保存对象。 DAO方法flush()和clear()是直接调用相应的Hibernate Session方法。

Hibernate属性'hibernate.use_second_level_cache'设置为false,并且Hibernate属性'hibernate.jdbc.batch_size'设置为50,Java常量JDBC_CACHE_SIZE也是如此。

有人可以提出一个更好的方法来解决这个问题,或者对上面的任何调整都可以提高这个批量加载程序的性能吗?

在此先感谢您的帮助。

- 詹姆斯

+2

你有正确设置交易,没有自动提交等?你的dao不会意外刷新会话持续吗?你能分析代码,看看大部分时间都花在哪里吗?你能否启用sql日志来验证没有中间刷新? – 2010-08-12 16:31:50

+0

是否最好将Hibernate属性'hibernate.connection.autocommit'设置为false(默认情况下该值为true)?我的DAO不会调用flush(),只会在返回前调用saveOrUpdate()。我还没有分析代码,也没有监视中间冲洗的日志,感谢这些建议。 – 2010-08-12 16:47:19

+0

亚当我建议看一下由hibernate和spring生成的日志,以查看插入过程中发生了什么;我认为无论是Spring还是Hibernate都会在事务中禁用自动提交(使设置无关紧要,但您应该确认)。 – 2010-08-12 18:10:36

回答

3

有几件事情:

  1. 你可以量化 “速度奇慢”?你实现每秒插入多少个插入点?你认为你应该有什么比率?数据库本身是什么类型的负载?其他人是否同时从桌上读书?

  2. 如何连接到数据库?所有这一切发生在单个事务中重新使用相同的连接?

  3. 您是否有机会使用identity标识符?本细则规定,JDBC batching is disabled silently if you are

,Hibernate在JDBC级别的关闭插入配料透明的,如果你使用的身份标识符生成。

+0

感谢您的回应,马特。 1.它看起来像我每秒大约4插入。我能期望大幅改善这一点吗? 2.我的DAO对象包含一个SessionFactory对象,它通过Spring依赖注入进行连接。我使用的SessionFactory类是org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean。我使用的事务管理器类是org.springframework.orm.hibernate3.HibernateTransactionManager,我在Spring应用程序上下文配置文件中启用了基于注释的事务行为。 – 2010-08-12 16:27:55

+0

DAO类将此作为事务性设置: @Transactional(propagation = Propagation.REQUIRED,readOnly = false,isolation = Isolation.DEFAULT) SessionFactory具有DataSource属性,并且这是在Spring应用程序上下文中配置的URL,用户/密码,驱动程序类的正常设置等。 3.我认为我没有使用身份标识符,但实体类的ID字段如下所示: @Id @Column(name =“ENTITY_ID”) @GeneratedValue(strategy = GenerationType.AUTO) public T getId() { return id; } – 2010-08-12 16:33:39

+0

对于没有格式化上述评论,我表示抱歉 - 我尝试使用基本的HTML和Markdown,但从未能够获取任何内容,并始终以一个运行字符串结尾。 – 2010-08-12 16:39:53

8

代码本身和Hibernate配置看起来是正确的(正确的我的意思是它们遵循文档中的batch insert习语)。但是,这里有一些额外的建议:

如前所述,使绝对确定您没有使用ID生成器,如IDENTITY击败批处理。当使用GenerationType.AUTO时,持久性提供者将根据数据库选择合适的策略,因此,根据数据库的不同,您可能必须更改TABLESEQUENCE策略(因为Hibernate可以使用hi-lo算法缓存ID )。

还要确保Hibernate按预期进行批处理。为此,请激活日志记录并监视BatchingBatcher以跟踪正在执行的批处理的大小(将被记录)。

在你的特殊情况下,你可能会考虑使用the StatelessSession interface(当然问题一旦解决)。