zoukankan      html  css  js  c++  java
  • Hibernate的批量插入(&&JDBC)

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

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

    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缓存的批量插入;

         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,影响查询速率。

  • 相关阅读:
    react和vue——比较
    CSS 网格布局学习
    Spark 获取指定分区内的数据
    Spark常见算子
    使用 python 批量插入 hive
    Linux 获取在使用的网卡名称,信息
    Linux 修改成静态IP 设置网关 DNS
    Linux shell 使用脚本 修改文本中的 key value
    Linux 上的一些有用的shell脚本
    使用canda 安装 pyhdfs 实现文件上传到 HDFS
  • 原文地址:https://www.cnblogs.com/mistor/p/6091290.html
Copyright © 2011-2022 走看看