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,影响查询速率。
Hibernate的批量插入(&&JDBC)的更多相关文章
- Hibernate批处理操作优化 (批量插入、更新与删除)
问题描述 我开发的网站加了个新功能:需要在线上处理表数据的批量合并和更新,昨天下午发布上线,执行该功能后,服务器的load突然增高,变化曲线异常,SA教育了我一番,让我尽快处理,将CPU负载降低. 工 ...
- Hibernate 数据的批量插入、更新和删除
4.2 Hibernate的批量处理 Hibernate完全以面向对象的方式来操作数据库,当程序里以面向对象的方式操作持久化对象时,将被自动转换为对数据库的操作.例如调用Session的delete ...
- JDBC中的批量插入和乱码解决
字符集-乱码问题 用JDBC访问MySql数据库的时候,如果JDBC使用的字符集和MySql使用的字符集不一致,那么会导致乱码发生.解决办法当时是在使用JDBC的时候指定和数据库一样的字符集.我们可以 ...
- 三种JDBC批量插入编程方法的比较
JDBC批量插入主要用于数据导入和日志记录因为日志一般都是先写在文件下的等. 我用Mysql 5.1.5的JDBC driver 分别对三种比较常用的方法做了测试 方法一,使用PreparedStat ...
- JDBC的批量批量插入
本文部分转载于:http://blog.itpub.net/29254281/viewspace-1151785/ http://www.cnblogs.com/chenjianjx/archive/ ...
- JDBC批量插入数据效率分析
对于需要批量插入数据库操作JDBC有多重方式,本利从三个角度对Statement和PreparedStatement两种执行方式进行分析,总结较优的方案. 当前实现由如下条件: 执行数据库:Mysql ...
- JDBC批量插入优化addbatch
// 获取要设置的Arp基准的List后,插入Arp基准表中 public boolean insertArpStandardList(List<ArpTable> list) { Con ...
- jdbc批量插入
分享牛,分享牛原创.有这样一个需求,文本文件中的数据批量的插入mysql,怎么用jdbc方式批量插入呢? jdbc默认提供了批量插入的方法,可能用一次就忘记了,这里做笔记记录一下jdbc批量插入吧. ...
- Mybatis与JDBC批量插入MySQL数据库性能测试及解决方案
转自http://www.cnblogs.com/fnz0/p/5713102.html 不知道自己什么时候才有这种钻研精神- -. 1 背景 系统中需要批量生成单据数据到数据库表,所以采用 ...
随机推荐
- java并发容器类
本文主要介绍java并发容器相关实现类,collections节点下接口方法介绍. Queue Java提供的线程安全的Queue可以分为阻塞队列和非阻塞队列,其中阻塞队列的典型例子是Blocking ...
- LeakCanary 内存检测 工具 --超级傻瓜 不会DDMS的福音
大神资料贴出 ,学习 ,集成到项目中 . LeakCanary 中文使用说明 http://www.liaohuqiu.net/cn/posts/leak-canary/ logcat 显示 ...
- WINDOWS下PhoneGap(Cordova)安装笔记
1.首先下载Node.js 安装nodejs很简单直接点击安装文件下一步直至成功即可,安装notejs的同时npm也会同时安装 成功后打开notejs的命令行工具 输入“node -v”," ...
- xmpp openfire smack 介绍和openfire安装及使用
前言 Java领域的即时通信的解决方案可以考虑openfire+spark+smack.当然也有其他的选择. Openfire是基于Jabber协议(XMPP)实现的即时通信服务器端版本,目前建议使用 ...
- MSSQL 全表搜索 指定字符串
平时在在MSSql中查询数据的时候,想查找,某个字段在数据库中是否存在,并且查询出在哪个表中,哪个字段下面,在不知道的情况下,操作起来会很麻烦,然后就写了一个sql语句,使用起来感觉挺方便的.当然了, ...
- 快速学习html、css的经典笔记
HTML语言剖析 Html简介-目录 全写: HyperText Mark-up Language 译名: 超文本标识语言 简释:一种为普通文件中某些字句加上标示的语言,其目的在于运用标签(tag ...
- RabbitMQ学习总结 第七篇:RCP(远程过程调用协议)
在http://www.cnblogs.com/leocook/p/mq_rabbitmq_2.html 这篇博文中我们实现了怎么去使用work queue来把比较耗时的任务分散给多个worker. ...
- NuGet控制台有几个常用命令
NuGet控制台有几个常用命令 Get-Package 获取当前项目已经安装的类库 Install-Package 安装指定类库,命令格式如下:Install-Package 类库ID,如Instal ...
- docker 组件(c/s)
Docker 组件 1. docker client : docker的客户端 2. docker server : docker daemon的主要组成部分,接受用户通过docker client发 ...
- 解决Chrome flash过期
解决Chrome flash过期的办法安装Adobe Flash Player PPAPI