Hibernate 5.x 生成 SessionFactory 源码跟踪分析

Posted Uzipiの柚子皮

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Hibernate 5.x 生成 SessionFactory 源码跟踪分析相关的知识,希望对你有一定的参考价值。

我们要使用 Hibernate 的功能,首先需要读取 Hibernate 的配置文件,根据配置启动 Hibernate ,然后创建 SessionFactory。

创建 SessionFactory 的代码很简单,这也是我们要分析的代码:

Configuration cfg = new Configuration().configure();
SessionFactory factory = cfg.buildSessionFactory();

接下来,就针对这两行代码进行分析。

1、初始化 Configuration

首先,我们来看看初始化 Configuration 实例的源码中都做了些什么:

public Configuration() {
    this( new BootstrapServiceRegistryBuilder().build() );
}

无参构造器调用了重载的构造器,接着看重载的构造器,Configuration.class 第121行:

public Configuration(BootstrapServiceRegistry serviceRegistry) {
    this.bootstrapServiceRegistry = serviceRegistry;
    this.metadataSources = new MetadataSources( serviceRegistry );
    reset();
}

在这个构造器中,有个reset() 方法我们等下再看。我们先来看看传入的参数 serviceRegistry,以及初始化的两个成员属性:

  • BootstrapServiceRegistry 是个接口,翻译过来是“启动服务注册器”,是 Hibernate 底层的的基础服务注册器。
  • MetadataSources 元数据来源,构造器接收了 BootstrapServiceRegistry 的实例

看一下这个构造器的源码,MeatadataSources.class 第 77 行:

// 使用指定的服务注册器实例创建一个 Metadata “元数据的源”
public MetadataSources(ServiceRegistry serviceRegistry) {

    // 传入的参数还只能是 BootstrapServiceRegistry 或 StandardServiceRegistry 类型的实例
    if ( ! isExpectedServiceRegistryType( serviceRegistry ) ) {
        LOG.debugf(
                "Unexpected ServiceRegistry type [%s] encountered during building of MetadataSources; may cause " +
                        "problems later attempting to construct MetadataBuilder",
                serviceRegistry.getClass().getName()
        );
    }
    this.serviceRegistry = serviceRegistry;
    this.xmlMappingBinderAccess = new XmlMappingBinderAccess( serviceRegistry );
}

好像看不太明白,那我们来看看这个 MetadataSource 类的说明注释:

Entry point into working with sources of metadata information (mapping XML, annotations). Tell Hibernate about sources and then call buildMetadata() , or use getMetadataBuilder() to customize how sources are processed (naming strategies, etc).

加载元数据信息(一般是XML文件、注解中配置的映射)。Hibernate 加载该信息之后,通过调用 buildMetadata() 或者 getMetadataBuilder() 方法来确定整个 Hibernate 运行时环境的执行策略。

看到这里,可以认为 MetadataSource 的就是用来加载配置信息的。其实在 Metadata 中,就存储了 ORM 的映射信息。

构造方法的最后一行代码:

this.xmlMappingBinderAccess = new XmlMappingBinderAccess( serviceRegistry );

我们继续跟踪看看 XmlMappingBinderAccess 的构造方法做了些什么:

public XmlMappingBinderAccess(ServiceRegistry serviceRegistry) {
    this.classLoaderService = serviceRegistry.getService( ClassLoaderService.class );

    // NOTE : 参数中的 boolean 值表示在加载 XML 文件时是否执行验证。
    // 在这里显式指定为true,可以让运行时的 JAXP(XML处理的Java API)和 JAXB(根据XML生成Java类)启动速度更快,
    // 如果不验证 XML,可能会因为 XML 中的一些小错误而导致大麻烦。
    this.mappingBinder 
        = new MappingBinder( serviceRegistry.getService( ClassLoaderService.class ), true );
}

好了,我们现在知道这行代码是在做 XML 文件中绑定映射初始化相关的处理


继续回到 Configuration(BootstrapServiceRegistry serviceRegistry) 重载构造器中。

先简单说明一下 Hibernate 中的 Service:

BootstrapServiceRegistry 接口的父接口是 ServiceRegistry,这个接口的父接口是 Service
Hibernate 将所有的底层的功能都封装为 Service 注册到 ServiceRegistry 中,需要的时候通过 getService() 方法获取即可。

刚刚留下了一行 reset() 方法没有看,我们现在来看看,Configuration.class 第 149 行:

