跳至主要內容

MyBatis-执行流程

holic-x...大约 9 分钟JAVA框架

MyBatis-执行流程

学习核心

  • MyBatis框架核心
  • 执行流程

学习资料

MyBatis框架核心

MyBatis执行流程

MyBatis 框架

MyBatisopen in new window是支持普通 SQL 查询,存储过程和高级映射的优秀持久层框架。 MyBatis消除了几乎所有的 JDBC 代码和参数的手工设置以及对结果集的检索。 MyBatis可以使用简单的XML 或注解用于配置和原始映射,将接口和 Java 的 POJO( Plain Old Java Objects,普通的Java 对象)映射成数据库中的记录。

​ MyBatis让程序将主要精力放在sql上,通过mybatis提供的映射方式,可以自由灵活的生成满足条件的sql(半自动化的orm框架)

框架流程

image-20240614105121612

【1】读取MyBatis的核心配置文件(例如myabtis-config.xml全局配置文件,配置数据库基础属性和其他映射配置)

【2】加载映射文件(多个mapper.xml,封装操作数据库的SQL语句):通过package扫描或者通过mapper指定

【3】构建会话工厂获取SqlSessionFactory(通过SqlSessionFactoryBuilder对象进行构建)

【4】创建会话对象SqlSession(每个线程拥有自己的SqlSession实例)

【5】Executor执行器(MyBatis核心)负责SQL语句的生成和查询缓存的维护(根据SqlSession传递的参数动态生成需要执行的SQL语句并维护查询缓存)

  • SimpleExecutor -- 普通的执行器
  • ReuseExecutor -- 执行器会重用预处理语句(PreparedStatements)
  • BatchExecutor -- 批处理执行器

【6】MappedStatement(底层封装对象),由StatementHandler对解析的SQL语句进行封装(一个MappedStatement对应sql语句标签)

  • 输入参数类型(输入参数映射):parameterType
  • 输出参数类型(封装结果集):resultType
<!--一个动态sql标签就是一个`MappedStatement`对象-->
<select id="findUserById" parameterType="int" resultType="com.noob.mybatis.model.User">
 		select * from user where id=#{id}
</select>

​ 综上,可以对比JDBC的操作流程理解,其核心流程可以总结为:

Configuration =》SqlSessionFactory =》SqlSession =》Executor =》StatementHandler(ParameterHandler、ResultHandler)

  • 加载解析配置文件(核心配置文件和映射文件)
  • 处理参数、解析SQL
  • 执行操作(CRUD)
  • 封装操作结果集

工作原理(可以结合两个点进行扩展)

【1】MyBatis启动加载

​ SqlSessionFactory:完成全局配置文件和映射文件的加载解析操作,然后将相关信息保存在Configuration对象中

​ SqlSession:通过SqlSessionFactory获取SqlSession会话对象

【2】MyBatis是如何处理请求的?(SQL的执行过程:参数映射、SQL解析、执行和结果处理)

​ SqlSession中提供了处理请求的方法:例如select、insert、update、save等

​ 通过解析SQL语句,调用相应的Executor执行:如果有配置缓存处理则先走二级缓存再走一级缓存,如果缓存中都没有命中数据则继续走数据库操作,由单个StatementHandler进行处理。

​ 通过StatementHandler处理SQL中的占位符,生成完整的SQL语句

​ 最终执行SQL并通过ResultHander处理结果集的映射

【3】SQL的执行流程

​ Mapper代理方法方式:寻找SQL语句(根据namespace、方法名定位解析后的SQL语句)、执行SQL语句(执行SQL)

概念详解

​ 可结合普通的mybatis项目、spring整合mybatis、springboot整合mybatis 不同案例,主要掌握其构建核心。此处以普通的mybatis项目案例为参考,解析mybatis执行流程

1.核心配置文件:mybatis-config.xml

​ 可查阅相关的官方doc文档,内部有详细的配置关于mybatis的运行环境、数据源、事务等配置说明

Mybatis官网open in new windowMybatis中文网址open in new window

加载核心配置文件mybatis-config.xml(核心配置、mapper映射配置)

