Loading

转载Hibernate原理

转载一位大佬写的文章!链接是http://blog.csdn.net/he90227/article/details/50300039

可惜打不开链接叻/我也是在百度快照里面看的/担心百度快照也没了就copy下来叻/

1.核心类加载配置文件

1.创建SessionFactory

 

 Configuration接口的作用是对Hibernate进行配置 以及对他进行启动 在Hibernate的启动过程中 Configuration类的实例首先定位映射文档的位置 读取这些配置 然后创建一个SessionFactory对象
    一个org.hibernate.cfg.Configuration实例代表了一个应用程序中Java类型到SQL数据库映射的完整集合。Configuration被用来构建一个不可变的SessionFactory,映射定义则由不同的XML映射定义文件编译而来。
   Configuration有以下几个方面的操作函数
 
1  为Configuration指定映射文件
你可以直接实例化Configuration来获取一个实例 并为他指定XML映射定义文件 如果映射定义文件在类路径中 请使用addResource()
  1. Configuration cfg = new Configuration().addResource("com/demo/hibernate/beans/User.hbm.xml");

 

 

2  为Configuration指定持久化类

一个替代的方法是指定被映射的类 让Hibernate帮你寻找映射定义文件

 

  1. Configuration cfg = new Configuration().addClass(com.demo.hibernate.beans.User.class);

Hibernate将会在类路径中需找名字为 /com/demo/hibernate/beans/User.hbm.xml 映射定义文件 消除了任何对文件名的硬编译

3  为Configuration指定配置属性

Configuration也允许指定配置属性

  1. Configuration cfg =new Configuration().addClass(com.demo.hibernate.beans.User.class)
  2. .setProperty("hibernate.dialect","org.hibernate.dialect.MySQLInnoDBDialect")
  3. .setProperty("hibernate.connection.datasource","java:comp/env/jdbc/test")
  4. .setProperty("hibernate.order_update","true");

 

4  Configuration的三种加载方式

Hibernate的启动与开发流程中 要使用一个Configuration 需要为他设置三个方面的内容

数据库连接属性

hbm.xml文件

POJO类

其中 第二个和第三个只需要设置一个 就会自动需找另一个 因为这两者只需一个

第一种方式是使用hibernate.cfg.xml 该文件设置了数据库连接的属性和hbm.xml映射文件配置 hibernate会自动加载该配置属性 并自动找到POJO 因此要取得Configuration对象 只需要简单的创建改对象即可

  1. Configuration cfg = new Configuration();
  2. cfg.configuration("hibernate.cfg.xml");

第二种方式是通过hibernate.properties  省略 

第三种方式是完全在构造时进行硬编码设置 设置过程如下所示

  1. Configuration cfg =new Configuration()
  2. .addClass(com.demo.hibernate.beans.User.class)
  3. .setProperty("hibernate.dialect","org.hibernate.dialect.MySQLInnoDBDialect")
  4. .setProperty("hibernate.connection.datasource","java:comp/env/jdbc/test")
  5. .setProperty("hibernate.order_update","true");

 

第一种方式是我们最常用的方式

 

2.Hibernate加载基本的配置信息源码浅析

我们在获取SessionFactory的时候,第一个语句就是:

  1. Configuration configuration = new Configuration();  

查看源码可知,Configuration类的公共构造方法只有一个,并且是无参数的:

  1. public Configuration() {  
  2.     this( new SettingsFactory() );  
  3. }  

这个构造方法调用了一个受保护的构造方法:

  1. protected Configuration(SettingsFactory settingsFactory) {  
  2.     this.settingsFactory = settingsFactory;  
  3.     reset();  
  4. }  

受保护的构造方法里调用了私有方法reset,reset方法里有一句这样的代码:

  1. properties = Environment.getProperties();  

这句代码会从Hibernate环境里去获取hibernate配置,查看Environment类的方法getProperties:

  1. public static Properties getProperties() {  
  2.     Properties copy = new Properties();  
  3.     copy.putAll(GLOBAL_PROPERTIES);  
  4.     return copy;  
  5. }  

这个方法得到的是全局配置属性的副本。再看看全局属性GLOBAL_PROPERTIES是如何初始化的。Environment的静态代码块里有如下代码:

  1. InputStream stream = ConfigHelper.getResourceAsStream("/hibernate.properties");  
  2.     try {  
  3.         GLOBAL_PROPERTIES.load(stream);  
  4.         log.info( "loaded properties from resource hibernate.properties: "   
  5.                         + PropertiesHelper.maskOut(GLOBAL_PROPERTIES, PASS) );  
  6. }  

代码跟踪到这里,我们可以知道hibernate首先一定会加载属性配置文件hibernate.properties,而且此文件的路径是写死的。
如果用户想用XML配置hibernate,就需要编写如下代码:

  1. configuration.configure();  

 

configure有五个重载方法:

