Hibernate的批量插入(&&JDBC)

一、批量插入(两种方式)

1,通过hibernate缓存

如果这样写代码进行批量插入(初始设想):

package com.anlw.util;  

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;  

import com.anlw.entity.Student;  

public class SessionUtil {
    Configuration conf = null;
    ServiceRegistry st = null;
    SessionFactory sf = null;
    Session sess = null;
    Transaction tx = null;  

    public void HIbernateTest() {
        conf = new Configuration().configure();
        st = new StandardServiceRegistryBuilder().applySettings(conf.getProperties()).build();
        sf = conf.buildSessionFactory(st);
        try {
            sess = sf.openSession();
            tx = sess.beginTransaction();
            for (int i = 0; i < 10; i++) {
                Student s = new Student();
                s.setAge(i + 1);
                s.setName("test");
                sess.save(s);
                if(i%100 == 0){             //以每100个数据作为一个处理单元
                    sess.flush();           //保持与数据库数据的同步
                    sess.clear();           //清楚Session级别的一级缓存的全部数据,及时释放占用的内存
                }
            }
            tx.commit();
        } catch (Exception e) {
            if (tx != null) {
                tx.rollback();
            }
        } finally {
            sess.close();
            sf.close();
        }
    }  

    public static void main(String[] args) {
        new SessionUtil().HIbernateTest();
    }
}

  如果数据量太大,会有可能出现内存溢出的异常;

小知识:

(1).Hibernate一级缓存,对其容量没有限制,强制使用,由于所有的对象都被保存到这个缓存中,内存总会达到一定数目时出现内存溢出的情况;

(2).Hibernate二级缓存可以进行大小配置;

要解决内存溢出的问题,就应该定时的将Sessiion缓存中的数据刷到数据库,正确的批量插入方式:

(1).设置批量尺寸(博主至今还没有明白下面这个属性和flush()方法的区别)

<property name="hibernate.jdbc.batch_size">2</property>

配置这个参数的原因就是尽量少读数据库,该参数值越大,读数据库的次数越少,速度越快;上面这个配置,是Hibernate是等到程序积累了100个sql之后在批量提交;

(2).关闭二级缓存(这个博主也不是很明白)

<property name="hibernate.cache.use_second_level_cache">false</property>

除了Session级别的一级缓存,Hibernate还有一个SessionFactory级别的二级缓存,如果启用了二级缓存,从机制上来说,Hibernate为了维护二级缓存,在批量插入时,hibernate会将对象纳入二级缓存,性能上就会有很大损失,也可能引发异常,因此最好关闭SessionFactory级别的二级缓存;

(3).在一二设置完成的基础上,清空Session级别的一级缓存;

package com.anlw.util;  

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;  

import com.anlw.entity.Student;  

public class SessionUtil {
    Configuration conf = null;
    ServiceRegistry st = null;
    SessionFactory sf = null;
    Session sess = null;
    Transaction tx = null;  

    public void HIbernateTest() {
        conf = new Configuration().configure();
        st = new StandardServiceRegistryBuilder().applySettings(conf.getProperties()).build();
        sf = conf.buildSessionFactory(st);
        try {
            sess = sf.openSession();
            tx = sess.beginTransaction();
            for (int i = 0; i < 10; i++) {
                Student s = new Student();
                s.setAge(i + 1);
                s.setName("test");
                sess.save(s);
                if(i%100 == 0){             //以每100个数据作为一个处理单元
                    sess.flush();           //保持与数据库数据的同步
                    sess.clear();           //清楚Session级别的一级缓存的全部数据,及时释放占用的内存
                }
            }
            tx.commit();
        } catch (Exception e) {
            if (tx != null) {
                tx.rollback();
            }
        } finally {
            sess.close();
            sf.close();
        }
    }  

    public static void main(String[] args) {
        new SessionUtil().HIbernateTest();
    }
}

  2,绕过Hibernate,直接调用JDBC API

package com.anlw.util;  

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;  

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.jdbc.Work;
import org.hibernate.service.ServiceRegistry;  

public class SessionUtil {
    Configuration conf = null;
    ServiceRegistry st = null;
    SessionFactory sf = null;
    Session sess = null;
    Transaction tx = null;  

