使用 Hibernate 和 Spring 进行批量插入

Posted

技术标签:

【中文标题】使用 Hibernate 和 Spring 进行批量插入【英文标题】:Batch Insertions with Hibernate & Spring 【发布时间】:2012-03-31 21:02:43 【问题描述】:

我的应用程序基于 Hibernate 3.2 和 Spring 2.5。下面是应用上下文中事务管理相关的sn-p:

  <tx:annotation-driven transaction-manager="txManager"/>
    <bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
          <property name="sessionFactory" ref="sessionFactory"/>
          <property name="nestedTransactionAllowed" value="true"/> 
    </bean> 
    <bean id="transactionTemplate"  classs="org.springframework.transaction.support.TransactionTemplate">
           <property name="transactionManager" ref="txManager"/>
    </bean>
    <bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor"/>
    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
    <property name="configLocation" value="classpath:/hibernate.cfg.xml"></property>
    </bean>

对于所有 DAO,都有相关的服务类,并且在服务层中的每个方法上使用 @Transactional 处理事务。但是现在有一种情况,DAO 中的一个方法说“parse()”是从服务层调用的。在服务层中我指定了@Transactional(readOnly=false)。 DAO 中的这个解析方法调用了同一个 DAO 中的另一个方法“save()”,它在数据库中存储了大量行(大约 5000 行)。现在从 parse 函数循环调用 save 方法。现在的问题是,在调用了大约 100 次“保存”方法之后。有时我会收到 OutOfMemory 异常,或者有时程序会停止响应。

现在这些是我对保存方法所做的更改:

Session session = getHibernateTemplate().getSessionFactory().openSession();
            Transaction tx = session.beginTransaction();

            int counter = 0;
            if(books!=null && !books.isEmpty())
                for (Iterator iterator = books.iterator(); iterator
                        .hasNext();) 
                    Book book = (Book) iterator.next();
                    session.save(book);
                    counter++;
                    if(counter % 20==0) 
                         session.flush();
                         session.clear();
                    
                
            
            tx.commit();
        session.close();

这是我的应用程序中唯一启动这样一个事务并在方法结束时提交它的方法。否则我通常只打电话给getHibernateTemplate.save()。我不确定是否应该通过将@Transactional(readOnly=false, PROPOGATION=NEW) 放在save() 上,在DAO 中单独执行此保存方法的事务管理,或者这种方法可以吗?

我还在 hibernate.cfg 配置文件中将 hibernate.jdbc.batch_size 更新为 20。

有什么建议吗?

【问题讨论】:

【参考方案1】:

使用hibernate批量插入,最好的做法是StatelessSession,它不会缓存你实体的任何状态,你不会遇到OutOfMemory,代码如下:

if (books == null || books.isEmpty) 
    return;

StatelessSession session = getHibernateTemplate().getSessionFactory().openStatelessSession();
Transaction tx = session.beginTransaction();

for (Book each : books)            
    session.insert(book);           

tx.commit();
session.close();

并且StatelessSession的Transaction独立于当前的事务上下文。

【讨论】:

【参考方案2】:

您只需要刷新和清除会话的位。将事务管理留给 Spring。使用 sessionFactory.getCurrentSession() 来访问 Spring 已经为您打开的会话。此外,Spring 最近的建议是避免使用 HibernateTemplate 并直接使用 Hibernate 的 API。将 SessionFactory 注入到您的 dao-bean。

【讨论】:

【参考方案3】:

我将重构parse,使其不直接调用save,而是从服务层获取一些回调。服务层将使用save 调用作为此回调传递其事务方法。

它可能与您的情况描述的不完全一样,但从这个简短的描述来看,这将是我会尝试的。

【讨论】:

以上是关于使用 Hibernate 和 Spring 进行批量插入的主要内容,如果未能解决你的问题,请参考以下文章

使用 Spring 3 和 Hibernate 3 进行身份验证(基于注释)

使用 Spring JPA / Hibernate 进行条件插入

使用 Spring 和 Hibernate 跨多个数据库进行分布式事务的“最佳”方法是啥

使用 Spring/Hibernate 进行密码加密 - Jasypt 还是其他? [关闭]

使用 Spring Boot 和 Kotlin 进行 Hibernate 急切加载整个对象图

如何使用 Spring + Hibernate 对实体进行自定义验证以进行多租户设置