Hibernate管理Session
Hibernate自身提供了三种管理Session对象的方法:
- Session对象的生命周期与本地线程绑定。
- Session对象的生命周期与JTA事务绑定。
- Hibernate委托程序管理Session对象的生命周期。
在Hibernate配置文件中,hibernate.current_session_context_class属性用于指定Session的管理方式,可选值包括
- thread: Session对象的生命周期与本地线程绑定。
- jta*:Session对象的生命周期与JTA事务绑定。
- managed:Hibernate委托程序管理Session对象的生命周期。
我们来考虑一个需求,如何在一个DAO类中添加Department对象到数据库中。我们也许会使用构造函数的方式添加。
public void save(Session session, Department dept){
session.save(dept);
}
若需要传入一个Session,则意味上上一层可能是Service,需要获取到Session对象,上一层需要和Hibernate的API紧密耦合。所以不推荐使用此种方式。我们希望提供一个干净的save方法。
可以获取和当前线程绑定的Session对象,这样做有两个好处:
- 不需要从外部传入Session对象。
- 多个DAO方法可以使用同一个事务。
使用当前线程绑定的Session来改造上述的程序
首先我们先创建一个HIbernate工具类(HibernateUtil)。该类为单例模式的。
public class HibernateUtils {
private SessionFactory sessionFactory;
public static HibernateUtils instance = new HibernateUtils();
public static HibernateUtils getInstance(){
return instance;
}
public SessionFactory getSessionFactory() {
if(sessionFactory == null){
//1.1 Create configuration object.
Configuration configuration = new Configuration().configure();
sessionFactory = configuration.buildSessionFactory();
}
return sessionFactory;
}
public Session getSession(){
return getSessionFactory().getCurrentSession();
}
}
之后在DAO中执行保存操作
public class HelloWorldDAO {
public void save(HelloWorld helloWorld){
Session session = HibernateUtils.getInstance().getSession();
session.save(helloWorld);
}
}
测试类
public void test(){
Session session = HibernateUtils.getInstance().getSession();
System.out.println(session.hashCode());
Transaction transaction = session.beginTransaction();
HelloWorldDAO dao = new HelloWorldDAO();
HelloWorld helloWorld = new HelloWorld();
helloWorld.setTitle("Hello Session");
helloWorld.setDate(new Date());
helloWorld.setTitle("Title");
helloWorld.setAuthor("HFR");
dao.save(helloWorld);
transaction.commit();
System.out.println(session.isOpen());
}
Session是由Thread管理的,则在提交,回滚事务时已经关闭。
Hibernate按照以下规则将Session与本地线程绑定在一起:
- 当一个线程(thread A)第一次调用SessionFactory对象的getCurrentSession方法时,该方法会创建一个新的Session(sessionA)对象,把该对象与threadA绑定,并将sessionA返回。
- 当thread A再一次调用SessionFactory对象的getCurrentSession方法时,该方法返回session A对象。
- 当thread A 提交session A关联的事务时,Hibernate会自动flush session A的缓存,然后提交事务,关闭session A对象。当thread A撤销session A关联的事务时,也会自动关闭session A对象。
- 当thread A再次调用SessionFactory对象的getCurrentSession方法时,该方法会创建一个新的session B对象,把该对象与thread A绑定,并将session B对象返回。
批量处理数据
通常指批量的添加,删除和修改。
在应用层进行批量操作,有以下几种方式:
- 通过Session
- 通过HQL
- 通过StatelessSession
- 通过JDBC API
凡是批量操作,使用原生的JDBC API是最快的,而且效率高,速度快。
通过Session
不推荐使用Session进行批量操作:因为Session的save和update方法会把处理的对象放入缓存中,如果通过一个Session来处理大量的持久化对象,应该及时从缓存中清空已处理完毕并不再使用的对象,具体做法是处理完一个对象或小批量的对象后,立即调用flush方法刷新缓存,然后调用clear方法清空缓存。
News news = null;
for(int i=0;i<1000;i++){
news = new News();
news.setTitle("title" + i);
session.save(news);
if((i+1)%20 == 0){
session.flush();
session.clear();
}
}
通过Session处理操作会遇到记下几种限制:
- 需要在Hibernate配置文件中指定JDBC单批量处理的数目,应保证每次向数据库发送的批量的SQL的数目要与batch_size属性一致。
- 若对象采用identity生成器,则Hibernate无法在JDBC进行批量操作。
- 进行批量操作建议关闭Hibernate二级缓存。
通过StatelessSession
StatelessSeesion和Session有以下的区别:
- StatelessSession没有缓存,通过StatelessSession加载,保存或更新后的对象处于游离状态。
- StatelessSession不与Hibernate的二级缓存交互。
- 当调用StatelessSession的update, insert或delete方法时,这些方法会立即执行相应的SQL语句,而不会仅执行一条SQL语句。
- StatelessSession不会进行脏检查,因此修改了一个对象后,还需要调用StatelessSession的update对象确保数据的一致性。
- StatelessSession不会对关联对象进行任何级联操作。
- 通过一个StatelessSession对象加载两次OID为1的对象,得到的两个对象的内存地址不同。
- StatelessSession所做的操作可以被Interceptor拦截器拦截,但会被HIbernate的事件处理系统忽略掉。
注意:HQL只支持INSERT INTO ...SELECT形式的插入语句,但不支持INSERT INTO...VALUES形式的插入语句。所以HQL不能进行批量插入操作。