在休眠中使用批量插入时,行不会持久化

时间:2013-07-23 16:45:31

标签: java hibernate orm

我试图在while循环中插入很多行。我的理解是我必须在事务的循环体中创建一个单独的新瞬态实例。

根据我的经验,不创建单独的瞬态实例(在循环体外部的瞬态实例)至少插入一行,即最后一行。在提交交易之前,我是否只需要在这里使用save一次?

在这种情况下,flush()clear()的使用是否正确?

我对hibernate相对较新。我会感谢任何帮助或指出我正确的方向。

请原谅我对变量和方法的骆驼案例约定的蔑视。

public boolean addVariableHonorariumDetails(Monthly_variable_honorarium_processing mvhp
                                             , Monthly_variable_group_honorarium_processing mvghp
                                             , Monthlyvariablecompid compositeID)
{
    Session session = HibernateUtil.getSessionFactory().openSession();   
    Transaction tx = null;
    String ccyymm = mvhp.getMvhp().getVariable_monthly_ccyy()+mvhp.getMvhp().getVariable_monthly_month();

    try
    {
        tx = session.beginTransaction();

        // ctrl query

        M_control_table mct = (M_control_table) session.createQuery("from M_control_table").uniqueResult(); //single result

        Query query = session.createSQLQuery(
"Select c.Booklet_type, SUM(c.End_Leaf -  c.Start_Leaf +1) as No_of_Coupons from m_coupon_sale c where date_format(c.Booklet_Sale_Date, '%Y%m') = :string and c.Booklet_type in ('GR','PI') group by c.Booklet_Type")
                             .addScalar("c.Booklet_type", Hibernate.STRING)
                             .addScalar("No_of_Coupons", Hibernate.STRING)
                             .setString("string", ccyymm);

        ScrollableResults results = query.scroll();

        Integer sumTotalOfPayment = 0;
        while ( results.next() )
        {
            Object[] row = results.get();

            mvghp.setMvhpgroup(new Monthlyvariablecompid(mvhp.getMvhp().getVariable_monthly_month()
                              , mvhp.getMvhp().getVariable_monthly_ccyy()
                              , row[0].toString())
                              );

            mvghp.setNo_of_items(row[1].toString());

            Integer no_of_items = Integer.parseInt(mvghp.getNo_of_items(), 10);

            mvghp.setGroup_rate( (row[0].toString().equals("GR")) ? mct.getRate_for_green_coupons()
                                                                  : mct.getRate_for_pink_coupons()  );

            Integer variable_payment = no_of_items * Integer.parseInt( mvghp.getGroup_rate(), 10);
            sumTotalOfPayment = sumTotalOfPayment + variable_payment;

            mvghp.setVariable_payment(variable_payment.toString());

            session.save(mvghp);
            session.flush();
            session.clear();
        }

        Query query2 = session.createSQLQuery(
"select m.pooja_name, count(*) as no_of_items from t_pooja_booking t inner join m_pooja_detail m on t.pooja_id=m.pooja_id where date_format(t.pooja_date, '%Y%m')= :string and m.pooja_name in ('A','B' ) group by m.pooja_name")
                                             .addScalar("m.pooja_name", Hibernate.STRING)
                                             .addScalar("no_of_items", Hibernate.STRING)
                                             .setString("string", ccyymm);

        ScrollableResults results1 = query2.scroll();

        while ( results1.next() )
        {
            Object[] row = results1.get();

            mvghp.setMvhpgroup(new Monthlyvariablecompid(mvhp.getMvhp().getVariable_monthly_month()
                                , mvhp.getMvhp().getVariable_monthly_ccyy()
                              , row[0].toString())
                              );

            mvghp.setNo_of_items(row[1].toString());
            Integer no_of_items = Integer.parseInt(mvghp.getNo_of_items(), 10);

            mvghp.setGroup_rate((row[0].toString().equals("A"))?mct.getRate_for_108_vadamalas_cook()
                                                                             :mct.getRate_for_51_vadamalas_cook() );

            Integer variable_payment = no_of_items * Integer.parseInt(mvghp.getGroup_rate() , 10) ;
            sumTotalOfPayment = sumTotalOfPayment + variable_payment;

            mvghp.setVariable_payment(variable_payment.toString());

            session.save(mvghp);
            session.flush();
            session.clear();
        }


        mvhp.setTotal_variable_payment(sumTotalOfPayment.toString());
        session.save(mvhp);

        tx.commit();

    }
    catch(Exception e)
    {
        if(tx!=null)
        {
            tx.rollback();
        }
        e.printStackTrace();
        return false;
    }
    finally
    {
        session.close();
    }

    return true;
}

