MyBatis学习——第一篇(增删改查实现和源码分析)
1:MyBatis入门
1.1 Mybatis概述
MyBatis 本质是一个半自动的 ORM 框架(Object Relational Mapping,对象关系映射),核心作用是消除 JDBC 代码的冗余,把 SQL 语句从 Java 代码中抽离出来,同时完成「Java 对象」和「数据库表记录」之间的自动转换。
1.2 ORM简介
A . 简单:ORM以最基本的形式建模数据。比如ORM会将MySQL的一张表映射成一个Java类(模型),表的字段就是这个类的成员变量
B . 精确:ORM使所有的MySQL数据表都按照统一的标准精确地映射成java类,使系统在代码层面保持准确统一
C .易懂:ORM使数据库结构文档化。比如MySQL数据库就被ORM转换为了java程序员可以读懂的java类,java程序员可以只把注意力放在他擅长的java层面(当然能够熟练掌握MySQL更好)
D.易用:ORM包含对持久类对象进行CRUD操作的API,例如create(), update(), save(), load(), find(), find_all(), where()等,也就是讲sql查询全部封装成了编程语言中的函数,通过函数的链式组合生成最终的SQL语句。通过这种封装避免了不规范、冗余、风格不统一的SQL语句,可以避免很多人为Bug,方便编码风格的统一和后期维护。
1.3 ORM(对象关系映射)的优缺点:
优点:
1)它支持定制化 SQL、存储过程以及高级映射,
2)MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息
3)文档社区支持,有apache-google-github 前身是iBatis
4)配置化便于上手,动态sql,可以很方便地引入数据缓存之类的附加功能
缺点:
1)自动化进行关系数据库的映射需要消耗系统性能。其实这里的性能消耗还好啦,一般来说都可以忽略之。
2)在处理多表联查、where条件复杂之类的查询时,ORM的语法会变得复杂。
实例如下:
Java对象User()--------Mapping映射---------Relational 数据库表(User表)
Java对象 映射 关系数据库
2:在使用mybatis之前,jdbc操作数据库
我们需要创建数据库链接、写sql、查询数据、将数据封装成entity、关闭链接。这会十分的麻烦
package org.example.mybatis1.jdbc测试;
import org.example.mybatis1.pojo.User;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
/**
* 在mybatis之前,需要使用JDBC进行数据库连接和操作
* 手动将返回值封装成Java对象
* JDBC工具类
*/
public class JdbcTool {
// 配置文件中的属性
String url;
String username;
String password;
String driverClass;
/**
* 使用JDBC获取连接
*/
public Connection getConnection() throws SQLException {
try {
Class.forName(driverClass);
} catch (ClassNotFoundException e) {
throw new RuntimeException("JDBC Driver not found,哈哈",e);
}
return DriverManager.getConnection(url, username, password);
}
/**
* 测试数据库连接
*/
void testConnection() {
try {
Connection connection = getConnection();
System.out.println("数据库连接成功: " + connection);
if (connection != null && !connection.isClosed()) {
System.out.println("数据库URL: " + url);
System.out.println("用户名: " + username);
System.out.println("密码: " + password);
System.out.println("驱动类: " + driverClass);
}
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 关闭连接
*/
public void close(Connection connection) {
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public void query(Connection connection, String sql, int pram) throws SQLException {
connection.setAutoCommit(false);
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//设置查询参数
preparedStatement.setInt(1, pram);
//执行查询
ResultSet resultSet = preparedStatement.executeQuery();
//手动将结果封装成Java对象
List<User> list = new ArrayList<>();
while (resultSet.next()){
// 打印结果数据 两列字段,根据实际情况调整
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
System.out.println("ID: " + id + ", Name: " + name);
list.add(new User(id, name));
}
connection.commit();
System.out.println("查询执行完成!"+list);
}
public static void main(String[] args) throws SQLException {
JdbcTool jdbcTool = new JdbcTool();
// 设置默认值用于直接运行main方法
jdbcTool.url = "jdbc:mysql://localhost:3306/mybatis1";
jdbcTool.username = "root";
jdbcTool.password = "123456";
jdbcTool.driverClass = "com.mysql.cj.jdbc.Driver";
jdbcTool.testConnection();
System.out.println("===========分割线==============");
Connection connection = jdbcTool.getConnection();
System.out.println(connection);
//查询数据库
jdbcTool.query(connection, "select * from user where id >?", 1);
jdbcTool.close(connection);
}
}
3:Mybatis实现增删改查
说明:该案例是一个简单的mybatis入门案例 实现了数据的增删改查基本操作,更多属性配置请参考mybatis官网
主要是新建一个表M_test,包含主键、账户、密码、账户金额四个字段数据。实现简单的入门案例。
3.1:创建表:M_test id为主键自增

bean代码:
package com.thit.entity;
public class User {
private Integer id;
private String name;
private String password;
private Double account;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getname() {
return name;
}
public void setname(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Double getAccount() {
return account;
}
public void setAccount(Double account) {
this.account = account;
}
@Override
public String toString() {
return "user [id=" + id + ", name=" + name + ", password=" + password + ", account=" + account + "]";
}
}
mybatis.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 引入外部的数据配置文件 便于配置多个不同环境的数据源 -->
<properties resource="db.properties"></properties>
<environments default="demo">
<!-- environment 可以有多个 对应开发测试生产不同的环境-->
<environment id="demo">
<!-- type="JDBC" 代表使用JDBC的提交和回滚来管理事务 -->
<transactionManager type="JDBC"></transactionManager>
<!-- mybatis提供了3种数据源类型,分别是:POOLED,UNPOOLED,JNDI -->
<!-- POOLED 表示支持JDBC数据源连接池 -->
<!-- UNPOOLED 表示不支持数据源连接池 -->
<!-- JNDI 表示支持外部数据源连接池 -->
<dataSource type="POOLED">
<property name="driver" value="${mysqldriver}"/>
<property name="url" value="${mysqlurl}"/>
<property name="username" value="${mysqlusername}"/>
<property name="password" value="${mysqlpassword}"/>
</dataSource>
</environment>
</environments>
<mappers>
<!-- 引入实体映射 -->
<mapper resource="mapper/userMapper.xml"/>
</mappers>
</configuration>
数据库配置db.properties
#数据库信息
mysqldriver=com.mysql.jdbc.Driver
mysqlurl=jdbc:mysql://localhost:3306/huyiju?serverTimezone=GMT&useUnicode=true&characterEncoding=UTF-8
mysqlusername=root
mysqlpassword=123456
用户表映射配置:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.thit.entity.User">
<resultMap id="userResultMap" type="com.thit.entity.User">
<!-- 主键和字段绑定 -->
<id property="id" column="id" />
<result property="name" column="username" />
<result property="password" column="password"/>
<result property="account" column="account"/>
</resultMap>
<insert id="insert" >
insert into M_test(username,password,account) values (#{name},#{password},#{account})
</insert>
<update id="updatebyid" >
update M_test set username=#{name},password=#{password},account=#{account} where id=#{id}
</update>
<delete id="deletebyid" >
delete from M_test where
id=#{id}
</delete>
<!-- <select id="selectbyid" parameterType="int" resultType="com.thit.entity.User">
select id,username as name,password,account from M_test where
id= #{id}
</select> -->
<select id="selectbyid" parameterType="int" resultMap="userResultMap">
select id,username,password,account from M_test where
id= #{id}
</select>
<select id="selectall" resultType="com.thit.entity.User">
select id,username as name,password,account from M_test
</select>
</mapper>
最后的代码测试:
工具类:
package com.thit.test;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class Dbtools {
private static SqlSessionFactory sessionFactory;
static{
try {
String resource="mybatis.xml";
InputStream inputStream=Resources.getResourceAsStream(resource);
//sqlSessionFactory通过SqlSessionFactoryBuilder构建出DefaultSqlSessionFactory
//DefaultSqlSessionFactory继承了SqlSessionFactory接口
sessionFactory=new SqlSessionFactoryBuilder()
.build(inputStream);
//使用MyBatis提供的Resources类加载mybatis的配置文件
} catch (Exception e) {
e.printStackTrace();
}
}
//创建能执行映射文件中sql的sqlSession
public static SqlSession getSession(){
return sessionFactory.openSession();
}
}
测试主方法:测试不同的新增修改删除查询和查询全部等方法
package com.thit.test;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import com.thit.entity.User;
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
Test t=new Test();
t.selectbyid();
t.selectall();
t.updatebyid();
t.insert();
}
void selectbyid(){
Dbtools dbtools=new Dbtools();
SqlSession sqlSession= dbtools.getSession();
User u=sqlSession.selectOne("selectbyid", 1);
System.out.println("根据id查询"+u);
}
void selectall(){
Dbtools dbtools=new Dbtools();
SqlSession sqlSession= dbtools.getSession();
List<User> list=sqlSession.selectList("selectall");
System.out.println("查询所有:"+list.size());
for(User u:list) {
System.out.println(u);
}
}
void deletebyid(){
Dbtools dbtools=new Dbtools();
SqlSession sqlSession= dbtools.getSession();
int result=sqlSession.delete("deletebyid", 5);
sqlSession.commit();
System.out.println(result);
}
void updatebyid(){
Dbtools dbtools=new Dbtools();
SqlSession sqlSession= dbtools.getSession();
User u=new User();
u.setId(3);
u.setusername("土豆修改0903");
u.setPassword("123");
u.setAccount(11.11);
int a=sqlSession.update("updatebyid",u);
sqlSession.commit();
}
void insert(){
Dbtools dbtools=new Dbtools();
SqlSession sqlSession= dbtools.getSession();
User u=new User();
u.setusername("土豆新增111");
u.setPassword("3333");
u.setAccount(11.11);
int a=sqlSession.insert("insert", u);
sqlSession.commit();
}
}
最后数据库的执行结果如图:

4:Mybatis源码分析
4.1:创建SqlSessionFactory
这是自定义工具类,创建SqlSessionFactory
public class SqlSessionFactoryTool {
/**
* sqlSessionFactory 静态变量 单例
*/
static SqlSessionFactory sqlSessionFactory = null;
/*
静态代码块,初始化 sqlSessionFactory
*/
static {
try {
//1:读取mybatis-config.xml文件
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
//2:创建sqlSessionFactory,通过io流读取mybatis-config.xml
// 将mybatis的配置文件ybatis-config.xml中的所有标签解析
// 加载到sqlSessionFactory的实现类DefaultSqlSessionFactory的 configuration 对象中
sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取 sqlSession
* @return sqlSession
*/
public SqlSession getSqlSession() {
val sqlSession = sqlSessionFactory.openSession();
return sqlSession;
}
public static void main(String[] args) {
SqlSessionFactoryTool sqlSessionFactoryTool = new SqlSessionFactoryTool();
val sqlSession = sqlSessionFactoryTool.getSqlSession();
System.out.println(sqlSession);
}
}
源码分析:
紧接着我们详细分析build的源码,也就是将核心步骤二,配置文件中的环境(数据源、事务管理器)、mappers、设置(settings)、别名等读取解析之后,构造进DefaultSqlSessionFactory
/**
* 重载的build方法:核心作用是从InputStream(配置文件字节流)构建SqlSessionFactory
* @param inputStream mybatis-config.xml配置文件的字节输入流(通常来自类路径)
* @param environment 指定要使用的环境(对应mybatis-config.xml中的<environment>标签id)
* @param properties 额外的属性配置(可覆盖配置文件中的属性)
* @return SqlSessionFactory MyBatis的核心工厂类,用于创建SqlSession
*/
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
// 定义局部变量var5,用于存储最终返回的SqlSessionFactory对象
// 这里先声明是为了在try-catch-finally结构中统一返回
SqlSessionFactory var5;
try {
// ========== 核心步骤1:创建XML配置解析器 ==========
// XMLConfigBuilder是MyBatis专门用于解析mybatis-config.xml的核心类
// 入参说明:
// - inputStream:配置文件字节流(数据源)
// - environment:指定要激活的环境(如development/production)
// - properties:外部传入的属性(优先级高于配置文件内的属性)
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
// ========== 核心步骤2:解析配置 + 构建SqlSessionFactory ==========
// 1. parser.parse():解析XML配置文件,生成MyBatis全局核心配置对象Configuration
// 解析内容包括:环境(数据源、事务管理器)、mappers、设置(settings)、别名等
// 2. this.build(parser.parse()):调用重载的build(Configuration)方法,
// 基于解析后的Configuration对象创建DefaultSqlSessionFactory(SqlSessionFactory的默认实现)
// 注意:此步骤仅解析配置、初始化连接池(未创建实际数据库连接),属于“准备工作”
var5 = this.build(parser.parse());
} catch (Exception e) {
// ========== 异常处理 ==========
// 捕获解析/构建过程中的所有异常,包装成MyBatis统一的异常类型抛出
// ExceptionFactory.wrapException:MyBatis异常封装工具,添加友好的错误提示
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
// ========== 最终清理 ==========
// 重置ErrorContext(MyBatis的错误上下文对象,用于记录解析过程中的错误信息)
// 避免后续解析操作受当前错误上下文的影响
ErrorContext.instance().reset();
try {
// 关闭配置文件的输入流,释放IO资源
// 即使解析过程出错,也必须关闭流,避免资源泄漏
if (inputStream != null) {
inputStream.close();
}
} catch (IOException var13) {
// 捕获关闭流的异常但不处理(属于兜底操作,避免finally块抛出异常覆盖原异常)
// 这是MyBatis的源码设计习惯:流关闭失败不影响核心流程
}
}
// 返回构建好的SqlSessionFactory(默认是DefaultSqlSessionFactory实例)
return var5;
}
//创建DefaultSqlSessionFactory 传递各种环境信息
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
4.2:获取sqlsession
代码:
/**
* 获取 sqlSession
* @return sqlSession
*/
public SqlSession getSqlSession() {
val sqlSession = sqlSessionFactory.openSession();
return sqlSession;
}
源码分析:sqlsession并没有创建数据库的连接connection,只是初始化了执行器、事务,将事务绑定进执行器,然后返回了DefaultSqlSession
/**
* DefaultSqlSessionFactory中无参的openSession()方法(开发者最常调用)
* 核心作用:创建默认配置的SqlSession(自动提交事务=false,默认执行器,默认环境)
* 关键:此方法全程不创建数据库连接,仅初始化SqlSession的核心组件
*/
@Override
public SqlSession openSession() {
// 调用重载方法,传入默认参数:
// 参数1:autoCommit = false(事务默认不自动提交)
// 参数2:级别 = null(使用默认事务隔离级别)
// 参数3:executorType = null(使用默认执行器类型SIMPLE)
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
/**
* 从数据源创建SqlSession的核心方法(MyBatis内部核心逻辑)
* @param execType 执行器类型(SIMPLE/REUSE/BATCH,默认SIMPLE)
* @param level 事务隔离级别(null=使用数据库默认)
* @param autoCommit 是否自动提交事务(默认false)
* @return SqlSession 初始化完成的DefaultSqlSession对象
*/
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null; // 声明事务对象,用于管理数据库事务
try {
// ========== 步骤1:获取全局配置中的环境(Environment) ==========
// Environment包含:数据源(DataSource)、事务管理器类型(JDBC/MANAGED)
// 注意:此处仅获取配置,未从DataSource获取Connection
final Environment environment = configuration.getEnvironment();
// ========== 步骤2:创建事务工厂(TransactionFactory) ==========
// 事务工厂用于创建Transaction对象,根据配置的事务管理器类型(JDBC/MANAGED)实例化
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
// ========== 步骤3:创建Transaction事务对象 ==========
// 核心:此处创建的Transaction仅绑定了DataSource和事务配置,**未获取Connection**
// Transaction是事务管理器的抽象,它持有DataSource,但不会主动创建连接
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// ========== 步骤4:创建执行器(Executor) ==========
// Executor是MyBatis执行SQL的核心组件(SIMPLE/REUSE/BATCH)
// 此处创建执行器,并绑定事务对象(tx),但tx尚未关联任何Connection
final Executor executor = configuration.newExecutor(tx, execType);
// ========== 步骤5:创建并返回DefaultSqlSession ==========
// DefaultSqlSession是SqlSession的默认实现类,封装了:
// - Configuration:全局配置
// - Executor:执行器(绑定了事务,但无连接)
// - autoCommit:事务自动提交标志
// 关键:此时Executor/Transaction仅持有DataSource引用,未调用getConnection()
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
// 若创建过程出错,关闭事务(此时事务未关联连接,关闭仅做清理)
closeTransaction(tx);
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
// 重置错误上下文,避免影响后续操作
ErrorContext.instance().reset();
}
}
4.3:获取Mapper
代码
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
源码分析:通过动态代理获取代理接口,并不会执行方法
/**
* DefaultSqlSession中getMapper方法的实现(开发者直接调用的入口)
* @param type 要获取的Mapper接口类型(如UserMapper.class)
* @return T Mapper接口的动态代理对象(而非真实实现类)
* 核心:仅从Mapper注册中心获取代理对象,无数据库交互、无连接创建
*/
@Override
public <T> T getMapper(Class<T> type) {
// 核心:委托给Configuration中的MapperRegistry(Mapper注册中心)处理
// Configuration是MyBatis的全局配置类,初始化时已注册所有Mapper接口
// 此处仅“查询注册中心”,无任何数据库操作
return configuration.getMapper(type, this);
}
/**
* MapperRegistry:MyBatis的Mapper接口注册中心(全局唯一)
* 作用:存储“Mapper接口Class”与“MapperProxyFactory(代理工厂)”的映射关系
* @param type Mapper接口类型
* @param sqlSession 当前的SqlSession对象(供代理对象后续执行SQL使用)
* @return T Mapper接口的动态代理对象
*/
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
// 步骤2.1:从注册中心获取当前Mapper接口对应的代理工厂
// knownMappers是一个Map<Class<?>, MapperProxyFactory<?>>,初始化时已填充
// 键:Mapper接口Class(如UserMapper.class);值:对应的代理工厂
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
// 若未找到代理工厂,说明Mapper接口未注册(常见错误:<mappers>配置遗漏)
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
// 步骤2.2:通过代理工厂创建Mapper接口的动态代理对象
// 核心:此处创建代理对象,无数据库交互,仅绑定SqlSession和接口信息
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
/**
* MapperProxyFactory:专门创建Mapper接口动态代理对象的工厂类
* 泛型T:Mapper接口类型(如UserMapper)
*/
public class MapperProxyFactory<T> {
// 要代理的Mapper接口类型(如UserMapper.class)
private final Class<T> mapperInterface;
// 缓存Mapper接口的方法信息(避免重复解析)
private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap<>();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
/**
* 创建Mapper接口的动态代理对象(核心方法)
* @param sqlSession 当前的SqlSession对象
* @return T Mapper接口的动态代理实例
*/
public T newInstance(SqlSession sqlSession) {
// 步骤3.1:创建MapperProxy(动态代理的InvocationHandler实现类)
// MapperProxy是JDK动态代理的核心处理器,后续调用接口方法时,会触发它的invoke()方法
// 此处仅绑定SqlSession和Mapper接口信息,无任何数据库操作
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
// 步骤3.2:通过JDK动态代理创建最终的Mapper代理对象
return newInstance(mapperProxy);
}
/**
* 底层创建JDK动态代理对象的方法(封装了Proxy.newProxyInstance)
* @param mapperProxy 代理处理器(InvocationHandler)
* @return T Mapper接口的动态代理对象
*/
protected T newInstance(MapperProxy<T> mapperProxy) {
// JDK动态代理核心API:Proxy.newProxyInstance
// 参数1:类加载器(使用Mapper接口的类加载器)
// 参数2:要代理的接口数组(仅包含当前Mapper接口,如UserMapper.class)
// 参数3:InvocationHandler(MapperProxy,处理方法调用)
// 核心:返回的是“实现了Mapper接口”的代理对象,而非真实实现类
return (T) Proxy.newProxyInstance(
mapperInterface.getClassLoader(),
new Class[] { mapperInterface },
mapperProxy
);
}
}
4.4:执行自定义方法,实际上是执行invoke方法
val user = mapper.selectUserById(1);
System.out.println(user);
源码分析:在执行自定义方法的时候才会初始化connection连接,有注解的话将参数根据注解绑定到sql,并且将返回值映射到配置文件的resultMap中。
//执行invoke方法,层层debug点进去就能看到初始化数据库连接
//设置事务隔离级别、参数绑定、返回值绑定等等方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
return Object.class.equals(method.getDeclaringClass()) ? method.invoke(this, args) : this.cachedInvoker(method).invoke(proxy, method, args, this.sqlSession);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
4.5:总结
| 步骤 | 操作内容 | 是否创建数据库连接? | 关键说明(补充底层逻辑) |
| 1 | 创建SqlSessionFactory(new SqlSessionFactoryBuilder().build(inputStream)) |
❌ 否 | 1. 核心行为:解析mybatis-config.xml生成Configuration对象,初始化连接池管理对象(如PooledDataSource),但未创建任何实际的数据库 TCP 连接;2. 易误解点:“初始化连接池”≠“创建连接”——POOLED 数据源仅初始化连接池的队列、锁等管理结构,无任何和数据库的网络交互;3. 源码印证:XMLConfigBuilder仅解析数据源配置,未调用DataSource.getConnection()。 |
| 2 | 创建SqlSession(sqlSessionFactory.openSession()) |
❌ 否 | 1. 核心行为:创建DefaultSqlSession对象,绑定DataSource和Transaction(事务对象),但Transaction仅持有数据源引用,未调用getConnection();2. 关键设计:SqlSession只是 “数据库会话的容器”,而非 “实际连接”,设计为 “懒加载” 避免资源浪费;3. 源码印证:DefaultSqlSessionFactory.openSessionFromDataSource()仅初始化执行器、事务对象,无连接获取逻辑。 |
| 3 | 获取 Mapper 代理对象(session.getMapper(UserMapper.class)) |
❌ 否 | 1. 核心行为:通过 JDK 动态代理创建MapperProxy对象,仅在内存中关联 “接口方法 - SQL 语句” 的映射关系;2. 无交互证据:全程仅操作MapperRegistry(Mapper 注册中心)和代理工厂,无任何数据库相关 API 调用;3. 易踩坑点:若报错 “Type UserMapper is not known to the MapperRegistry”,是配置遗漏,和连接无关。 |
| 3 | 执行 Mapper 方法(userMapper.findById(1)) |
✅ 是(核心触发点) | 1. 底层链路:代理对象invoke() → Executor.query() → Transaction.getConnection() → DataSource.getConnection();2. 连接池影响: - POOLED(默认):从连接池获取空闲连接(首次获取时才新建 TCP 连接); - UNPOOLED:每次新建 TCP 连接;3. 关键:若多次执行 Mapper 方法(如连续调用findById),复用同一SqlSession的连接,直到session.close()归还 / 关闭。 |
更多推荐



所有评论(0)