看看这里:
------------------------------------
14.1. 批量插入(Batch inserts)如果要将很多对象持久化,你必须通过经常的调用 flush() 以及稍后调用 clear() 来控制第一级缓存的大小。Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
   
for ( int i=0; i<100000; i++ ) {
    Customer customer = new Customer(.....);
    session.save(customer);
    if ( i % 20 == 0 ) { //20, same as the JDBC batch size //20,与JDBC批量设置相同
        //flush a batch of inserts and release memory:
        //将本批插入的对象立即写入数据库并释放内存
        session.flush();
        session.clear();
    }
}
   
tx.commit();
session.close();
-------------------------
因为你采用的是声明式事务,那就可以不用管session和transaction的打开关闭问题。session实例可以通过Dao的getSession()或sessionFactory.getCurrentSession()获得。

解决方案 »

  1.   

    使用的是spring的hibernateTemplate模板吗?
      

  2.   

    我是本帖作者:
        没错,我使用的是spring的hibernateTemplate模板,请问如何解决!
      

  3.   

    hibernate提供了批量更新的方法:
    (spring直接使用hibernate方法)
    public void Foo(){
    getHibernateTemplate().execute(new HibernateCallback() {
    public Object doInHibernate(Session session) throws HibernateException {
    for ( int i=0; i<100000; i++ ) {
        Customer customer = new Customer(.....);
        session.save(customer);
        if ( i % 20 == 0 ) { //20, same as the JDBC batch size //20,与JDBC批量设置相同
            //flush a batch of inserts and release memory:
            //将本批插入的对象立即写入数据库并释放内存
            session.flush();
            session.clear();
        }
    }
    return new Integer(100000); //象征性的返回一个obj }
    });
    }楼主可以试试,至于批量更新,批量删除,可以直接用hql的update和delete来做,更为简单,可以参考hibernate文档
      

  4.   

    对于批量更新和批量删除,只是把操作变的简单,实际上对性能提升不大,对于这种情况,还是直接用hibernate控制jdbc来直接操作要快捷些。对于批量插入,是因为你的连接可能没有及时关闭,我不清楚你的具体代码写法,你是不是对service层进行循环操作了呢?比如
    for(int i=0;i<10000;i++)
       service.add(obj[i])
    如果是这样的话,得把这些都挪到业务层里去做就ok了。楼主最好能让偶看看具体的代码。
    btw:几千条数据还不算大批数据,应该不会有什么问题。
      

  5.   

    或者是扩展一下spring的方法,就是在下面这个代码里写上具体语句,这和楼上的代码是一样的
    public Object doInHibernate(Session session)
    throws HibernateException {
    }另外,批量更新和删除是hibernate3里出来的,2.x版本里没有。
      

  6.   

    to:mxlmwl(飞星),2.x版本里好象有批量更新吧,而批量删除做只是一次load一个在delete一个
    楼主的问题,应该是你的批量更新没在一个事务中,每更新一个就创建一个connection ,而又不能立即释放,直到连接数等于数据库连接数
      

  7.   

    回mxlmwl(飞星):
         我是本帖的作者,我在service的循环里既有既有jdbc的操作,又有hibernate的操作。以下是我的方法:
     public boolean saveTYearsSalary(int comp, String eemp, String memp) throws
          Exception {
        int isDelete = this.getYearsSalaryJdbc().deleteTYearsSalaryByComp(comp);
    //    boolean isDelete = this.getYearsSalaryDao().deleteTYearsSalaryByComp(comp);
        boolean isSave = false;    DbTime dbTime = new DbTime();
        Date now = dbTime.getNow();    List list = this.getPersonDao().loadTPersonByComp(comp);
        List listTYearsSalary=new ArrayList();
        int inYears = 0;
        int yrAllow = 0;
        if (list.size() > 0) {
          for (int i = 0; i <= list.size() - 1; i++) {
            System.out.println("i: " + i);
            TPerson tPerson = new TPerson();
            tPerson = (TPerson) list.get(i);        TYearsSalary tYearsSalary = new TYearsSalary();
            tYearsSalary.setComp(new Short(tPerson.getComp().toString()));
            tYearsSalary.setPerNo(tPerson.getPerNo());
            tYearsSalary.setInDate(tPerson.getInCdt());
            tYearsSalary.setPostType(tPerson.getPost());
            tYearsSalary.setEemp(eemp);
            tYearsSalary.setCdt(now);
            tYearsSalary.setUdt(now);
            tYearsSalary.setMemp(memp);        if (tPerson.getInCdt() != null) {
              inYears = this.getPersonServiceTiger().getLengthOfPerson(tPerson.
                  getInCdt());
              tYearsSalary.setInYears(new Byte(Integer.toString(inYears)));//post有可能变动:
              if (tPerson.getPost().equalsIgnoreCase("22")) { //员工
                switch (inYears) {
                  case 0:
                    yrAllow = 0;
                    break;
                  case 1:
                    yrAllow = 100;
                    break;
                  case 2:
                    yrAllow = 150;
                    break;
                  case 3:
                    yrAllow = 200;
                    break;
                  default:
                    yrAllow = 200;
                }
              }
              else { //非员工
                switch (inYears) {
                  case 0:
                    yrAllow = 0;
                    break;
                  case 1:
                    yrAllow = 20;
                    break;
                  case 2:
                    yrAllow = 40;
                    break;
                  case 3:
                    yrAllow = 60;
                    break;
                  case 4:
                    yrAllow = 80;
                    break;
                  case 5:
                    yrAllow = 100;
                    break;
                  case 6:
                    yrAllow = 120;
                    break;
                  case 7:
                    yrAllow = 140;
                    break;
                  case 8:
                    yrAllow = 160;
                    break;
                  case 9:
                    yrAllow = 280;
                    break;
                  case 10:
                    yrAllow = 200;
                    break;
                  default:
                    yrAllow = 200;
                }
              }
              tYearsSalary.setYrAllow(new BigDecimal(Integer.toString(yrAllow)));
            }
            else {
              tYearsSalary.setInYears(new Byte(Integer.toString(0)));
              tYearsSalary.setYrAllow(new BigDecimal(Integer.toString(0)));
            }        if (tYearsSalary.getInYears().intValue() > 0) { //如果工龄小于一年的员工就不会新增到年资表
    //          try {
              listTYearsSalary.add(tYearsSalary);
    //            isSave = this.getYearsSalaryDao().saveTYearsSalary(tYearsSalary);
    /*          }
              catch (Exception ex) {
                System.out.println(ex.toString());
                this.getYearsSalaryJdbc().deleteTYearsSalaryByComp(comp);
    //            this.getYearsSalaryDao().deleteTYearsSalaryByComp(comp);
                return false;
              }*/
            }
          }
          isSave = this.getYearsSalaryDao().saveTYearsSalaryPL(listTYearsSalary);
          return isSave;
        }
        else { //没有符合的记录
          return false;
        }
      }
      

  8.   

    用hibernate3不就行了?支持批量更新和删除。
      

  9.   

    呵呵,我的意思是3.x版本中对于更新和删除直接用update和delete这种hql语句就可以了,而2.x版本是必须要load后再删除,算不上批量更新。何况hibernate用批量更新和删除的效率相对低些,这是orm框架的软肋,还是直接用jdbc好些。虽然说通过更改batch_size可以改善,但是如果追求效率的话,最终还是用jdbc省事。另外,我大致看了一下程序,没发现你有jdbc操作啊,是哪句??
    估计应该是你的 isSave = this.getYearsSalaryDao().saveTYearsSalaryPL(listTYearsSalary);这句出问题了吧,跟踪一下看看。看看你相应的DAO操作
      

  10.   

    如果使用了hibernateTemplate的话,spring会帮你管理session的,除非你使用了其他的JDBC
      

  11.   

    hibernate对于批量操作的处理性能是比较差的,当然这也取决与你的机器配置还有数据库的设计等等,对于相同的批量操作,hibernate大概要比JDBC慢许多,而且batch_size的设定对其性能影响也不是很明显(数据库与应用程序在同一台机器上)
      

  12.   

    在Hibernate应用中如何处理批量更新和批量删除
    批量更新是指在一个事务中更新大批量数据,批量删除是指在一个事务中删除大批量数据。以下程序直接通过Hibernate API批量更新CUSTOMERS表中年龄大于零的所有记录的AGE字段: 
     
    代码内容
    tx = session.beginTransaction();  
    Iterator customers=session.find("from Customer c where c.age>0").iterator();  
    while(customers.hasNext()){  
    Customer customer=(Customer)customers.next();  
    customer.setAge(customer.getAge()+1);  
    }   
    tx.commit();  
    session.close();   
    如果CUSTOMERS表中有1万条年龄大于零的记录,那么Session的find()方法会一下子加载1万个Customer对象到内存。当执行tx.commit()方法时,会清理缓存,Hibernate执行1万条更新CUSTOMERS表的update语句: 
     
    代码内容
    update CUSTOMERS set AGE=? …. where ID=i;  
    update CUSTOMERS set AGE=? …. where ID=j;  
    update CUSTOMERS set AGE=? …. where ID=k;   
    以上批量更新方式有两个缺点: 
    (1) 占用大量内存,必须把1万个Customer对象先加载到内存,然后一一更新它们。 
    (2) 执行的update语句的数目太多,每个update语句只能更新一个Customer对象,必须通过1万条update语句才能更新一万个Customer对象,频繁的访问数据库,会大大降低应用的性能。 
    为了迅速释放1万个Customer对象占用的内存,可以在更新每个Customer对象后,就调用Session的evict()方法立即释放它的内存: 代码内容
    tx = session.beginTransaction();  
    Iterator customers=session.find("from Customer c where c.age>0").iterator();  
    while(customers.hasNext()){  
    Customer customer=(Customer)customers.next();  
    customer.setAge(customer.getAge()+1);  
    session.flush();  
    session.evict(customer);  
    }   
    tx.commit();  
    session.close();  
    在以上程序中,修改了一个Customer对象的age属性后,就立即调用Session的flush()方法和evict()方法,flush()方法使Hibernate立刻根据这个Customer对象的状态变化同步更新数据库,从而立即执行相关的update语句;evict()方法用于把这个Customer对象从缓存中清除出去,从而及时释放它占用的内存。 
    但evict()方法只能稍微提高批量操作的性能,因为不管有没有使用evict()方法,Hibernate都必须执行1万条update语句,才能更新1万个Customer对象,这是影响批量操作性能的重要因素。假如Hibernate能直接执行如下SQL语句: update CUSTOMERS set AGE=AGE+1 where AGE>0; 那么以上一条update语句就能更新CUSTOMERS表中的1万条记录。但是Hibernate并没有直接提供执行这种update语句的接口。应用程序必须绕过Hibernate API,直接通过JDBC API来执行该SQL语句: 代码内容
    tx = session.beginTransaction();  
    Connection con=session.connection();  
    PreparedStatement stmt=con.prepareStatement("update CUSTOMERS set AGE=AGE+1 "  
    +"where AGE>0 ");  
    stmt.executeUpdate();  
    tx.commit();  
    以上程序演示了绕过Hibernate API,直接通过JDBC API访问数据库的过程。应用程序通过Session的connection()方法获得该Session使用的数据库连接,然后通过它创建PreparedStatement对象并执行SQL语句。值得注意的是,应用程序仍然通过Hibernate的Transaction接口来声明事务边界。 
    如果底层数据库(如Oracle)支持存储过程,也可以通过存储过程来执行批量更新。存储过程直接在数据库中运行,速度更加快。在Oracle数据库中可以定义一个名为batchUpdateCustomer()的存储过程,代码如下: 代码内容
    create or replace procedure batchUpdateCustomer(p_age in number) as  
    begin  
    update CUSTOMERS set AGE=AGE+1 where AGE>p_age;  
    end;  
    以上存储过程有一个参数p_age,代表客户的年龄,应用程序可按照以下方式调用存储过程: 
    代码内容
    tx = session.beginTransaction();  
    Connection con=session.connection();  
    String procedure = "{call batchUpdateCustomer(?) }";  
    CallableStatement cstmt = con.prepareCall(procedure);  
    cstmt.setInt(1,0); //把年龄参数设为0  
    cstmt.executeUpdate();  
    tx.commit();  从上面程序看出,应用程序也必须绕过Hibernate API,直接通过JDBC API来调用存储过程。 
    Session的各种重载形式的update()方法都一次只能更新一个对象,而delete()方法的有些重载形式允许以HQL语句作为参数,例如: 代码内容
    session.delete("from Customer c where c.age>0");  
    如果CUSTOMERS表中有1万条年龄大于零的记录,那么以上代码能删除一万条记录。但是Session的delete()方法并没有执行以下delete语句 代码内容
    delete from CUSTOMERS where AGE>0;  
    Session的delete()方法先通过以下select语句把1万个Customer对象加载到内存中: 代码内容
    select * from CUSTOMERS where AGE>0;  
    接下来执行一万条delete语句,逐个删除Customer对象: 代码内容
    delete from CUSTOMERS where ID=i;  
    delete from CUSTOMERS where ID=j;  
    delete from CUSTOMERS where ID=k;  
    由此可见,直接通过Hibernate API进行批量更新和批量删除都不值得推荐。而直接通过JDBC API执行相关的SQL语句或调用相关的存储过程,是批量更新和批量删除的最佳方式,这两种方式都有以下优点: (1) 无需把数据库中的大批量数据先加载到内存中,然后逐个更新或修改它们,因此不会消耗大量内存。 
    (2) 能在一条SQL语句中更新或删除大批量的数据。 
      

  13.   

    是的,3.x版本的批量更新实际上是省去了开发者的麻烦,不用先load再delete了,但实际上对性能提高并非很大。
    如果对跨数据库要求不是很高的话,就用jdbc好了。
      

  14.   

    回myth822(↑八月的吟游者↑) 和mxlmwl(飞星) :
        我是本帖的作者,我的事务管理方法里面确实既有hibernate操作,又有JDBC操作!
        也就是在方法的每一次循环里(大概有好几千个循环)都分别执行了几个hibernate操作和JDBC操作!    请问现在程序出现这样的异常:
        org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is org.apache.commons.dbcp.SQLNestedException: Cannot create PoolableConnectionFactory (Data source rejected establishment of connection,  message from server: "Too many connections")
        是不是跟这个原因有关呢?
      

  15.   

    这是连接池报错啊,提示太多的连接啦,应该还是你程序的问题。不过你的jdbc操作是哪一段,我没有看到啊。
      

  16.   

    回myth822(↑八月的吟游者↑) 和mxlmwl(飞星)和各位大侠 :
        我是本帖的作者,下面是我的方法中写在循环里的数据库操作的方法:
    (1)int isDelete = this.getYearsSalaryJdbc().deleteTYearsSalaryByComp(comp);
    (2)inYears = this.getPersonServiceTiger().getLengthOfPerson(tPerson.getInCdt());(3)isSave = this.getYearsSalaryDao().saveTYearsSalaryPL(listTYearsSalary);    其中,(1)和(2)都是JDBC的操作,(3)是hibernate操作,而这些方法我都是写在Dao这一层,在service层中调用,而没有直接在service层中实现,所以你在我的代码中没有看看数据库操作的语句。并且我对这个方法做了声明试的事务管理,结果运行不久程序就报这个链接错误了。
        而我现在想知道的就是能否在一个事务管理中管理既有JBDC操作又有hibernate操作的情况,如果可以的话,请告诉我解决的方法!
      

  17.   

    应该是你的JDBC有问题。  你应该在每一次执行完之后关闭JDBC连接,  否则的话,同时打开几千的数据库连接。  导致你的SPring管理的Hibernate没法得到数据库连接。