部分内容转自 :http://gaolixu.iteye.com/blog/519086
部分内容转自 :http://superjavason.iteye.com/blog/255423
--------------------------------------------------------------------------------------------
hibernate.jdbc.fetch_size 50
hibernate.jdbc.batch_size 25
这面这两项属性很重要
配置方法如下:
<session-factory>
...
<property name="hibernate.jdbc.batch_size">50</property>
...
</session.factory>
--------------------------------------------------
Fetch Size (抓取大小):
是设定JDBC的Statement读取数据的时候每次从数据库中取出的记录条数。
一般我们查询时会返回一个ResultSet对象,他其实是一个数据库的游标,要时刻保持与数据库的连接,不可断开。
例如一次查询结果是1万条记录,对于Oracle的JDBC驱动来说,是不会1次性把1万条结果全返出来的,而只会返出Fetch Size数量的记录,当不够用时,再去数据库取Fetch Size条数据,当然这一过程你是完全感觉不出来的。
Fetch Size设的越大,读数据库的次数越少,速度越快,越耗内存;
Fetch Size设的越小,读数据库的次数越多,速度越慢,前期会省内存(后期1万条都读出来了还是要用内存的)。
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秒!!!
这有点像平时我们写程序写硬盘文件一样,设立一个Buffer,每次写入Buffer,等Buffer满了以后,一次写入硬盘,道理相同。
-----------------------------------------------------------------------------
hibernate.max_fetch_depth 设置外连接抓取树的最大深度取值. 建议设置为0到3之间
就是每次你在查询时,会级联查询的深度,譬如你对关联vo设置了eager的话,如果fetch_depth值太小的话,会发多很多条sql
-----------------------------------------------------------------------------
1 伪批量删除
- public void delete(final List<Integer> ids)
- {
- final Session session = hibernateUtil.getCS();
- final Query q = session.createQuery("delete from Img where userId=:userId and id in(:ids)");
- final User user = (User) ActionContext.getContext().getSession().get("s");
- q.setInteger("userId", user.getId());
- q.setParameterList("ids", ids);
- q.executeUpdate();
- }
public void delete(final List<Integer> ids)
{
final Session session = hibernateUtil.getCS();
final Query q = session.createQuery("delete from Img where userId=:userId and id in(:ids)");
final User user = (User) ActionContext.getContext().getSession().get("s");
q.setInteger("userId", user.getId());
q.setParameterList("ids", ids);
q.executeUpdate();
}
2 批量插入
大家说下面的代码,50次flush一下, 如果同时也设置了<property name="hibernate.jdbc.batch_size">40(或其它值)</property>会怎么样呢?
- Session session = sessionFactory.openSession();
- Transaction tx = session.beginTransaction();
- for ( int i=0; i<100000; i++ ) {
- Customer customer = new Customer(.....);
- //如果你的 hibernate.cache.use_second_level_cache 是 true, 请在会话级别上关闭他
- //向(任何一级)缓存中加载大量数据通常也意味着它们很快会被清除出去,这会增加GC开销。
- session.setCacheMode(CacheMode.IGNORE);
- session.save(customer);
- if ( i % 50 == 0 ) {
- //将本批插入的对象立即写入数据库并释放内存
- session.flush();
- session.clear();
- }
- }
- tx.commit();
- session.close();
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
for ( int i=0; i<100000; i++ ) {
Customer customer = new Customer(.....);
//如果你的 hibernate.cache.use_second_level_cache 是 true, 请在会话级别上关闭他
//向(任何一级)缓存中加载大量数据通常也意味着它们很快会被清除出去,这会增加GC开销。
session.setCacheMode(CacheMode.IGNORE);
session.save(customer);
if ( i % 50 == 0 ) {
//将本批插入的对象立即写入数据库并释放内存
session.flush();
session.clear();
}
}
tx.commit();
session.close();
--------------------------------------------------------------------------------------------------------------
JDBC批量操作
Statement加批量的方法
- conn.setAutoCommit(false);
- Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY);
- for(int x = 0; x < size; x++){
- stmt.addBatch("INSERT INTO adlogs(ip,website,yyyymmdd,hour,object_id) VALUES('192.168.1.3', 'localhost','20081009',8,'23123')");
- }
- stmt.executeBatch();
- conn.commit();
conn.setAutoCommit(false);
Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY);
for(int x = 0; x < size; x++){
stmt.addBatch("INSERT INTO adlogs(ip,website,yyyymmdd,hour,object_id) VALUES('192.168.1.3', 'localhost','20081009',8,'23123')");
}
stmt.executeBatch();
conn.commit();
使用PreparedStatement加批量的方法
- try {
- Class.forName("com.mysql.jdbc.Driver");
- conn = DriverManager.getConnection(o_url, userName, password);
- conn.setAutoCommit(false);
- String sql = "INSERT adlogs(ip,website,yyyymmdd,hour,object_id) VALUES(?,?,?,?,?)";
- PreparedStatement prest = conn.prepareStatement(sql,ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_READ_ONLY);
- for(int x = 0; x < size; x++){
- prest.setString(1, "192.168.1.1");
- prest.setString(2, "localhost");
- prest.setString(3, "20081009");
- prest.setInt(4, 8);
- prest.setString(5, "11111111");
- prest.addBatch();
- }
- prest.executeBatch();
- conn.commit();
- conn.close();
- } catch (SQLException ex) {
- Logger.getLogger(MyLogger.class.getName()).log(Level.SEVERE, null, ex);
- } catch (ClassNotFoundException ex) {
- Logger.getLogger(MyLogger.class.getName()).log(Level.SEVERE, null, ex);
- }
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection(o_url, userName, password);
conn.setAutoCommit(false);
String sql = "INSERT adlogs(ip,website,yyyymmdd,hour,object_id) VALUES(?,?,?,?,?)";
PreparedStatement prest = conn.prepareStatement(sql,ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_READ_ONLY);
for(int x = 0; x < size; x++){
prest.setString(1, "192.168.1.1");
prest.setString(2, "localhost");
prest.setString(3, "20081009");
prest.setInt(4, 8);
prest.setString(5, "11111111");
prest.addBatch();
}
prest.executeBatch();
conn.commit();
conn.close();
} catch (SQLException ex) {
Logger.getLogger(MyLogger.class.getName()).log(Level.SEVERE, null, ex);
} catch (ClassNotFoundException ex) {
Logger.getLogger(MyLogger.class.getName()).log(Level.SEVERE, null, ex);
}
注意上面JDBC驱动使用的是mysql的, 好像不支持批量更新,所以大家一定要使用oracle的JDBC驱动试验啊。