看一下缺省的重载方法:

  1. public Configuration configure() throws HibernateException {  
  2.     configure( "/hibernate.cfg.xml" );  
  3.     return this;  
  4. }  

它调用的是另一个重载方法,加载的配置文件是写死的。这五个方法最后调用同一个方法doConfigure(Document doc):

  1. protected Configuration doConfigure(Document doc) throws HibernateException {  
  2.         Element sfNode = doc.getRootElement().element( "session-factory" );  
  3.         String name = sfNode.attributeValue( "name" );  
  4.         if ( name != null ) {  
  5.             properties.setProperty( Environment.SESSION_FACTORY_NAME, name );  
  6.         }  
  7.         addProperties( sfNode );  
  8.         parseSessionFactory( sfNode, name );  
  9.   
  10.         Element secNode = doc.getRootElement().element( "security" );  
  11.         if ( secNode != null ) {  
  12.             parseSecurity( secNode );  
  13.         }  
  14.   
  15.         log.info( "Configured SessionFactory: " + name );  
  16.         log.debug( "properties: " + properties );  
  17.   
  18.         return this;  
  19. }  

看一下方法addProperties:

  1. private void addProperties(Element parent) {  
  2.         Iterator itr = parent.elementIterator( "property" );  
  3.         while ( itr.hasNext() ) {  
  4.             Element node = (Element) itr.next();  
  5.             String name = node.attributeValue( "name" );  
  6.             String value = node.getText().trim();  
  7.             log.debug( name + "=" + value );  
  8.             properties.setProperty( name, value );  
  9.             if ( !name.startsWith( "hibernate" ) ) {  
  10.                 properties.setProperty( "hibernate." + name, value );  
  11.             }  
  12.         }  
  13.         Environment.verifyProperties( properties );  
  14. }  

可以看到,如果配置属性名不是以“hibernate”开头会自动加上“hibernate.”,这就是为什么,我们在用XML配置hibernate的时候,属性名“hibernate.”可以省去,但是在使用属性文件或者编程方式配置时,“hibernate.”是不能省掉的。

hibernate加载配置的顺序是:properties配置——》XML配置或者编程方式配置。至于是先加载XML配置还是编程方式的配置,就要看用户的语句顺序了,但是有一点是确定的:后加载的配置会覆盖先加载的配置。

最后我们也可以看到,hibernate将基本的配置信息(不包括实体映射信息)保存到了configuration的properties成员属性中。

获取sessionFactory:

  1. SessionFactory sessionFactory = configuration.buildSessionFactory();  

 

3.SessionFactory创建Session

 

SessionFactory在Hibernate中实际上起到了一个缓冲区的作用 他缓冲了HIbernate自动生成SQL语句和其他的映射数据 还缓冲了一些将来有可能重复利用的数据
    为了能创建一个SessionFactory对象 应该在Hibernate初始化的时候创建一个Configuration类的实例 并将已经写好的映射文件交给他处理 这样Configuration对象就可以创建一个SessionFactory对象 当SessionFactory对象创建成功后 Configuration对象就没用用了 就可以简单的抛弃他
示例代码:
  1. Configuration cfg = new Configuration();
  2. cfg.addResource("com/demo/hibernate/beans/User.hbm.xml");
  3. cfg.setProperty(System.getProperties());
  4. SessionFactory sessionFactory = cfg.buildSessionFactory();

 

SessionFactory用到了一个设计模式 工厂模式 用户程序从工程类SessionFactory取得Session实例 设计者的意图就是让它能在整个应用中共享 典型的来说 一个项目通常只需要一个SessionFactory就够了 因此我们就设计了HibernateSessionFactory.java这个辅助类 定义了一个静态的Configuration和SessionFactory对象

 

  1. private static final Configuration cfg = new Configuration();
  2. private static org.hibernate.SessionFactory sessionFactory;

这两个对象对整个应用来说只有一个实例存在 因此为用户的访问定义一个本地线程变量:

 

  1. private static final ThreadLocal threadLocal = new ThreadLocal();

 

该线程变量是静态的 对每一个访问该线程的用户产生一个实例 这样在要取得Session对象时 首先从当前用户的线程中取得Session对象 如果还没有创建 则从SessionFactory中创建一个Session 此时会判断SessionFactory对象是否已经创建 该对象对这个应用来说 只有一个 因此 只有第一次访问该变量的用户才会创建该对象

HibernateSessionFactory.java 取得Session对象的过程如下表示

 

  1. public static Session currentSession() throws HibernateException {
  2.         Session session = (Session) threadLocal.get();
  3.         if (session == null) {
  4.             if (sessionFactory == null) {
  5.                 try {
  6.                     cfg.configure(CONFIG_FILE_LOCATION);
  7.                     sessionFactory = cfg.buildSessionFactory();
  8.                 }
  9.                 catch (Exception e) {
  10.                     System.err.println("%%%% Error Creating SessionFactory %%%%");
  11.                     e.printStackTrace();
  12.                 }
  13.             }
  14.             session = sessionFactory.openSession();
  15.             threadLocal.set(session);
  16.         }
  17.         return session;
  18.     }