    public void HIbernateTest() {
        conf = new Configuration().configure();
        st = new StandardServiceRegistryBuilder().applySettings(conf.getProperties()).build();
        sf = conf.buildSessionFactory(st);
        try {
            sess = sf.openSession();
            tx = sess.beginTransaction();
            //执行Work对象指定的操作,即调用Work对象的execute()方法
            //Session会把当前使用的数据库连接传给execute()方法
            sess.doWork(new Work() {  

                @Override
                public void execute(Connection arg0) throws SQLException {//需要注意的是,不需要调用close()方法关闭这个连接
                    //通过JDBC API执行用于批量插入的sql语句
                    String sql = "insert into student(name,age) values(?,?)";
                    PreparedStatement ps = arg0.prepareStatement(sql);
                    for(int i=0;i<10;i++){
                        ps.setString(1, "kobe");
                        ps.setInt(2,12);
                        ps.addBatch();
                    }
                    ps.executeBatch();
                }
            });
            tx.commit();
        } catch (Exception e) {
            if (tx != null) {
                tx.rollback();
            }
        } finally {
            sess.close();
            sf.close();
        }
    }  

    public static void main(String[] args) {
        new SessionUtil().HIbernateTest();
    }
}

  注意:通过JDBC API中的PreparedStatement接口来执行sql语句,sql语句涉及到的数据不会被加载到Session的缓存中,因此不会占用内存空间,因此直接调用JDBC API批量化插入的效率要高于Hibernate缓存的批量插入;

来自: http://blog.csdn.net/an_2016/article/details/51759890

   3,hibernate.jdbc.fetch_size 和 hibernate.jdbc.batch_size  

hibernate.jdbc.fetch_size 50

hibernate.jdbc.batch_size 25

这两个选项非常重要!!!将严重影响Hibernate的CRUD性能!

Fetch Size 是设定JDBC的Statement读取数据的时候每次从数据库中取出的记录条数。例如一次查询1万条记录,对于Oracle的JDBC驱动来说,是不会1次性把1万条取出来的,而只会取出Fetch Size条数,当纪录集遍历完了这些记录以后,再去数据库取Fetch Size条数据。因此大大节省了无谓的内存消耗。当然Fetch Size设的越大,读数据库的次数越少,速度越快;Fetch Size越小,读数据库的次数越多,速度越慢。这有点像平时我们写程序写硬盘文件一样,设立一个Buffer,每次写入Buffer,等Buffer满了以后,一次写入硬盘,道理相同。

Oracle数据库的JDBC驱动默认的Fetch Size=10,是一个非常保守的设定,根据测试,当Fetch Size=50的时候,性能会提升1倍之多,当Fetch Size=100,性能还能继续提升20%,Fetch Size继续增大,性能提升的就不显著了,反而会消耗过多的内存 。因此建议使用Oracle时至少要将Fetch Size设到50 

不过并不是所有的数据库都支持Fetch Size特性,例如MySQL就不支持 。MySQL就像上面说的那种最坏的情况,他总是一下就把1万条记录完全取出来,内存消耗会非常非常惊人!这个情况就没有什么好办法了。

Batch Size是设定对数据库进行批量删除,批量更新和批量插入的时候的批次大小,有点相当于设置Buffer缓冲区大小的意思。Batch Size越大,批量操作的向数据库发送sql的次数越少,速度就越快。一个测试结果是当Batch Size=0的时候,使用Hibernate对Oracle数据库删除1万条记录需要25秒,Batch Size = 50的时候,删除仅仅需要5秒!!!

另外hibernate.max_fetch_depth 设置外连接抓取树的最大深度取值. 建议设置为0到3之间。就是每次你在查询时,会级联查询的深度,譬如你对关联vo设置了eager的话,如果fetch_depth值太小的话,会发多很多条sql,影响查询速率。

时间: 2024-10-13 08:22:14

Hibernate的批量插入(&&JDBC)的相关文章

Hibernate批处理操作优化 (批量插入、更新与删除)