protected void reset() {
    implicitNamingStrategy = ImplicitNamingStrategyJpaCompliantImpl.INSTANCE;
    physicalNamingStrategy = PhysicalNamingStrategyStandardImpl.INSTANCE;
    namedQueries = new HashMap<String,NamedQueryDefinition>();
    namedSqlQueries = new HashMap<String,NamedSQLQueryDefinition>();
    sqlResultSetMappings = new HashMap<String, ResultSetMappingDefinition>();
    namedEntityGraphMap = new HashMap<String, NamedEntityGraphDefinition>();
    namedProcedureCallMap = new HashMap<String, NamedProcedureCallDefinition>(  );

    // 初始化 standardServiceRegistryBuilder 成员变量
    standardServiceRegistryBuilder = new StandardServiceRegistryBuilder( bootstrapServiceRegistry );
    entityTuplizerFactory = new EntityTuplizerFactory();
    interceptor = EmptyInterceptor.INSTANCE;
    properties = new Properties(  );
    properties.putAll( standardServiceRegistryBuilder.getSettings());
}

implicitNamingStrategyphysicalNamingStrategy 就是 Hibernate 的命名策略相关的实例:

  • implicitNamingStrategy:隐式命名策略
  • physicalNamingStrategy:物理命名策略

命名策略有多种实现方式:Hibernate 标准,JPA 标准。可以调用 Configuration 对象的 setImplicitNamingStrategy()setPhysicalNamingStrategy() 方法设置命名策略。

扩展:Hibernate中实体映射时的命名策略

引用一张图片,显示了 Hibernate 5.x 中的命名策略的关系:

技术分享图片

在这里先不对命名策略做太细化的研究,我们接着看下面几行代码中的 HashMap 实例分别代表什么:

  • namedQuerys 读取映射文件中<query>元素的内容或注解,该元素用于定义 HQL 语句
  • namedSqlQueries 读取映射文件中<sql-query>元素的内容或注解,该元素用于定义 SQL 语句
  • sqlResultSetMappings 读取映射文件中 SQL 查询结果集的结构内容。
  • namedEntityGraphMap 读取映射文件或注解中的 EntityGraph 配置,是 JPA 2.1 的新规范
  • namedProcedureCallMap 存储过程相关的配置

我对 reset() 方法小结一下:重置初始化了另一些成员变量(囧)

至此,Configuration 的初始化过程就已经完成了。


接着调用 Configuration 对象的 configure() 方法,可以重载该方法指定配置文件的资源路径。

我们先来看看无参的方法:

// 在程序的资源路径下,读取文件名为 hibernate.cfg.xml 映射配置文件
public Configuration configure() throws HibernateException {
    return configure( StandardServiceRegistryBuilder.DEFAULT_CFG_RESOURCE_NAME );
}

再来看看有参的方法:

// 读取符合 hibernate-configuration-3.0.dtd 文档类型规范的配置文件
public Configuration configure(String resource) throws HibernateException {
    standardServiceRegistryBuilder.configure( resource );
    // ...
    properties.putAll( standardServiceRegistryBuilder.getSettings() );
    return this;
}

我们可以发现, configure() 方法内部调用了 standardServiceRegistryBuilder.configure( resource );,如果我们只是使用 Configuration 对象,那么这个方法就没有作用,这里我们还是追踪了看一下,追踪到类 StandardServiceRegistryBuilder,看看其中的 configure() 做了什么处理:

// 从指定的资源位置,读取 XML 文件获取配置信息(常用)
public StandardServiceRegistryBuilder configure(String resourceName) {
    return configure( configLoader.loadConfigXmlResource( resourceName ) );
}

// 指定 File 对象
public StandardServiceRegistryBuilder configure(File configurationFile) {
    return configure( configLoader.loadConfigXmlFile( configurationFile ) );
}

// 可以获取网络上的指定路径URL
public StandardServiceRegistryBuilder configure(URL url) {
    return configure( configLoader.loadConfigXmlUrl( url ) );
}

// 有多个 cfg.xml 文件时,合并它们
public StandardServiceRegistryBuilder configure(LoadedConfig loadedConfig) {
    aggregatedCfgXml.merge( loadedConfig );
    settings.putAll( loadedConfig.getConfigurationValues() );
    return this;
}

最终返回了 StandardServiceRegistryBuilder 对象,这个对象用作 Hibernate 5.x 中创建 SessionFactory。关于 Hibernate 5.x 的创建方式,将在另一篇文章中讲解。

2、创建 SessionFactory

接下来,看我们要分析的第二行代码:

SessionFactory factory = cfg.buildSessionFactory();

调用 Configuration 对象的 buildSessionFactory() 方法。我们进到这个方法看看里面是什么:

