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());
}
implicitNamingStrategy
和 physicalNamingStrategy
就是 Hibernate 的命名策略相关的实例:
implicitNamingStrategy
:隐式命名策略physicalNamingStrategy
:物理命名策略
命名策略有多种实现方式:Hibernate 标准,JPA 标准。可以调用 Configuration 对象的 setImplicitNamingStrategy()
和 setPhysicalNamingStrategy()
方法设置命名策略。
引用一张图片,显示了 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个核心步骤:
- 创建 MetadataBuilder ,然后做一些配置工作
- 使用 MetadataBuilder 创建 Metadata 实例,metadata 中存储了所有的 ORM 映射信息
- 创建并返回 SessionFactory 实例
至此,Hibernate 5.x 创建 SessionFactory 的源码全部走完。
Hibernate 5.x 生成 SessionFactory 源码跟踪分析的更多相关文章
- 1、Hibernate之生成SessionFactory源码追踪
Hibernate的所有session都是由sessionFactory来生成的,那么,sessionFactory是怎么得来的呢?它与我们配置的xxx.cfg.xml文件以及xxx.hbm.xml文 ...
- spring security之 默认登录页源码跟踪
spring security之 默认登录页源码跟踪 2021年的最后2个月,立个flag,要把Spring Security和Spring Security OAuth2的应用及主流程源码研究透 ...
- 1 weekend110的hdfs源码跟踪之打开输入流 + hdfs源码跟踪之打开输入流总结
3种形式的元数据,fsimage是在磁盘上,meta.data是在内存上, 我们继续,前面呢,断点是打在这一行代码处, FileSystem fs = FileSystem.get(conf); we ...
- Java关于ReentrantLock获取锁和释放锁源码跟踪
通过对ReentrantLock获取锁和释放锁源码跟踪主要想进一步深入学习AQS. 备注:AQS中的waitStatus状态码含义:
- Java源码跟踪阅读技巧
转:https://www.jianshu.com/p/ab865109070c 本文基于Eclipse IDE 1.Quick Type Hierarchy 快速查看类继承体系. 快捷键:Ctrl ...
- Thread.interrupt()源码跟踪
1 JDK源码跟踪 // java.lang.Thread public void interrupt() { if (this != Thread.currentThread()) checkAcc ...
- Google Protocol Buffers 快速入门(带生成C#源码的方法)
Google Protocol Buffers是google出品的一个协议生成工具,特点就是跨平台,效率高,速度快,对我们自己的程序定义和使用私有协议很有帮助. Protocol Buffers入门: ...
- spring security 之自定义表单登录源码跟踪
上一节我们跟踪了security的默认登录页的源码,可以参考这里:https://www.cnblogs.com/process-h/p/15522267.html 这节我们来看看如何自定义单表认 ...
- spring security 认证源码跟踪
spring security 认证源码跟踪 在跟踪认证源码之前,我们先根据官网说明一下security的内部原理,主要是依据一系列的filter来实现,大家可以根据https://docs.sp ...
随机推荐
- 表格插件BootStrap-Table使用教程
Bootstrap table 是一款基于 Bootstrap 的 jQuery 表格插件,功能比较完备,能够实现数据异步获取,编辑,排序等一系列功能. 官网https://bootstrap-tab ...
- 洛谷p2827蚯蚓题解
题目 算法标签里的算法什么的都不会啊 什么二叉堆?? qbxt出去学习的时候讲的,一段时间之前做的,现在才写到博客上的 维护3个队列,队列1表示最开始的蚯蚓,队列2表示每一次被切的蚯蚓被分开的较长的那 ...
- JS中 (function(){...})()立即执行函数
(function(){...})() (function(){...}()) 这是两种js立即执行函数的常见写法. 基本概念: 函数声明:function fname(){...}; 使用funct ...
- 深入理解 HTTP/1.x、HTTP/2 和 HTTPS
很多站长可能到现在都没有理解 HTTP/1.x.HTTP/2 和 HTTPS 之间的区别和关系吧?说实话,明月也是“一知半解”的水准而已,今天看到了这篇文章感觉总结还算是比较全面,特此分享出来给大家就 ...
- MOT19数据集百度云盘
图片按视频分的压缩包 [已失效] 链接: https://pan.baidu.com/s/1kNw6yhvqgitNK5N__WOpxw 提取码: yia4 链接: https://pan.baidu ...
- RuntimeError: Model class myapp.models.Test doesn't declare an explicit app_label and isn't in an application in INSTALLED_APPS.
没有添加子应用在settings里面!
- lua entry thread aborted: runtime error: /usr/../process.lua:448: attempt to concatenate field 'np_sum_duration' (a userdata value)
[1]问题场景原代码 引起问题的原代码,访问数据库,汇总数据后,使用汇总结果报异常: local function amount_sum_fee(cycleid) local select_produ ...
- 公众号后台开发(SpingMVC接收与响应公众号消息)
1.准备 1.准备服务 与微信对接的url要具备以下条件: (1)在公网上能够访问 (2)端口只支持80端口 在这里如果是公网能够访问的服务最好,也可以通过花生壳或者其他外网映射工具进行映射,比如ng ...
- Flink 源码解析 —— 项目结构一览
Flink 源码项目结构一览 https://t.zsxq.com/MNfAYne 博客 1.Flink 从0到1学习 -- Apache Flink 介绍 2.Flink 从0到1学习 -- Mac ...
- Lua代码编写规范
开发中,大量使用lua,暂时根据当前状况,总结相对而言较好的规范,在多人协作中可以更好的开发.交流. 介绍 该文档旨在为使用lua编写应用程序建立编码指南. 制订编码规范的目的: 统一编码标准,通用 ...