1 个答案:

答案 0 :(得分:1)

通过循环调用session.save(mvghp),session.flush()和session.clear()对于每次迭代都是效率最低的,并且看起来它可能是您混淆源自的地方的一部分。

让我们回到基础。在Hibernate中,每个对象松散地表示顶级表中的一行,以及子表中连接到该顶级行的行。 (是的,也有例外,是的,这是过于简单化了,但是对于刚接触Hibernate的人来说,这是一个很好的工作模式,可以帮助你站稳脚跟。)这意味着如果你最终会有多个人最后的表中的行(例如,每循环迭代一次),您应该最终得到多个对象,每行一个。你正在做什么(重用同一个Hibernate对象)违背了这一点;如果我已经正确理解了你的用例,你应该为循环中的每次迭代创建一个新的Hibernate POJO(Plain Old Java Object的缩写)。

其次,POJO可以是持久的(你的Hibernate会话知道对象,并且当你的事务被刷新或关闭时会保存对数据库的更改)或者瞬态(Hibernate对这个对象一无所知,并赢了)不保存对数据库的更改。调用save()会使瞬态对象持久化(并且对已经持久的POJO没有影响!);它可能是Hibernate API中命名最差的方法,应该被称为makePersistent()而不是save(),因为它实际上并没有将对象保存到数据库中。相反,它使其持久化,以便最终保存到数据库中。对clear()的调用恰恰相反:它占用会话中的所有持久对象并使它们成为非持久对象,丢弃进程中的所有更改,并且它对已经是瞬态的POJO没有影响。

而flush()只是强制Hibernate写出对数据库的持久对象的所有更改; Hibernate可以在任何其他时间自由地执行此操作,如果它选择,并且保证在会话的事务提交时执行此操作,但调用flush()会强制它立即执行此操作。但是它没有提交事务,所以a)如果事务被回滚你仍然会丢失这些更改,并且b)在事务提交之前你不会看到数据库中的更改,如果你正在查看类似于SQLPlus。它通常只有在您希望将更改从POJO推送到数据库之后才能运行基于SQL的查询,您希望它考虑您刚刚进行的更改,或者您希望刷新的批量事务非常大的地方您对一个批处理进行了更改,然后对这些POJO进行垃圾收集,以便在事务完成之前不会耗尽内存,而且对于您说Hibernate的位置肯定没有必要。

因此,当您为每个POJO调用save(),flush()和clear()时,您正在做的是使一个对象持久化,然后强制数据库将其数据写入数据库,以及然后让它变得短暂。然后在下一次循环迭代期间对其数据进行更改,当您再次使其持久化时,Hibernate认为它是一个新对象(如您所愿)并将新行保存到数据库中。这是有效的,但所有刷新都是低效的(并且熟悉Hibernate的人会感到困惑),并且只需创建POJO类型的N个实例并在每个实例上调用save()(没有flush())就可以获得相同的行为,没有明确()),具有更好的性能和更少的混淆。

另外,clear()是一个大锤;它会从Hibernate会话中删除所有 POJO,这可能会导致任何希望能够更改这些对象并保持这些更改的代码出现意外行为。 (你之前对flush()的调用将确保已经进行的任何更改都会被持久化,但不会做任何事情来确保将来的更改。)这也意味着只要Hibernate需要检索任何这些对象,它就会一直回到数据库而不是从会话的缓存中拉出它们,这是低效的。如果你真的想要使用你在这里得到的方法(我偶尔也会这样做,例如在我想要最终保留许多对象的情况下,但是无法合理地为每个对象构建一个新对象,所以我重用同一个对象,在瞬态时更改其属性,不跟踪这些更改,然后简单地使其持久化,以便将“新”对象写入数据库),您可能想要使用的方法是逐出( )方法,它允许您从缓存中只删除一个Hibernate对象。但我想强调的是,甚至evict()更像是一个仅限专家的功能,在大多数情况下你都不需要它(也不应该使用它),包括你的例子中的那些。

希望这有助于您进一步了解Hibernate ......