首先判断threadLocal中是否存在Session对象 如果不存在 则创建Session对象 在创建Session对象时 首先要判断系统是否已经加载Configuration 如果没有sessionFactory 则需要先创建该对象 创建完成的Session对象 需要保存在threadLocal中以供本次访问线程的下一次调用

在关闭Session对象是 只需要从当前线程中取得Session对象 关闭该对象 并置空本地线程变量即可

 

  1. public static void closeSession() throws HibernateException {
  2.         Session session = (Session) threadLocal.get();
  3.         threadLocal.set(null);
  4.         if (session != null) {
  5.             session.close();
  6.         }
  7.     }
 

4.Hibernate核心接口

Hibernate作为持久成中间件,它的具体实现对与上层调用是透明的,即上层通过接口来调用Hibernate的具体实现,所以对于入门级别的讨论来说,自然应该先从接口开始了。
 
所有的Hibernate应用都会访问它的5个核心接口,分别如下:
Configuration接口:
SessionFactory接口:
Session接口:
Transaction接口:
Query和Criteria接口:
 
分别简单介绍一下:
1、Configuration接口
Configuration用于配置并启动HibernateHibernate应用通过Configuration的实例来指定对象-关系映射文件,或通过Configuration动态配置Hibernate的属性,然后通过Configuration来创建相应的SessionFactory实例。
 
2、SessionFactory接口
一个SessionFactory对应一个数据源,它是个重量级对象,不可随意生成多个实例。对于一般的单数据库应用来说,只需要一个SessionFactory就足够了。当然如果有多个数据库的话,还是需要为每个数据库生成对应的SessionFactory。它是线程安全的,同一个实例可以被应用中的多个线程共享。
也许你会很好奇,SessionFactory为什么是重量级对象呢?我也同样好奇,通过查看Hibernate的源码,发现SessionFactory存放了大量预定义的SQL语句以及映射元数据,所以自然需要很大的缓存了,同时需要一定的CPU时间来计算生成。想想Hibernate的这个设计是很有意义的,因为有了Mapping文件,很多SQL语句就已经确定了,只需要动态生成一次就可以了,这个设计也是为了提高持久化的效率。
 
3、Session接口
从SessionFactory中可以获得Session实例。
Session接口是Hibernate应用中使用最广泛的接口了,它是持久化管理器,提供添加、更新、删除、加载、查询对象。Session不是线程安全的,所以应避免多个线程共享同一个Session实例。Session是轻量级对象,它的创建和销毁不需要太多资源,这意味着在应用中可以经常创建和销毁Session对象。
Session有一个缓存,称之为Hibernate的一级缓存,它存放当前工作单元加载的持久化对象,每个Session都有自己的缓存,缓存中的对象只能被当前工作单元访问。
 
4、Transaction接口
Transaction是Hibernate的数据库事务接口,它对底层道德事务接口进行了封装,底层事务接口包括:
    JDBC API
    JTA(Java Transaction API)
    CORBA(Common Object Requet Broker Architecture) API
Hibernate应用可以通过一致Transaction接口来声明事务边界,这有助于应用可以在不同的环境或容器中移植。具体的事务实现使用在Hibernate.properties中进行指定。
 
5、Query和Criteria接口
这两个是Hibernate的查询接口,用于向数据库查询对象,以及控制执行查询的过程。Query实例包装了一个HQL(Hibernate Query Language)来查询。Criteria接口完全封装了基于字符串形式的查询语句,比Query更面向对象,Criteria更擅长执行动态查询。

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. public static ConnectionProvider newConnectionProvider(Properties properties, Map connectionProviderInjectionData)  
    2.             throws HibernateException {  
    3.         ConnectionProvider connections;  
    4.         String providerClass = properties.getProperty( Environment.CONNECTION_PROVIDER );//hibernate.connection.provider_class  
    5.         if ( providerClass != null ) {  
    6.             connections = initializeConnectionProviderFromConfig( providerClass );  
    7.         }  
    8.         else if ( c3p0ConfigDefined( properties ) && c3p0ProviderPresent() ) {  
    9.             connections = initializeConnectionProviderFromConfig("org.hibernate.connection.C3P0ConnectionProvider");  
    10.         }  
    11.         else if ( properties.getProperty( Environment.DATASOURCE ) != null ) {//hibernate.connection.datasource  
    12.             connections = new DatasourceConnectionProvider();  
    13.         }  
    14.         else if ( properties.getProperty( Environment.URL ) != null ) {//hibernate.connection.url  
    15.             connections = new DriverManagerConnectionProvider();  
    16.         }  
    17.         else {  
    18.             connections = new UserSuppliedConnectionProvider();  
    19.         }  
posted @ 2018-03-21 17:17  ichar  阅读(122)  评论(0编辑  收藏  举报