1.核心类加载配置文件
1.创建SessionFactory
- Configuration cfg = new Configuration().addResource("com/demo/hibernate/beans/User.hbm.xml");
2 为Configuration指定持久化类
一个替代的方法是指定被映射的类 让hibernate帮你寻找映射定义文件
- Configuration cfg = new Configuration().addClass(com.demo.hibernate.beans.User.class);
Hibernate将会在类路径中需找名字为 /com/demo/hibernate/beans/User.hbm.xml 映射定义文件 消除了任何对文件名的硬编译
3 为Configuration指定配置属性
Configuration也允许指定配置属性
- Configuration cfg =new Configuration().addClass(com.demo.hibernate.beans.User.class)
- .setProperty("hibernate.dialect","org.hibernate.dialect.MySQLInnoDBDialect")
- .setProperty("hibernate.connection.datasource","java:comp/env/jdbc/test")
- .setProperty("hibernate.order_update","true");
4 Configuration的三种加载方式
在Hibernate的启动与开发流程中 要使用一个Configuration 需要为他设置三个方面的内容
数据库连接属性
hbm.xml文件
POJO类
其中 第二个和第三个只需要设置一个 就会自动需找另一个 因为这两者只需一个
第一种方式是使用hibernate.cfg.xml 该文件设置了数据库连接的属性和hbm.xml映射文件配置 hibernate会自动加载该配置属性 并自动找到POJO 因此要取得Configuration对象 只需要简单的创建改对象即可
- Configuration cfg = new Configuration();
- cfg.configuration("hibernate.cfg.xml");
第二种方式是通过hibernate.properties 省略
第三种方式是完全在构造时进行硬编码设置 设置过程如下所示
- Configuration cfg =new Configuration()
- .addClass(com.demo.hibernate.beans.User.class)
- .setProperty("hibernate.dialect","org.hibernate.dialect.MySQLInnoDBDialect")
- .setProperty("hibernate.connection.datasource","java:comp/env/jdbc/test")
- .setProperty("hibernate.order_update","true");
第一种方式是我们最常用的方式
2.Hibernate加载基本的配置信息源码浅析
我们在获取SessionFactory的时候,第一个语句就是:
查看源码可知,Configuration类的公共构造方法只有一个,并且是无参数的: 这个构造方法调用了一个受保护的构造方法: 受保护的构造方法里调用了私有方法reset,reset方法里有一句这样的代码: 这句代码会从Hibernate环境里去获取hibernate配置,查看Environment类的方法getProperties: 这个方法得到的是全局配置属性的副本。再看看全局属性GLOBAL_PROPERTIES是如何初始化的。Environment的静态代码块里有如下代码: 代码跟踪到这里,我们可以知道hibernate首先一定会加载属性配置文件hibernate.properties,而且此文件的路径是写死的。如果用户想用XML配置hibernate,就需要编写如下代码:
configure有五个重载方法:
看一下缺省的重载方法:
它调用的是另一个重载方法,加载的配置文件是写死的。这五个方法最后调用同一个方法doConfigure(Document doc): 看一下方法addProperties: 可以看到,如果配置属性名不是以“hibernate”开头会自动加上“hibernate.”,这就是为什么,我们在用XML配置hibernate的时候,属性名“hibernate.”可以省去, 但是在使用属性文件或者编程方式配置时,“hibernate.”是不能省掉的。hibernate加载配置的顺序是:properties配置——》XML配置或者编程方式配置。至于是先加载XML配置还是编程方式的配置,就要看用户的语句顺序了,但是有一点是确定的:后加载的配置会覆盖先加载的配置。
最后我们也可以看到,hibernate将基本的配置信息(不包括实体映射信息)保存到了configuration的properties成员属性中。
获取sessionFactory:
3.SessionFactory创建Session
- Configuration cfg = new Configuration();
- cfg.addResource("com/demo/hibernate/beans/User.hbm.xml");
- cfg.setProperty(System.getProperties());
- SessionFactory sessionFactory = cfg.buildSessionFactory();
SessionFactory用到了一个设计模式 工厂模式 用户程序从工程类SessionFactory取得Session实例 设计者的意图就是让它能在整个应用中共享 典型的来说 一个项目通常只需要一个SessionFactory就够了 因此我们就设计了HibernateSessionFactory.Java这个辅助类 定义了一个静态的Configuration和SessionFactory对象
- private static final Configuration cfg = new Configuration();
- private static org.hibernate.SessionFactory sessionFactory;
这两个对象对整个应用来说只有一个实例存在 因此为用户的访问定义一个本地线程变量:
- private static final ThreadLocal threadLocal = new ThreadLocal();
该线程变量是静态的 对每一个访问该线程的用户产生一个实例 这样在要取得Session对象时 首先从当前用户的线程中取得Session对象 如果还没有创建 则从SessionFactory中创建一个Session 此时会判断SessionFactory对象是否已经创建 该对象对这个应用来说 只有一个 因此 只有第一次访问该变量的用户才会创建该对象
HibernateSessionFactory.java 取得Session对象的过程如下表示
- public static Session currentSession() throws HibernateException {
- Session session = (Session) threadLocal.get();
-
- if (session == null) {
- if (sessionFactory == null) {
- try {
- cfg.configure(CONFIG_FILE_LOCATION);
- sessionFactory = cfg.buildSessionFactory();
- }
- catch (Exception e) {
- System.err.println("%%%% Error Creating SessionFactory %%%%");
- e.printStackTrace();
- }
- }
- session = sessionFactory.openSession();
- threadLocal.set(session);
- }
-
- return session;
- }
首先判断threadLocal中是否存在Session对象 如果不存在 则创建Session对象 在创建Session对象时 首先要判断系统是否已经加载Configuration 如果没有sessionFactory 则需要先创建该对象 创建完成的Session对象 需要保存在threadLocal中以供本次访问线程的下一次调用
在关闭Session对象是 只需要从当前线程中取得Session对象 关闭该对象 并置空本地线程变量即可
- public static void closeSession() throws HibernateException {
- Session session = (Session) threadLocal.get();
- threadLocal.set(null);
-
- if (session != null) {
- session.close();
- }
- }
4.Hibernate核心接口
5.Hibernate中Session获取Connection
从上图可以看出,session有两种方法获取数据库连接:
1.session.getJDBCContext().getConnectionManager().getConnection();
2.((SessionFactoryImplementor)session.getSessionFactory()).getConnectionProvider().getConnection();
无论是使用哪一种方法,最终都是从sessionFactory的Settings中获取ConnectionProvider,然后再从ConnectionProvider获取connection。
2.Hibernate连接池ConnectionProvider的初始化
Hibernate是如何初始化连接池的呢?先看下图:
1.configuration实例化的时候,hibernate会去读取配置信息,并且将基本的配置信息(不包括实体映射信息)保存到configuration的字段properties中。
2.调用configuration的buildSessionFactory()方法,buildSessionFactory()方法又会调用SettingFactory的buildSettings(Properties props)方法。
3.buildSettings方法调用ConnectionProviderFactory的newConnectionProvider(Properties props)方法产生一个ConnectionProvider对象。
4.buildSettings方法实例化一个Setting对象,并将ConnectionProvider传递给Setting。
5.buildSettings返回一个Setting对象,然后buildSessionFactory()方法会实例化一个SessionFactoryImpl,并且将Setting对象传给SessionFactoryImpl,最后返回SessionFactoryImpl。
configuration的buildSessionFactory()方法实际上是实例化了一个连接池,并且把这个连接池交给SessionFactory管理。
ConnctionProvider是连接提供者,hibernate的数据库连接都来自它。当我们调用SessionFactoryImpl的openSession()方法时,就很容易获得数据库连接了。
Hibernate为ConnctionProvider提供了以下几个实现类:
1.C3P0ConnectionProvider。从C3P0连接池中获取数据库连接。
2.ProxoolConnectionProvider。从Proxool连接池中获取数据库连接。
3.DatasourceConnectionProvider。一般是通过JNDI从应用服务器(如JBoss)中获取数据源。
4.DriverManagerConnectionProvider。Hibernate自带的连接池。
5.UserSuppliedConnectionProvider。没有任何实现,只是抛出了异常。
到底使用哪一个呢?看看ConnectionProviderFactory.newConnectionProvider(Properties props)方法就知道了:
上面是核心代码。
1.如果配置了hibernate.connection.provider_class属性,就会根据指定的类去实例化。属性值为C3P0ConnectionProvider或者ProxoolConnectionProvider的全限定名。
2.如果配置了hibernate.connection.datasource属性,则会实例化一个DatasourceConnectionProvider。属性值为JNDI名称。
3.如果上面两个都没配置,则会去判断是否配置hibernate.connection.url属性。如果配置了,会实例化一个DriverManagerConnectionProvider。
4.如果没有配置hibernate.connection.url属性,则实例化一个UserSuppliedConnectionProvider,实际上是抛出了异常。
我们也可以自己写一个ConnctionProvider的实现,然后配置hibernate.connection.provider_class属性,属性值为我们自己的实现类的全限定名。
从上图可以看出,session有两种方法获取数据库连接:
1.session.getJDBCContext().getConnectionManager().getConnection();
2.((SessionFactoryImplementor)session.getSessionFactory()).getConnectionProvider().getConnection();
无论是使用哪一种方法,最终都是从sessionFactory的Settings中获取ConnectionProvider,然后再从ConnectionProvider获取connection。