1.Configuration 接口
org.hibernate.cfg.Configuration 接口的作用是加载主配置文件及映射文件,以实现对Hibernate 的启动。
Configuration 实例的获取方式:Configuration cfg = new Configuration().configure();
(1)new Configuration()
new Configuration()会加载一个属性文件 hibernate.properties。该属性文件中存放着数据连接配置、Hibernate 配置等配置信息。一般情况下不用设置该属性文件,其设置已经放到了主配置文件中。若要设置,应将其放于 src 目录下。该文件的模板存放于 Hibernate 框架解压目录下的 project/etc 下。
(2)无参 configure()方法
configure()方法,默认用于加载和解析名称为 hibernate.cfg.xml 的配置文件,并通过主配置文件找到并解析映射文件。该方法返回一个 Configuration 对象。所以,cfg 是一个包含配置信息及映射信息的 Configuration 对象.
(3)带参 configure()方法
Hibernate 主配置文件默认名称为 hibernate.cfg.xml,应存放在 src 类路径下。但也可以更换路径与文件名。此时,需要使用带参的 configuraion()方法。
2.SessionFactory接口
org.hibernate. SessionFactory 接口对象是由 Configuration 对象通过 buildSessionFactory()方法创建。创建该对象的目的是,用于开启 Session 对象。SessionFactory sessionFactory = cfg.buildSessionFactory();
(1)SessionFactory 对象特点
重量级对象(系统开销大)、单例的、线程安全的。
按理说,单例对象一定是被共享的,是线程不安全的。但查看 SessionFactory 接口的实现类 SessionFactoryImpl 源码,可以看其大多数成员变量是 final 的,所以其是线程安全的。
(2)SessionFactory 对象的使用原则
基于其是线程安全的重量级对象,其创建与销毁时系统开销大,又是单例的特点,SessionFactory 对象一般不手工关闭,而是在应用结束时自动将其销毁。因此,SessionFactory不用进行 close()关闭。
3.Session接口
org.hibernate.classic.Session 接口是应用程序与 Hibernate 连接的核心 API,是 Hibernate向应用程序提供的操纵 DB 的最主要接口。它提供了基本的保存、更新、删除与查询方法。由 SessionFactory 对象创建。
Session s= sessionFactory.getCurrentSession();
(1)Session 对象的特点
一个轻量级对象、线程不安全的、多例的。
在 Web 应用中,多个用户对同一应用访问,Hibernate 会为每个用户创建一个 Session对象。所以是多例的。Session 中包含大量非 final 成员变量,对于同一个用户的操作,可能会产生多个事务,这多个事务若同时对同一个 Session 的同一个成员变量进行访问,就会引起并发问题。所以 session 是线程不安全的。
(2)Session 对象的使用
基于 Session 的以上特点,Session 在使用时要做到一个线程一个 Session,即一个事务一个 Session。使用完毕,立即关闭。Session 不要作为某个类的成员变量出现,因为这样会出现多个实例对象对同一个 session 的共享,使其更不安全。
(3)Session 对象的获取
SessionFactory 对于 Session 对象的获取,提供了两种获取方式:
- sessionFactory.openSession():创建一个新的 Session 对象
- sessionFactory.getCurrentSession():获取当前线程中的 Session 对象
为了保证一个线程一个 Session,即一个线程中使用的 Session 是同一个对象,一般在获取 Session 对象时,使用 SessionFactory 的 getCurrentSession()方法。不过,使用该方法获取Session 对象,需要在主配置文件中对 Session 所处的上下文环境,即事务环境进行注册。
<property name="hibernate.current_session_context_class">thread</property>
hibernate.current_session_context_class 取值有三种:
取值 | 意义 |
thread | 表示当前Session所处的环境为本地事务环境,Session会与当前线程绑定 |
jta | 表示当前Session所处的环境为分布式事务环境 |
SpringSessionContext类 | SSH整合时使用。表示将当前Session的管理权交由Spring容器,而Spring容器中会有事务环境。即当前Session所处的环境为Spring事务环境。 |
为什么 getCurrentSession()方式获取到的是同一个对象?
getCurrentSession()首先会获取到当前线程 Thread.currentThread(),然后从 ThreadLocal中读取 key 为当前线程的 value。而该 value 即为当前线程中的 Session 对象。
ThreadLocal 是用于在线程中共享数据的。其底层为 Map<Thread, Object>,即该 map 的key 为线程 Thread,而 value 则为要共享的数据。
当在 Hibernate 主配置文件中设置了 hibernate.current_session_context_class 属性的值为thread 时,就表明每个用户获取到的 Session 对象都是自己当前线程中的Session,是同一个Session。
(4)Session关闭
使用 getCurrentSession()方法获取的 Session,在进行事务提交或回滚后,会自动关闭,无需再手工进行 close()。当然,也不能在最后的 finally{}语句块中对 Session 进行手工关闭。因为,无论是执行事务的 commit(),还是执行 rollback(),均会在 finally{}之前执行。也就是说,在执行 finally{}之前,session 已经关闭。若再手工关闭,将抛出异常。
总结:两种获取 Session 对象方式的区别
getCurrentSession()方式 | openSession()方式 | |
获取的对象 | 无论执行多少次该方法,只要是在同一线程中,获取的都是同一个Session对象 | 每执行一次该方法,获取到的都是一个新的Session对象。 |
对象的关闭 | 自动关闭Session,无需手工关闭 | 必须手工关闭Session对象 |
环境的注册 | 需要注册Session的运行环境 | 无需注册 |
查询对事物的支持 | 查询必须在事务内执行 | 查询可以不再事务内执行 |
4.Transaction接口
通过该接口,可以将事务从持久层,提升到业务层。由 Session 对象创建。
session.getTransaction();
事务的开启:session.beginTransaction(); 或 session.getTransaction().begin();
事务的提交:session.getTransaction().commit();
事务的回滚:session.getTransaction().rollback();
5.总结 -- 单例、多例与线程安全问题
单例与多例问题是指,当多个用户访问某个类时,系统是为每个用户创建一个该类实例,还是整个系统无论多少用户访问,只创建一个该类实例。线程安全问题是指,多个用户同时在访问同一个程序时,其对于某一数据的修改,会不会影响到其他用户中的该数据。若没有影响,则是线程安全的;若有可能影响,则是线程不安全的。
(1)HttpServlet
其是单例的。即无论多少用户访问同一个业务,如 LoginServlet,Web 容器只会创建一个该 Servlet 实例。而该实例是允许多用户访问的。若 Servlet 中包含成员变量,则每个用户对于成员变量的修改,均会影响到其他用户所看到的该变量的值,所以这时是线程不安全的。若不包含成员变量,则是线程安全的。
(2)HttpSession
其是多例的。Web 容器会为每个用户开辟一个 Session,多个用户会有多个 Session。而每个用户只能访问自己的 Session。所以,对于 Session 来说,就不存在并发访问的情况,也就不存在线程安全的问题了。所以可以说是线程安全的。
(3)Struts2的Action
其是多例的。对于同一个业务,例如 LoginAction,系统会为每一个用户创建一个LoginAction 的实例,并使其成员变量 username 与 password 接收用户提交的数据。同一用户只能访问自己的 Action。所以,对于 Action 来说,就不存在并发访问的情况,也就不存在线程安全的问题了。所以可以说是线程安全的。
(4)Hibernate的SessionFactory
其是单例的。无论多少用户访问该项目,系统只会创建一个 SessionFactory 对象,即这个对象是可以被所有用户访问的。SessionFactory 实现类中所包含的成员变量基本都是 final 常量,即任何用户均不能修改。所以,也就不存在用户的修改对其他用户的影响问题了,所以是线程安全的。
(5)Hibernate的Session
其是多例的。系统会为每个用户创建一个 Session。Session 的实现类中定义了很多的非 final 成员变量,一个事务对成员变量所做的修改,会影响到另一个事务对同一数据的访问结果,所以是线程不安全的。