<?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>
	<!-- 数据源的配置 -->
	<environments default="development">
		<environment id="development">
			<transactionManager type="JDBC" />
			<dataSource type="POOLED">
				<property name="driver" value="com.mysql.jdbc.Driver" />
				<property name="url" value="jdbc:mysql://localhost:3306/mybatis" />
				<property name="username" value="root" />
				<property name="password" value="root" />
			</dataSource>
		</environment>
	</environments>
  
  <!-- 加载映射文件的多种方式 -->
	<mappers>
		<!-- 
			方式1:通过resource一次加载一个文件 
			<mapper resource=""/>
		-->
		<mapper resource="sqlmap/user.xml"/>
		<!-- 
			方式2:遵循mapper定义规范
			通过mapper接口加载单个配置文件
			此种方式加载数据的前提是:映射文件与接口同名且必须在同一个包下
			通过指定的包与接口名结合进行加载
			<mapper class=""></mapper>
		 -->
		 <mapper class="com.noob.mybatis.b_mapper.UserMapper"></mapper>
		 <!-- 
		 	方式3:通过package进行批量加载
		 	<package name="指定包名"></package> 
		 	从而实现批量加载hiding包下的所有映射文件
		 -->
		 <!-- <package name="com.noob.mybatis.b_mapper"></package> -->
	</mappers>
  
</configuration>

针对一些核心配置简单理解,后续项目开发中通过spring、springboot整合mybatis能帮助开发者简化很多繁杂的配置项,此处主要理解其框架核心,然后结合实际场景案例掌握其应用技巧

2.原始DAO操作 VS Mapper代理方法

  • 原始DAO操作的CRUD是根据mapper.xml中定义的namespace.方法名的形式来执行
public class UserDAOTest {
 	// 定义SqlSessionFactory 	
private SqlSessionFactory sqlSessionFactory;

	@Before // 在所有测试代码执行之前执行该方法
	public void before() throws Exception {
		// a.定义mybatis配置文件,并得到配置文件的流
		String resource = "SqlMapConfig.xml";
		InputStream inputStream = Resources.getResourceAsStream(resource);

		// b.根据流创建会话工厂,传入mybatis配置信息到会话工厂
		sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
	}

	@Test
	public void test() {
    // 创建SqlSession对象
    SqlSession sqlSession = sqlSessionFactory.openSession();
    // 指定sql语句
		User findUser = sqlSession.selectOne("test.findUserById", 25);
		System.out.println(findUser);
		sqlSession.close();
	}
}

// 此处的test.findUserById为对应映射文件的namespace.方法名来进行定位
  • Mapper代理方法:通过定义Mapper接口和Mapper.xml 将两者进行绑定
开发步骤:
Mapper代理方法开发:程序员只需要定义mapper接口(相当于dao接口)
程序员需要编写mapper.xml映射文件,且在编写mapper接口(dao接口)的时候需要遵循一些开发规范,mapper可以自动生成mapper接口实现类代理对象
遵循以下定义规范:
	a.在mapper.xml中namespace 等于mapper接口地址
	b.mapper.java接口中的方法名和mapper.xml中的statement的id必须一致
	c.mapper.java接口的方法返回值类型和mapper.xml的resultType的类型一致
	d.mapper.java接口的方法的形参和mapper.xml中的parameterType的类型一致
总结:namespace有特殊含义等价于mapper接口地址,且mapper.java接口中的方法名、返回值类型、形参必须与mapper.xml中定义的内容一一对应
主要步骤分析:
	a.编写mapper.java文件
	b.配置映射文件mapper.xml遵循上述规范
	c.通过sqlMapConfig.xml加载映射配置文件
	d.编写测试代码进行测试
// 1.构建Mapper接口
public interface Mapper {
	// 此处简单定义两个方法进行测试
	public void addUser(User user);
	public User findUserById(int id);
}

// 2.构建Mapper.xml实现
<?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:代表命名空间,此处mapper标识访问地址
   -->
 <mapper namespace="com.noob.mybatis.b_mapper.UserMapper">
 	<!-- 定义添加用户相关的信息 -->
 	<insert id="addUser" parameterType="com.noob.mybatis.model.User">
 		<selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
 			select LAST_INSERT_ID()
 		</selectKey>
		insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address}) 	
 	</insert>
 	
 	<!-- 定义根据id查找用户信息 -->
 	<select id="findUserById" parameterType="java.lang.Integer" resultType="User">
 		select * from user where id=#{id}
 	</select>
 </mapper>
    
 // 3.测试
 public class UserMapperTest {

	// 优化测试,将公共代码提取出来
	SqlSessionFactory sqlSessionFactory;

	@Before // 在所有测试代码执行之前执行该方法
	public void before() throws Exception {
		// a.定义mybatis配置文件,并得到配置文件的流
		String resource = "SqlMapConfig.xml";
		InputStream inputStream = Resources.getResourceAsStream(resource);

		// b.根据流创建会话工厂,传入mybatis配置信息到会话工厂
		sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
	}

	@Test
	public void testAddUser() {
		SqlSession sqlSession = sqlSessionFactory.openSession();
		UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
		// 创建要添加的用户信息(通过指定的用户id修改数据)
		User user = new User();
		user.setUsername("小傻子");
		user.setAddress("王者峡谷");
		user.setBirthday(new Date());
		user.setSex("2");
		// 通过UserMapper对象执行操作
		userMapper.addUser(user);
		// 提交事务、释放连接
		sqlSession.commit();
		sqlSession.close();
	}

	@Test
	public void testFindUserById() {
		SqlSession sqlSession = sqlSessionFactory.openSession();
		// 通过SqlSession对象获取UserMapper对象
		UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
		// 根据id查找用户信息
		User findUser = userMapper.findUserById(25);
		System.out.println("查找到的用户:"+findUser);
		// 关闭连接
		sqlSession.close();
	}
}

