这里讨论一下通过SessionFactory获取Session的两个方法,openSession()
和getCurrentSession()
的区别:
(1) 采用getCurrentSession()
创建的Session会绑定到当前线程中,而采用openSession()
创建的Session则不会
(2) 采用getCurrentSession()
创建的Session在commit或rollback时会自动关闭,而采用openSession()
创建的Session必须手动关闭。
(3) 如果是通过getCurrentSession()
获取Session,进行查询也需要通过事务提交
(4) 如果希望使用getCurrentSession()
,我们需要在hibernate.cfg.xml文件中加入如下配置:
(1)如果使用的是本地事务(jdbc事务)
<property name="current_session_context_class">threadproperty>
(2)如果使用的是全局事务(jta事务)
<property name="current_session_context_class">jtaproperty>
本地事务:针对一个数据库的事务
全局事务:跨数据库的事务(比如银行的跨行转账)
openSession()和getCurrentSession()究竟使用哪一个?原则是:
如果需要在同一线程中,保证使用同一个Session,则使用getCurrentSession()
如果在一个线程中,需要使用不同的Session,则使用openSession()
这里我们可以改进我们的入门案例,增加HibernateUtil
类,使用ThreadLocal
(线程局部变量模式)将Session
与线程关联起来。
ackage com.gavin.util;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class HibernateUtil {
private static SessionFactory sessionFactory = null;
// 用ThreadLocal模式(线程局部变量模式)管理Session
private static ThreadLocal threadLocal = new ThreadLocal<>();
private HibernateUtil(){}
static{
sessionFactory = new Configuration().configure().buildSessionFactory();
}
/**
* 获取全新的Session
* @return
*/
public static Session openSession(){
return sessionFactory.openSession();
}
/**
* 获取和线程关联的session
* @return
*/
public static Session getCurrentSession(){
Session session = threadLocal.get();
if (session == null) {
session = sessionFactory.openSession();
// 把session对象设置到threadLocal
// 相当于该session已经和线程绑定
threadLocal.set(session);
}
return session;
}
}
openSession()和getCurrentSession()深入讨论
在SessionFactory启动的时候,Hibernate会根据配置创建相应的CurrentSessionContext,在getCurrentSession()
被调用的时候,实际执行的方法是CurrentSessionContext.currentSession()
。在currentSession执行时,如果当前Session为空,currentSession会调用SessionFactory的openSession()
。
Session接口的几个重要方法:
(1) 保存一个对象(记录):——save方法
(2) 删除一个对象(记录):——delete方法
(3) 查询一个对象(记录):——get/load方法
(4) 修改一个对象(记录):——update方法
get()和load()方法的区别:
(1) get()
方法直接返回实体类,如果查不到数据则返回null
,load()
会返回一个实体代理对象(当前这个对象可以自动转化为实体对象),但当代理对象被调用时,如果数据不存在,就会抛出个org.hibernate.ObjectNotFoundException
异常
(2) load()
方法先到缓存(session缓存/二级缓存)中去查,如果没有则返回一个代理对象(不马上到DB中去找),等后面使用这个代理对象操作的时候,才到DB中查询,这就是我们常说的load在默认情况下支持延迟加载(lazy)
(3) get()
先到缓存(session缓存/二级缓存)中去查,如果没有就到DB中去查(即马上发出sql语句)。总之,如果你确定DB中有这个对象就用load()
,不确定就用get()(这样效率高)
(4) 通过修改配置文件我们可以取消懒加载。–> 在对象关系映射文件中的class结点配置:lazy=”false”
Hibernate缓存分两级。通过缓存可以有效减少对数据库的频繁查询,提高性能。
这里简单说明一些什么是事务:事务简单的说,就是一组对数据库的操作集合,它们要么全部成功,要么全部失败,这个保证数据的一致性,事务具有原子性。
Query接口类型的对象可以对数据库操作,它可以使用HQL,QBC,QBE和原生SQL对数据库操作。官方推荐使用HQL语句。
举例:通过用户名查询数据:
public static void queryByName(String name){
Session session = HibernateUtil.getCurrentSession();
Transaction transaction = null;
try {
transaction = session.beginTransaction();
// 获取Query引用[这里的Employee不是指的表,而是domain类名]
Query query = session.createQuery("from Employee where name = '" + name + "'");
// 通过list方法获取结果,这个list会自动地将结果封装成对应的domain对象类型
List list = query.list();
list.forEach(e -> System.out.println("e = " + e));
transaction.commit();
}catch (Exception e){
if (transaction != null) {
transaction.rollback();
}
throw new RuntimeException(e);
}finally {
if (session != null && session.isOpen()) {
session.close();
}
}
}
Criteria接口也可用于面向对象方式的查询,关于它的具体用法这里先不做介绍。简单看几个案例:
Criteria crit = session.createCriteria(Cat.class);
crit.setMaxResults(50);
List cats = crit.list();
List cats = session.createCriteria(Cat.class).add(Restrictions.like("name", "Fritz%")).add(Restrictions.between("weight", "minWeight", "maxWeight")).lsit();
public static void testCriteria(){
Session session = HibernateUtil.getCurrentSession();
Transaction transaction = null;
try {
transaction = session.beginTransaction();
Criteria criteria = session.createCriteria(Employee.class).setMaxResults(2).addOrder(Order.desc("id"));
List list = criteria.list();
list.forEach(e -> System.out.println("e = " + e));
transaction.commit();
}catch (Exception e){
if (transaction != null) {
transaction.rollback();
}
throw new RuntimeException(e);
}finally {
if (session != null && session.isOpen()) {
session.close();
}
}
}