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 创建SqlSessionFactorynew SqlSessionFactoryBuilder().build(inputStream) ❌ 否 1. 核心行为:解析mybatis-config.xml生成Configuration对象,初始化连接池管理对象(如PooledDataSource),但未创建任何实际的数据库 TCP 连接;2. 易误解点:“初始化连接池”≠“创建连接”——POOLED 数据源仅初始化连接池的队列、锁等管理结构,无任何和数据库的网络交互;3. 源码印证:XMLConfigBuilder仅解析数据源配置,未调用DataSource.getConnection()
2 创建SqlSessionsqlSessionFactory.openSession() ❌ 否 1. 核心行为:创建DefaultSqlSession对象,绑定DataSourceTransaction(事务对象),但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()归还 / 关闭。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