3.输入映射和输出映射

​ 映射文件构建的核心是一条SQL语句的封装,要理解XML配置和对应Mapper、SQL的映射。

public interface UserCustomMapper {
	// 定义查询方法
	public List<UserCustom> findUserByUnion(UserQueryVO userQueryVO)throws Exception;
}
<?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:代表命名空间,此处mapper标识访问地址
   -->
 <mapper namespace="com.noob.mybatis.c_mapper.UserCustomMapper">
   <!-- 定义根据综合条件查找用户信息 -->
   <select id="findUserByUnion" parameterType="UserQueryVO" resultType="UserCustom">
      select * from user where user.sex=#{userCustom.sex} and user.username like '%${userCustom.username}%'
    <!-- 分析:#{userCustom.sex}是指获取UserQueryVO中userCustom属性的sex属性值 -->
   </select>
	</mapper>

​ 以最基础的查询语句分析,可以看到上述<select>标签定义:

  • id用于唯一标识方法名(和接口中的方法名对照)
  • parameterType 请求参数类型(此处为UserQueryVO,是一个封装的查询对象)
  • resultTyperesultMap 返回参数类型(此处为UserCustom,是一个封装的返回结果对象)

其中针对返回类型处理有多种不同的场景:

  • ResultType:可以是自定义的返回实体、也可以是MyBatis支持的简单类型(例如int、string等,参考官方文档进行配置)
  • POJO对象或POJO列表:通过resultType指定POJO的类型,MyBatis会自动根据指定类型封装返回的实体、列表数据
  • ResultMap:为了适配一对一、一对多、多对多的业务场景

4.动态SQL

动态SQL概念核心

​ MyBatis提供了强大的动态SQL功能,通过相应的标签配置,可以根据不同的条件拼接相应的SQL语句。

​ 例如SQL片段、循环遍历、if条件查询、集合过滤等

SQL片段

	<!-- a.定义sql片段 -->
 	<sql id="query_user_where">
 		<if test="userCustom!=null">
 			<if test="userCustom.sex!=null">
 				and user.sex=#{userCustom.sex}
 			</if>
 			<if test="userCustom.username!=null">
				and user.username like '%${userCustom.username}%'
 			</if>
 		</if>
 	</sql>
 	
 	<!-- b.在指定的位置引用sql片段 -->
 	<!-- 定义根据综合条件查找用户信息(动态sql完成拼接) -->
 	<select id="findUserByUnion" parameterType="UserQueryVO" resultType="UserCustom">
 		select * from user 
 		<where>
			<include refid="query_user_where"></include> 			
 		</where>
 	</select>

for循环遍历

	<!-- a.定义sql片段 -->
 	<sql id="query_user_foreach">
 		<if test="ids!=null">
 			<!--  
 				说明:
 				foreach表示对collection数据进行迭代
 				collection:要迭代的集合属性
 				item:在迭代遍历的过程中生成的对象名称
 				open:开始遍历id是拼接的字符串
 				close:遍历结束后拼接的字符串
 				separator:每次遍历两个对象中间需要拼接的内容
 			-->
 			<!-- 方式1:and (id=1 or id=10 or id=16) -->
 			<!-- 
	 			<foreach collection="ids" item="user_id" open="and (" close=")" separator="or"> 
	 				id=#{user_id}
	 			</foreach> 
 			-->
 			<!-- 方式2:id in (1,10,16) -->
 			<foreach collection="ids" item="user_id" open="id in (" close=")" separator=",">
 				#{user_id}
 			</foreach>
 		</if>
 	</sql>
 
 	<!-- b.通过指定的sql片段遍历用户id数据 -->
 	<select id="findUserListByIds" parameterType="UserQueryVO" resultType="UserCustom">
 		select * from user 
 		<where>
			<include refid="query_user_foreach"></include> 			
 		</where>
 	</select>
评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v3.1.3