Configuration接口的做用是对Hibernate进行配置 以及对他进行启动 在Hibernate的启动过程当中 Configuration类的实例首先定位映射文档的位置 读取这些配置 而后建立一个SessionFactory对象java
一个org.hibernate.cfg.Configuration实例表明了一个应用程序中Java类型到SQL数据库映射的完整集合。Configuration被用来构建一个不可变的SessionFactory,映射定义则由不一样的XML映射定义文件编译而来。node
Configuration有如下几个方面的操做函数数据库
1 为Configuration指定映射文件编程
你能够直接实例化Configuration来获取一个实例 并为他指定XML映射定义文件 若是映射定义文件在类路径中 请使用addResource()设计模式
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也容许指定配置属性session
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的三种加载方式app
在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");
第一种方式是咱们最经常使用的方式
咱们在获取SessionFactory的时候,第一个语句就是:
Configuration configuration = new Configuration();
查看源码可知,Configuration类的公共构造方法只有一个,而且是无参数的:
public Configuration() { this( new SettingsFactory() ); }
这个构造方法调用了一个受保护的构造方法:
protected Configuration(SettingsFactory settingsFactory) { this.settingsFactory = settingsFactory; reset(); }
受保护的构造方法里调用了私有方法reset,reset方法里有一句这样的代码:
properties = Environment.getProperties();
这句代码会从Hibernate环境里去获取hibernate配置,查看Environment类的方法getProperties:
public static Properties getProperties() { Properties copy = new Properties(); copy.putAll(GLOBAL_PROPERTIES); return copy; }
这个方法获得的是全局配置属性的副本。再看看全局属性GLOBAL_PROPERTIES是如何初始化的。Environment的静态代码块里有以下代码:
InputStream stream = ConfigHelper.getResourceAsStream("/hibernate.properties"); try { GLOBAL_PROPERTIES.load(stream); log.info( "loaded properties from resource hibernate.properties: " + PropertiesHelper.maskOut(GLOBAL_PROPERTIES, PASS) ); }
代码跟踪到这里,咱们能够知道hibernate首先必定会加载属性配置文件hibernate.properties,并且此文件的路径是写死的。
若是用户想用XML配置hibernate,就须要编写以下代码:
(properties 是直接用构造方法 new Configuration(),这里是生成后调用configure())
configuration.configure();
configure有五个重载方法:
看一下缺省的重载方法:
public Configuration configure() throws HibernateException { configure( "/hibernate.cfg.xml" ); return this; }
它调用的是另外一个重载方法,加载的配置文件是写死的。这五个方法最后调用同一个方法doConfigure(Document doc):
protected Configuration doConfigure(Document doc) throws HibernateException { Element sfNode = doc.getRootElement().element( "session-factory" ); String name = sfNode.attributeValue( "name" ); if ( name != null ) { properties.setProperty( Environment.SESSION_FACTORY_NAME, name ); } addProperties( sfNode ); parseSessionFactory( sfNode, name ); Element secNode = doc.getRootElement().element( "security" ); if ( secNode != null ) { parseSecurity( secNode ); } log.info( "Configured SessionFactory: " + name ); log.debug( "properties: " + properties ); return this; }
看一下方法addProperties:
private void addProperties(Element parent) { Iterator itr = parent.elementIterator( "property" ); while ( itr.hasNext() ) { Element node = (Element) itr.next(); String name = node.attributeValue( "name" ); String value = node.getText().trim(); log.debug( name + "=" + value ); properties.setProperty( name, value ); if ( !name.startsWith( "hibernate" ) ) { properties.setProperty( "hibernate." + name, value ); } } Environment.verifyProperties( properties );
能够看到,若是配置属性名不是以“hibernate”开头会自动加上“hibernate.”,这就是为何,咱们在用XML配置hibernate的时候,属性名“hibernate.”能够省去,可是在使用属性文件或者编程方式配置时,“hibernate.”是不能省掉的。
hibernate加载配置的顺序是:properties配置——》XML配置或者编程方式配置。至因而先加载XML配置仍是编程方式的配置,就要看用户的语句顺序了,可是有一点是肯定的:后加载的配置会覆盖先加载的配置。
最后咱们也能够看到,hibernate将基本的配置信息(不包括实体映射信息)保存到了configuration的properties成员属性中。
获取sessionFactory:
SessionFactory sessionFactory = configuration.buildSessionFactory();
SessionFactory在Hibernate中实际上起到了一个缓冲区的做用 他缓冲了HIbernate自动生成SQL语句和其余的映射数据 还缓冲了一些未来有可能重复利用的数据
为了能建立一个SessionFactory对象 应该在Hibernate初始化的时候建立一个Configuration类的实例 并将已经写好的映射文件交给他处理 这样Configuration对象就能够建立一个SessionFactory对象 当SessionFactory对象建立成功后 Configuration对象就没用用了 就能够简单的抛弃他
示例代码:
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;
这两个对象对整个应用来讲只有一个实例存在 所以为用户的访问定义一个本地线程变量:
该线程变量是静态的 对每个访问该线程的用户产生一个实例 这样在要取得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(); } }
Hibernate做为持久成中间件,它的具体实现对与上层调用是透明的,即上层经过接口来调用Hibernate的具体实现,因此对于入门级别的讨论来讲,天然应该先从接口开始了。
全部的Hibernate应用都会访问它的5个核心接口,分别以下:
Configuration接口:
SessionFactory接口:
Session接口:
Transaction接口:
Query和Criteria接口:
----------------------------------
分别简单介绍一下:
一、Configuration接口
Configuration用于配置并启动Hibernate。Hibernate应用经过Configuration的实例来指定对象-关系映射文件,或经过Configuration动态配置Hibernate的属性,而后经过Configuration来建立相应的SessionFactory实例。
二、SessionFactory接口
一个SessionFactory对应一个数据源,它是个重量级对象,不可随意生成多个实例。对于通常的单数据库应用来讲,只须要一个SessionFactory就足够了。固然若是有多个数据库的话,仍是须要为每一个数据库生成对应的SessionFactory。它是线程安全的,同一个实例能够被应用中的多个线程共享。
也许你会很好奇,SessionFactory为何是重量级对象呢?我也一样好奇,经过查看Hibernate的源码,发现SessionFactory存放了大量预约义的SQL语句以及映射元数据,因此天然须要很大的缓存了,同时须要必定的CPU时间来计算生成。想一想Hibernate的这个设计是颇有意义的,由于有了Mapping文件,不少SQL语句就已经肯定了,只须要动态生成一次就能够了,这个设计也是为了提升持久化的效率。
三、Session接口
从SessionFactory中能够得到Session实例。
Session接口是Hibernate应用中使用最普遍的接口了,它是持久化管理器,提供添加、更新、删除、加载、查询对象。Session不是线程安全的,因此应避免多个线程共享同一个Session实例。Session是轻量级对象,它的建立和销毁不须要太多资源,这意味着在应用中能够常常建立和销毁Session对象。
Session有一个缓存,称之为Hibernate的一级缓存,它存放当前工做单元加载的持久化对象,每一个Session都有本身的缓存,缓存中的对象只能被当前工做单元访问。
四、Transaction接口
Transaction是Hibernate的数据库事务接口,它对底层道德事务接口进行了封装,底层事务接口包括:
JDBC API
JTA(Java Transaction API)
CORBA(Common Object Requet Broker Architecture) API
Hibernate应用能够经过一致Transaction接口来声明事务边界,这有助于应用能够在不一样的环境或容器中移植。具体的事务实现使用在Hibernate.properties中进行指定。
五、Query和Criteria接口
这两个是Hibernate的查询接口,用于向数据库查询对象,以及控制执行查询的过程。Query实例包装了一个HQL(Hibernate Query Language)来查询。Criteria接口彻底封装了基于字符串形式的查询语句,比Query更面向对象,Criteria更擅长执行动态查询。
Hibernate是如何初始化链接池的呢?先看下图:
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)方法就知道了:
public static ConnectionProvider newConnectionProvider(Properties properties, Map connectionProviderInjectionData) throws HibernateException { ConnectionProvider connections; String providerClass = properties.getProperty( Environment.CONNECTION_PROVIDER );//hibernate.connection.provider_class if ( providerClass != null ) { connections = initializeConnectionProviderFromConfig( providerClass ); } else if ( c3p0ConfigDefined( properties ) && c3p0ProviderPresent() ) { connections = initializeConnectionProviderFromConfig("org.hibernate.connection.C3P0ConnectionProvider"); } else if ( properties.getProperty( Environment.DATASOURCE ) != null ) {//hibernate.connection.datasource connections = new DatasourceConnectionProvider(); } else if ( properties.getProperty( Environment.URL ) != null ) {//hibernate.connection.url connections = new DriverManagerConnectionProvider(); } else { connections = new UserSuppliedConnectionProvider(); } ...... connections.configure( properties ); return connections; }
上面是核心代码。
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。