问题描述 我开发的网站加了个新功能:需要在线上处理表数据的批量合并和更新,昨天下午发布上线,执行该功能后,服务器的load突然增高,变化曲线异常,SA教育了我一番,让我尽快处理,将CPU负载降低. 工作所需,我经常要写些程序批量处理数据,每次执行几十万数据处理的时候,我机子的CPU都会飙高,而且数据处理速度会越来越慢.比如第一个1W条要5分钟,第二个1W条就要10分钟,要干其他事情的时候机子也卡的不行,只能等着处理完数据. 其实我一直认为是数据量太大,从来不认为是程序问题,所以一直没怎么关注过.

jdbc批量插入实现大批量数据快速插入

今天在做一个将excel数据导入数据库的程序时,由于数据量大,准备采用jdbc的批量插入.于是用了preparedStatement.addBatch();当加入1w条数据时,再执行插入操作,preparedStatement.executeBatch().我原以为这样会很快,结果插入65536条数据一共花30多分钟,完全出乎我的意料.于是问了一下同事,他们在处理这种大批量数据导入的时候是如何处理的,发现他们也是用的jdbc批量插入处理,但与我不同是:他们使用了con.setAutoCommit

JDBC中的批量插入和乱码解决

字符集-乱码问题 用JDBC访问MySql数据库的时候,如果JDBC使用的字符集和MySql使用的字符集不一致,那么会导致乱码发生.解决办法当时是在使用JDBC的时候指定和数据库一样的字符集.我们可以在DB_URL字符串后加上”?characterEncoding=XXX”的方式指定JDBC使用的字符集. 同时,我们需要知道MySql中使用的字符集,可以通过以下两个命令获取. 1)      show variables like '%character%';获取数据库或者整个服务器使用的字符集

JDBC批量插入数据优化,使用addBatch和executeBatch

>>.在之前的玩转JDBC打造数据库操作万能工具类JDBCUtil,加入了高效的数据库连接池,利用了参数绑定有效防止SQL注入中其实忽略了一点,那就是SQL的批量插入的问题,如果来个for循环,执行上万次,肯定会很慢,那么,如何去优化呢? https://www.awaimai.com/841.html 一.用 preparedStatement.addBatch()配合preparedStatement.executeBatch()去批量插入 1 /** 2 * 执行数据库插入操作 4 *

JDBC批量插入性能简单分析

批量插入性能分析:仅仅针对性能 1.使用Statement对象,一次一个,循环插入 2.使用PreparedStatement对象,一次一个,循环插入 3.使用PreparedStatement对象,使用addBatch(),executeBatch(),clearBatch()实现批量插入 注意事项: 1)数据库的驱动需要是5.1.37之后的版本,否则不支持批量操作 2)mysql数据库默认不开启批量操作,需要在数据库的url后面加上:?rewriteBatchedStatements=tru

jdbc批量插入操作(addBatch)

1 /** 2 * 批量插入 3 */ 4 @Test 5 public void testInsert(){ 6 Connection conn=null; 7 Statement st=null; 8 PreparedStatement pst=null; 9 try { 10 conn=DBUtils.getConn(); 11 conn.setAutoCommit(false);//事物不能自动提交 12 st=conn.createStatement(); 13 long start

jdbc批量插入数据

//插入很多书(批量插入用法) public void insertBooks(List<Book> book) {   final List<Book> tempBook=book;   String sql="insert into book(name,pbYear) values(?,?)";   jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter()   {    public v

Hibernate 中批量处理数据

一.批量处理操作 批量处理数据是指在一个事务场景中处理大量数据.在应用程序中难以避免进行批量操作,Hibernate提供了以下方式进行批量处理数据: (1)使用HQL进行批量操作     数据库层面 (2)使用JDBC API进行批量操作  数据库层面 (3)使用Session进行批量操作   会进缓存 1.使用HQL进行批量操作 HQL可以查询数据,也可以批量插入.更新和删除数据.HQL批量操作实际上直接在数据库中完成,处理的数据不需要加载到Session缓存中.使用Query接口的execu

ibatis、hiberate之批量插入

ibatis批量插入 首先dao层要传递一个list过来,比如是一个user(list数据) 接着xml文件中这么配置,注意类型是"java.util.List, <insert id="insert" parameterClass="java.util.List"> insert into user(acount,pwd) <iterate conjunction=","> (#user[].acount#,#