转载Hibernate原理
转载一位大佬写的文章!链接是http://blog.csdn.net/he90227/article/details/50300039
可惜打不开链接叻/我也是在百度快照里面看的/担心百度快照也没了就copy下来叻/
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 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,就需要编写如下代码:
- 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();
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)方法就知道了:
- 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();
- }