// 使用当前 configuration 配置对象中的配置信息创建一个 SessionFactory 实例,该实例被创建后就不会再改变
// 此后再对 configuration 做修改也不会影响到已创建的 SessionFactory 实例
public SessionFactory buildSessionFactory() throws HibernateException {
    log.debug( "Building session factory using internal StandardServiceRegistryBuilder" );
    // ----- 1、使用 properties 重置配置属性 -----
    standardServiceRegistryBuilder.applySettings( properties );
    return buildSessionFactory( standardServiceRegistryBuilder.build() );
}

public SessionFactory buildSessionFactory(ServiceRegistry serviceRegistry) throws HibernateException {
    log.debug( "Building session factory using provided StandardServiceRegistry" );

    // ----- 2、创建 MetadataBuilder ,然后做一些配置工作 -----
    final MetadataBuilder metadataBuilder = metadataSources
        .getMetadataBuilder( (StandardServiceRegistry) serviceRegistry );
    // 设置默认的隐式命名策略
    if ( implicitNamingStrategy != null ) {
        metadataBuilder.applyImplicitNamingStrategy( implicitNamingStrategy );
    }
    // 设置默认的物理命名策略
    if ( physicalNamingStrategy != null ) {
        metadataBuilder.applyPhysicalNamingStrategy( physicalNamingStrategy );
    }
    // 设置共享缓存模式
    if ( sharedCacheMode != null ) {
        metadataBuilder.applySharedCacheMode( sharedCacheMode );
    }
    if ( !typeContributorRegistrations.isEmpty() ) {
        for ( TypeContributor typeContributor : typeContributorRegistrations ) {
            metadataBuilder.applyTypes( typeContributor );
        }
    }
    if ( !basicTypes.isEmpty() ) {
        for ( BasicType basicType : basicTypes ) {
            metadataBuilder.applyBasicType( basicType );
        }
    }
    if ( sqlFunctions != null ) {
        for ( Map.Entry<String, SQLFunction> entry : sqlFunctions.entrySet() ) {
            metadataBuilder.applySqlFunction( entry.getKey(), entry.getValue() );
        }
    }
    if ( auxiliaryDatabaseObjectList != null ) {
        for ( AuxiliaryDatabaseObject auxiliaryDatabaseObject : auxiliaryDatabaseObjectList ) {
            metadataBuilder.applyAuxiliaryDatabaseObject( auxiliaryDatabaseObject );
        }
    }
    if ( attributeConverterDefinitionsByClass != null ) {
        for ( AttributeConverterDefinition attributeConverterDefinition : attributeConverterDefinitionsByClass.values() ) {
            metadataBuilder.applyAttributeConverter( attributeConverterDefinition );
        }
    }

    // ----- 3、使用 MetadataBuilder 创建 Metadata 实例 -----
    final Metadata metadata = metadataBuilder.build();
    
    // 使用 Metadata 对象的 getSessionFactoryBuilder() 创建 SessionFactoryBuilder
    final SessionFactoryBuilder sessionFactoryBuilder = 
         metadata.getSessionFactoryBuilder();
    if ( interceptor != null && interceptor != EmptyInterceptor.INSTANCE ) {
        sessionFactoryBuilder.applyInterceptor( interceptor );
    }
    if ( getSessionFactoryObserver() != null ) {
        // 为 SessionFactory 添加观察者 Observers
        sessionFactoryBuilder.addSessionFactoryObservers( getSessionFactoryObserver() );
    }
    if ( getEntityNotFoundDelegate() != null ) {
        sessionFactoryBuilder.applyEntityNotFoundDelegate( getEntityNotFoundDelegate() );
    }
    if ( getEntityTuplizerFactory() != null ) {
        sessionFactoryBuilder.applyEntityTuplizerFactory( getEntityTuplizerFactory() );
    }
    if ( getCurrentTenantIdentifierResolver() != null ) {
        sessionFactoryBuilder.applyCurrentTenantIdentifierResolver( getCurrentTenantIdentifierResolver() );
    }
    
    // 4、创建并返回 SessionFactory 实例
    return sessionFactoryBuilder.build();
}

buildSessionFactory() 方法的内容比较多,但是主要就是3个核心步骤:

  1. 创建 MetadataBuilder ,然后做一些配置工作
  2. 使用 MetadataBuilder 创建 Metadata 实例,metadata 中存储了所有的 ORM 映射信息
  3. 创建并返回 SessionFactory 实例

至此,Hibernate 5.x 创建 SessionFactory 的源码全部走完。

以上是关于Hibernate 5.x 生成 SessionFactory 源码跟踪分析的主要内容,如果未能解决你的问题,请参考以下文章

Hibernate 5.x 生成 SessionFactory 源码跟踪分析

hibernate

增删改查

Hibernate4获得Session

EasyUI 页面分页

spring和hibernate 基础的增删改