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 中的命名策略的关系:

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 的源码全部走完。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容

  • Hibernate: 一个持久化框架 一个ORM框架 加载:根据特定的OID,把一个对象从数据库加载到内存中OID...
    JHMichael阅读 1,956评论 0 27
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,594评论 18 139
  • 本文包括: 1、CRM 项目的整体介绍 2、Hibernate 框架概述 3、Hibernate 快速入门 4、H...
    廖少少阅读 3,463评论 9 66
  • Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库...
    兰缘小妖阅读 1,195评论 1 18
  • 目录 1. Hibernate框架的概述 1.1 Hibernate简介 1.2 为什么要学习Hibernate ...
    深海鱼Q阅读 1,013评论 0 14