1.3 SqlSessionFactory的构建与解剖


文档摘要

1.3 SqlSessionFactory 的构建与解剖 本节摘要:SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession 构成 MyBatis 的三层对象链。本节解剖全局配置文件各配置块的职责,比较三种数据源实现的取舍,整理高频 settings 开关,并说明工厂为什么必须全局单例。 上一节我们把配置文件喂给了 Builder,拿到了工厂。这一节把这台"器械柜"拆开看:配置怎么被消化、环境怎么切换、哪些开关值得记住。 三层对象链:从图纸到手术通道 三层的生命周期一句话记牢:Builder 方法级、Factory 应用级、Session 请求级。

1.3 SqlSessionFactory 的构建与解剖

本节摘要:SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession 构成 MyBatis 的三层对象链。本节解剖全局配置文件各配置块的职责,比较三种数据源实现的取舍,整理高频 settings 开关,并说明工厂为什么必须全局单例。

上一节我们把配置文件喂给了 Builder,拿到了工厂。这一节把这台"器械柜"拆开看:配置怎么被消化、环境怎么切换、哪些开关值得记住。

三层对象链:从图纸到手术通道

// 三层对象链:Builder 用完即弃,Factory 全局单例,Session 一次一台 try (InputStream in = Resources.getResourceAsStream("mybatis-config.xml")) { // 第 1 层:Builder 解析 XML,产出 Factory。解析完就没用了 SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); SqlSessionFactory factory = builder.build(in); // 第 2 层:Factory 每次调用 openSession 产出一条新通道 SqlSession session = factory.openSession(); // 第 3 层:Session 承载一次工作单元,用完关闭 // ... 业务操作 ... session.close(); }

三层的生命周期一句话记牢:Builder 方法级、Factory 应用级、Session 请求级。Builder 内部把 XML 解析成一个 Configuration 大对象,然后连同 Factory 一起丢掉——它存在的意义只是"把图纸变成器械柜"这一次性动作。

配置的加载不止 XML 一条路。除了从流读取,还可以从 Reader 读取,或完全用 Java 代码拼装(无 XML 风格)。工程上 XML 仍是主流:SQL 与配置都集中在文件里,改语句不用重新编译。

// 纯 Java 配置:不写 XML 的等价写法(适合极简场景或动态组装) DataSource ds = PooledDataSource( "com.mysql.cj.jdbc.Driver", "jdbc:mysql://localhost:3306/mybatis_db", "root", "root123"); Environment env = new Environment("dev", new JdbcTransactionFactory(), ds); Configuration cfg = new Configuration(env); cfg.addMapper(UserMapper.class); // 注册映射 cfg.setMapUnderscoreToCamelCase(true); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(cfg);

全局配置解剖:五个配置块各管什么

全局配置解剖:五个配置块各管什么

解析有顺序讲究:properties 最先被读入(后面所有配置块都能引用它的占位符),mappers 最后注册(注册时会解析所有 SQL 节点)。把连接信息抽到独立的 properties 文件,是让密码离开主配置的第一步:

<!-- 主配置引用外部属性:数据库密码不进代码库 --> <properties resource="db.properties"/> <dataSource type="POOLED"> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </dataSource>

多环境切换:environments 的真实用法

environments 的设计意图是一套配置文件装下多套环境(开发、测试、生产),靠 default 属性选一套生效:

<environments default="dev"> <environment id="dev"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="url" value="${dev.jdbc.url}"/> <!-- 其余省略 --> </dataSource> </environment> <environment id="prod"> <transactionManager type="MANAGED"/> <dataSource type="JNDI"> <!-- 生产环境从容器命名空间取数据源 --> <property name="initial_context" value="java:comp/env"/> <property name="data_source" value="jdbc/prodDs"/> </dataSource> </environment> </environments>

三种数据源的取舍值得单独记:

类型 行为 适用
UNPOOLED 每次请求新开连接,用完即关 小工具、测试代码
POOLED 内置连接池,复用连接 无容器环境的标准选择
JNDI 从外部容器取数据源 传统应用服务器部署

settings:值得记住的行为开关

开关 默认 作用
mapUnderscoreToCamelCase false 列 created_time 自动映射属性 createdTime
cacheEnabled true 二级缓存总开关(5.1 节展开)
lazyLoadingEnabled false 关联对象按需加载(3.2 节展开)
defaultExecutorType SIMPLE 执行器类型,可切 BATCH(2.3 节展开)
localCacheScope SESSION 一级缓存范围,可收窄为 STATEMENT
logImpl 未设置 指定日志实现,调试期开 STDOUT_LOGGING

这些开关在 XML 里配置,也有对应的 Java 方法(如 setMapUnderscoreToCamelCase),语义完全一致。

💡 关键直觉:全局配置在工厂构建时被一次性消化进 Configuration 对象——工厂建好后再改配置文件不会生效。这让"配置热更新"在原生 MyBatis 里没有立足点,也是工厂必须单例的深层原因:多份工厂意味着多份不一致的配置。

为什么工厂必须单例

工厂持有连接池、全部映射语句的解析结果、缓存实现与插件链。建两个工厂,等于开了两个互不相通的连接池、两套互不相通的二级缓存——资源翻倍,收益为零。标准做法是用单例模式包一层:

// 单例工厂:双重检查锁,保证全局一份 public class FactoryHolder { private static volatile SqlSessionFactory instance; public static SqlSessionFactory get() { if (instance == null) { synchronized (FactoryHolder.class) { if (instance == null) { try (InputStream in = Resources.getResourceAsStream("mybatis-config.xml")) { instance = new SqlSessionFactoryBuilder().build(in); } catch (IOException e) { throw new IllegalStateException("工厂构建失败", e); } } } } return instance; } }

变式:给上面的单例加一个 build(Environment 指定环境) 的重载,让测试代码能显式选择一套环境构建工厂——这是多环境配置最常见的落地方式(其实 Builder 的 build 方法本就带 environment 参数,试试用它替换 default 属性方案,体会两种切换风格)。

本节要点回顾

  • 三层寿命:Builder 方法级、Factory 应用级、Session 请求级,记错寿命是后续所有"会话污染"问题的起点
  • 五个配置块:properties、settings、typeAliases、environments、mappers 各管一段,解析顺序决定引用关系
  • 数据源三型:UNPOOLED 每次新建、POOLED 池化复用、JNDI 交给容器
  • 配置一次性消化:工厂建好后改配置无效,单例是纪律不是建议
  • 常用开关先记三个:驼峰映射、日志实现、执行器类型

器械柜认完了,下一节钻进从柜中取出的手术通道:SqlSession 的开合、方法族,以及一次真实的数据丢失排错。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U