管理Session
一.Hibernate自身提供了三种管理Session对象的方法:
- Session对象的生命周期与本地线程绑定
- Session对象的生命周期与JTA事务绑定
- Hibernate委托程序管理Session对象的生命周期
- thread: Session对象的生命周期与本地线程绑定
- jta*: Session对象的生命周期与JTA事务绑定
- managed: Hibernate委托程序来管理Session对象的生命周期
三.Hibernate按以下规则把Session与本地线程绑定:
当一个线程(threadA)第一次调用SessionFactory的 getCurrentSession() 方法时,该方法会创建一个新的Session(sessionA)对象,把该对象与threadA绑定,并将sessionA返回。
当threadA再次调用SessionFactory对象的getCurrentSession()方法时,该方法将返回sessionA对象。
当threadA提交sessionA对象关联的事务时,Hibernate会自动flush sessionA对象的缓存,然后提交事务, 关闭sessionA对象 。当threadA撤销sessionA关联的事务时, 也会自动关闭sessionA对象 。
若threadA再次调用SessionFactory对象的getCurrentSession()方法时,该方法 会创建一个新的Session(sessionB)对象 ,把该对象与threadA绑定,并将sessionB返回。
四.现在介绍第一种管理方式,即将Session对象的生命周期与本地线程绑定。
如果把Hibernate配置文件的hibernate.current_session_context_class属性设置为thread,Hibernate就会按照与本地线程绑定的方式来管理Session。
①. 在Hibernate 的配置文件hibernate.cfg.xml 中配置 hibernate.current_session_context_class
<!-- 配置管理session 的方式 -->
<property name="hibernate.current_session_context_class">thread</property>
②. 创建管理session 的类HibernateUtils.java
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;
public class HibernateUtils {
//hIbernate的构造器
public HibernateUtils(){}
//获取Hibernte的单实例
private static HibernateUtils instance = new HibernateUtils();
public static HibernateUtils getInstance(){
return instance;
}
//获取sessionFactory
private SessionFactory sessionFactory;
public SessionFactory getSessionFaction(){
if(sessionFactory == null){
Configuration configuration = new Configuration().configure();
ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
.applySettings(configuration.getProperties())
.buildServiceRegistry();
sessionFactory = configuration.buildSessionFactory(serviceRegistry);
}
return sessionFactory;
}
// 通过getCurrentSession() 可以把 session和 当前线程绑定 :Current 的意思是:当前的
public Session getSession(){
return getSessionFaction().getCurrentSession();
}
}
Department.java
public class DepartmentDao{
//内部获取Session
//获取和当前线程绑定的Session对象
//1.不需要从外部传入Sesssion对象
//2.多个DAO方法也可以使用一个事务
Session session=HibernateUtils.getInstance().getSession();
System.out.println(Session.hashCode());
session.save(dept);
}
测试:
@Test
public void testHibernateManageSession(){
//获取session
//开启事务
Session session = HibernateUtils.getInstance().getSession();
System.out.println("-->" + session.hashCode());
Transaction transaction = session.beginTransaction();
DepartmentDAO departmentDAO = new DepartmentDAO();
Department dept = new Department();
dept.setName("ChuckHero");
departmentDAO.save(dept);
departmentDAO.save(dept);
departmentDAO.save(dept);
//若 Session 是由 thread 来管理的,则 在提交或回滚事务后,就 已经关闭Session
System.out.println( "session.isOpen()1: " + session.isOpen());
transaction.commit();
System.out.println( "session.isOpen()2: " + session.isOpen());
}
打印结果:观察它们的hashCode 的值和提交事务前后session.isOpen的值:
-->852989707
session.hashCode: 852989707
Hibernate:
select
user_deptid.nextval
from
dual
session.hashCode: 852989707
session.hashCode: 852989707
session.isOpen()1: true
Hibernate:
insert
into
BB_DEPARTMENTS
(NAME, ID)
values
(?, ?)
session.isOpen()2: false
批量操作
批量处理数据是指在一个事务中处理大量数据。
在应用层进行批量操作,主要有以下方式:
1.通过Session
2.通过HQL
3.通过StatelessSession
4.通过JDBC API(推荐方式,效率最高)
通过Session进行批量操作:
Session的save()及update()方法都会把处理的对象存放在自己的缓存中。如果通过一个Session对象来处理大量持久化对象,应该及时从缓存中清空已经处理完毕并且不会再访问的对象。具体的做法是在处理完一个对象或小批量对象后,立即调用flush()方法刷新缓存,然后再调用clear()方法情况缓存。
通过Session来进行处理操作会受到以下约束:
1.需要在Hibernate配置文件中设置JDBC单次批量处理的数目,应保证每次向数据库发送的批量的SQL语句数目与batch size属性一致。
2.若对象采用”identity”标识生成器,则Hibernate无法在JDBC曾进行批量插入操作
3.进行批量操作时,建议关闭Hibernate的二级缓存
下面的代码演示了通过session批量插入数据:
News news = null;
for(int i = 0; i < 10000; i++) {
news = new News();
news.setTitle("--" + i);
session.save(news);
if((i + 1) % 20 == 0) {
session.flush();
session.clear();
}
}
对于批量更新,在进行批量更新时,如果一下子把所有对象都加载到Session缓存,然后在缓存中一一更新,显然是不可取的。
使用可滚动的结果集org.hibernate.ScrollableResults,该对象中实际上并不包含任何对象,只包含用于在线定位记录的游标。只有当程序遍历访问ScrollableResults对象的特定元素时,它才会到数据库中加载相应的对象。
org.hibernate.ScrollableResults对象由Query的scroll方法返回。
代码如下:
ScrollableResults sr = session.createQuery("FROM News").scroll();
int count = 0;
while (sr.next()) {
News n = (News) sr.get(0);
n.setTitle(n.getTitle() + "*****");
if (((count++) + 1) % 100 == 0) {
session.flush();
session.clear();
}
}
通过HQL进行批量操作:
注意:HQL只支持INSERT INTO … SELECT形式的插入语句,但不支持INSERT INTO … VALUES形式的插入语句,所以使用HQL不能进行批量插入操作。
通过StatelessSession进行批量操作:
从形式上看,StatelessSession与Session的用法类似。StatelessSession与Session相比,有以下区别:
StatelessSession没有缓存,通过StatelessSession来加载、保存或更新后的对象处于游离状态.
StatelessSession不会与Hibernate的二级缓存交互。
当调用StatelessSession的save()、update()或delete()方法时,这些方法会立即执行相应的SQL语句,而不会仅计划执行一条SQL语句。
StatelessSession不会进行脏检查,因此修改了Customer对象属性后,还需要调用StatelessSession的update()方法来更新数据库中数据。
StatelessSession不会对关联的对象进行任何的级联操作。
通过同一个StatelessSession对象两次加载的OID为1的Customer对象,得到的两个对象内存地址不同。
StatelessSession所做的操作可以被Interceptor拦截器捕获到,但是会被Hibernate的事件处理系统忽略掉。
通过JDBC API执行批量操作:(推荐)
这是效率最高的方法,代码如下:
public void testBatch() {
session.doWork(new Work() {
@Override
public void execute(Connection connection) throws SQLException {
// 通过 JDBC 原生的 API 进行操作, 效率最高, 速度最快!
}
});
}
参考:http://blog.csdn.net/xiangwanpeng/article/details/53517051