Orm框架的发展历史与mybatis的高级应用

仓库
源码
https://gitee.com/laomaodu/mybatis-chinese-annotations
测试代码
https://gitee.com/laomaodu/jdbctset01_2026_2_2
课程目标
1.jdbc操作
jdbc的繁杂
sql耦合
结果集处理
资源的关闭
代码的重复
2.一步步封装
重复代码 结果集 处理 资源关闭 sql语句 优化
DbUtils
重复代码处理
v3
DbUtils2
针对
sql简单优化 sql解耦
String sql = "INSERT INTO T_USER(user_name,real_name,password,age,d_id)values(?,?,?,?,?)";
try {
DBUtils2.update(sql,"wangwu","王五","111",22,1001);
} catch (Exception e) {
e.printStackTrace();
}
v4结果集
效果
数据库对象转Java对象
反射
哪怕这样还有很多的漏洞
2封装
| 层级 | 框架 |
|---|---|
| 原生 | JDBC |
| 工具级 | DbUtils |
| 工业级 | Spring JDBC |
| 主流 | MyBatis / MyBatis-Plus |
| 重 ORM | Hibernate / JPA |
算作组件,功能不全
缓存处理,延迟加载,动态拼接,不方便
引出ORM
对象关系映射
mybatis
| 框架 | 国内 | 国外 |
|---|---|---|
| MyBatis | ⭐⭐⭐⭐⭐ | ⭐ |
| MyBatis-Plus | ⭐⭐⭐⭐ | ⭐ |
| Spring JDBC | ⭐⭐ | ⭐⭐ |
| JPA / Hibernate 适合很爽 特殊的复杂语句不灵活 | ⭐⭐ | ⭐⭐⭐⭐⭐ |
| Spring Data JPA | ⭐ | ⭐⭐⭐⭐ |
| jOOQ | ⭐ | ⭐⭐ |
3 Apache DBUtils
DButils中提供了一个QueryRunner类,它对数据库的增删改查的方法进行了封装,获取QueryRunner的方式

SpringJDBC
在Spring框架平台下,也提供的有JDBC的封装操作,在Spring中提供了一个模板方法 JdbcTemplate,里面封装了各种各样的 execute,query和update方法。
JdbcTemplate这个类是JDBC的核心包的中心类,简化了JDBC的操作,可以避免常见的异常,它封装了JDBC的核心流程,应用只要提供SQL语句,提取结果集就可以了,它是线程安全的。 ------------------------------------------------ 在SpringJdbcTemplate的使用中,我们依然要配置对应的数据源,然后将JdbcTemplate对象注入到IoC容器中。
@Configuration
@ComponentScan
public class SpringConfig {
@Bean
public DataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUsername("root");
dataSource.setPassword("123456");
dataSource.setUrl("jdbc:mysql://localhost:3306/mybatisdb?characterEncoding=utf-8&serverTimezone=UTC");
return dataSource;
}
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource){
JdbcTemplate template = new JdbcTemplate();
template.setDataSource(dataSource);
return template;
}
}
在我们具体操作数据库中数据的时候,我们只需要从容器中获取JdbcTemplate实例即可
DataSource 的作用可以总结为一句话:
提供统一、可配置、安全、高效的数据库连接获取方式,替代直接使用 DriverManager。

@Repository
public class UserDao {
@Autowired
private JdbcTemplate template;
public void addUser(){
int count = template.update("insert into t_user(user_name,real_name)values(?,?)","bobo","波波老师");
System.out.println("count = " + count);
}
public void query1(){
String sql = "select * from t_user";
List<User> list = template.query(sql, new RowMapper<User>() {
@Override
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
User user = new User();
user.setId(rs.getInt("id"));
user.setUserName(rs.getString("user_name"));
user.setRealName(rs.getString("real_name"));
return user;
}
});
for (User user : list) {
System.out.println(user);
}
}
public void query2(){
String sql = "select * from t_user";
List<User> list = template.query(sql, new BeanPropertyRowMapper<>(User.class));
for (User user : list) {
System.out.println(user);
}
}
}
ORM

前面介绍的Apache DBUtils和SpringJdbcTemplate虽然简化了数据库的操作,但是本身提供的功能还是比较简单的(缺少缓存,事务管理等),所以我们在实际开发中往往并没有直接使用上述技术,而是用到了Hibernate和MyBatis等这些专业的ORM持久层框架。
ORM( Object Relational Mapping) ,也就是对象与关系的映射,对象是程序里面的对象,关系是它与数据库里面的数据的关系,也就是说,ORM框架帮助我们解决的问题是程序对象和关系型数据库的相互映射的问题
O:对象
M:映射
R:关系型数据库

Session 和jdbc
Session 是 ORM 框架的核心概念,比如 Hibernate 或 MyBatis 中:
-
Session= 数据库操作会话(类似 JDBC 的 Connection,但更高层次) -
它封装了:
-
JDBC
Connection -
SQL 执行(手写或自动生成)
-
对象-关系映射(ORM)
-
缓存、事务管理
Session ≈ 高级封装的 Connection + ORM 功能
JDBC 是低级接口,你操作 Connection/Statement/ResultSet
Session 是高级接口,你操作对象和事务,底层还是走 JDBC
-
| 层次 | JDBC | ORM / Session |
|---|---|---|
| 数据库连接 | Connection | 内部封装 Connection |
| SQL 执行 | Statement / PreparedStatement | Session 内部处理 SQL / Mapper |
| 结果映射 | ResultSet + 手动封装 | 自动映射成对象 |
| 事务 | Connection 管理 | Session 管理事务,自动 commit/rollback |
| 缓存 | 无 | 可以有一级/二级缓存 |
| 优点 | 灵活,完全控制 | 简化开发,自动映射,事务/缓存管理 |
Hibernate的使用
Hibernate是一个很流行的ORM框架,2001年的时候就出了第一个版本。使用步骤如下
重 ORM Hibernate / JPA
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.11</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
在使用Hibernate的使用,我们需要为实体类创建一些hbm的xml映射文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
'-//Hibernate/Hibernate Mapping DTD 3.0//EN'
'http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd'>
<hibernate-mapping>
<class name="com.boge.model.User" table="t_user">
<id name="id" />
<property name="userName" column="user_name"></property>
<property name="realName" column="real_name"></property>
</class>
</hibernate-mapping>
以及Hibernate的配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">
com.mysql.cj.jdbc.Driver
</property>
<property name="hibernate.connection.url">
jdbc:mysql://localhost:3306/mybatisdb?characterEncoding=utf8&serverTimezone=UTC
</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">123456</property>
<property name="hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</property>
<property name="hibernate.show_sql">true</property>
<property name="hibernate.format_sql">true</property>
<property name="hibernate.hbm2ddl.auto">update</property>
<mapping resource="User.hbm.xml"/>
</session-factory>
</hibernate-configuration>
CRUD 操作
public class HibernateTest {
/**
* Hibernate操作案例演示
* @param args
*/
public static void main(String[] args) {
Configuration configuration = new Configuration();
// 默认使用hibernate.cfg.xml
configuration.configure();
// 创建Session工厂
SessionFactory factory = configuration.buildSessionFactory();
// 创建Session
Session session = factory.openSession();
// 获取事务对象
Transaction transaction = session.getTransaction();
// 开启事务
transaction.begin();
// 把对象添加到数据库中
User user = new User();
user.setId(666);
user.setUserName("hibernate");
user.setRealName("持久层框架");
session.save(user);
transaction.commit();
session.close();
}
}
Hibernate的出现大大简化了我们的数据库操作,同时也能够更好的应对更加复杂的业务场景,Hibernate具有如下的特点
根据数据库方言自定生成SQL,移植性好 自动管理连接资源 实现了对象和关系型数据的完全映射,操作对象就想操作数据库记录一样 提供了缓存机制 Hibernate在处理复杂业务的时候同样也存在一些问题
比如API中的get(),update()和save()方法,操作的实际上是所有的字段,没有办法指定部分字段,换句话说就是不够灵活 自定生成SQL的方式,如果要基于SQL去做一些优化的话,也是非常困难的。 不支持动态SQL,比如分表中的表名,条件,参数变化等,无法根据条件自动生成SQL 因此我们需要一个更为灵活的框架
| 维度 | Spring JDBC | MyBatis | JPA |
|---|---|---|---|
| SQL 控制权 | 全在你 | 在你 | 在框架 |
| 是否建模 | ❌ | ⚠️(弱) | ✅ |
| 复杂 SQL | 强 | 很强 | 弱 |
| 调优难度 | 低 | 低 | 高 |
| 团队依赖 | 开发 | 开发+DBA | 领域专家 |
| 国内适配 | 高 | 极高 | 低 |
mybatis 半自动orm框架
全局配置文件
映射文件xml mapper
@Test
public void test1() throws Exception{
// 1.获取配置文件
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
// 2.加载解析配置文件并获取SqlSessionFactory对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
// 3.根据SqlSessionFactory对象获取SqlSession对象
SqlSession sqlSession = factory.openSession();
// 4.通过SqlSession中提供的 API方法来操作数据库
List<User> list = sqlSession.selectList("org.example.mapper.UserMapper.selectUserList");
for (User user : list) {
System.out.println(user);
}
// 5.关闭会话
sqlSession.close();
}
基础实验
public void test2() throws Exception{
// 1.获取配置文件
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
// 2.加载解析配置文件并获取SqlSessionFactory对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
// 3.根据SqlSessionFactory对象获取SqlSession对象
SqlSession sqlSession = factory.openSession();
// 4.通过SqlSession中提供的 API方法来操作数据库
List<User> list ;
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
list = mapper.selectUserList();
for (User user : list) {
System.out.println(user);
}
// 5.关闭会话
sqlSession.close();
}
代理对象封装
全局文件
xml配置
查看官网配置
// 对于全局配置文件各种标签的解析
propertiesElement(root.evalNode("properties"));
settingsElement(settings);


MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 配置文档的顶层结构如下:
configuration(配置) properties(属性) settings(设置) typeAliases(类型别名) typeHandlers(类型处理器) objectFactory(对象工厂) plugins(插件) environments(环境配置) environment(环境变量) transactionManager(事务管理器) dataSource(数据源) databaseIdProvider(数据库厂商标识) mappers(映射器)
自定义类型
public class IntegerTypeHandler extends BaseTypeHandler<Integer>
| 方法 | 作用 | 调用时机 | 示例逻辑(在字符串前加 "666") |
|---|---|---|---|
setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) |
将 Java 对象设置到 SQL 参数中 | 当执行 INSERT 或 UPDATE 时调用 |
ps.setString(i, "666" + parameter); |
getNullableResult(ResultSet rs, String columnName) |
从 ResultSet 根据列名获取值 | 查询数据库返回结果时调用 | return "666" + rs.getString(columnName); |
getNullableResult(ResultSet rs, int columnIndex) |
从 ResultSet 根据列索引获取值 | 查询数据库返回结果时调用 | return "666" + rs.getString(columnIndex); |
getNullableResult(CallableStatement cs, int columnIndex) |
从存储过程调用返回结果获取值 | 调用存储过程返回值时调用 | return "666" + cs.getString(columnIndex); |
在 MyBatis 中自定义类型处理器(TypeHandler)的作用是控制 Java 类型与 JDBC 类型之间的转换。常见用途包括:
-
数据库存储的是
String,Java 中希望是枚举或自定义类 -
数据库存储的是
JSON字符串,Java 中希望映射为对象 -
特定格式的日期或加密字段转换
自写类型处理器
对象工厂
DefaultObjectFactory 是 MyBatis 用来统一创建结果对象和集合对象的核心组件,负责接口实现选择、反射实例化以及构造器访问控制,是 MyBatis 对象模型的入口点。
在反射包下,具体源码回头看
<select id="list" resultType="User">
流程
ResultSet
↓
ObjectFactory.create(User.class)
↓
new User()
↓
MetaObject.setValue(...)
List<User> list = mapper.list();
返回类型 = List
↓
resolveInterface(List.class)
↓
ArrayList.class
↓
new ArrayList()
数据库到类的映射
package org.example;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
public class GpObjectFactory extends DefaultObjectFactory {
@Override
public <T> T create(Class<T> type) {
if (type.equals(User.class))
{
User user = new User();
user.setUserName("测试");
return (T) user;
}
return super.create(type);
}
}

事务

和映射文件


动态语句
<select id="list" resultType="user">
SELECT * FROM user
<where>
<if test="name != null">
AND name = #{name}
</if>
<if test="age != null">
AND age = #{age}
</if>
</where>
</select>
MyBatis 的动态 SQL = SqlNode 树在运行期生成最终 SQL
Mapper 方法调用
↓
MappedStatement
↓
SqlSource
↓
DynamicSqlSource
↓
SqlNode.apply(context)
↓
生成最终 SQL
↓
BoundSql
↓
Executor 执行 JDBC
SqlNode.apply(DynamicContext context)
XML 解析阶段(启动时)
XMLScriptBuilder.parseScriptNode()
作用:
-
把
<if> <where> <foreach>解析成 SqlNode 树 -
只解析结构,不生成 SQL
-
SQL 生成阶段(运行时)
DynamicSqlSource.getBoundSql(parameterObject)
核心代码逻辑:
DynamicContext context = new DynamicContext(configuration, parameterObject);
rootSqlNode.apply(context);
String sql = context.getSql();
MyBatis 动态 SQL = 运行期 SQL AST 解释执行

动态sql
批量操作

慢,提供了了执行器
批处理
| 类型 | 说明 |
|---|---|
SIMPLE |
每次执行立刻发 SQL(默认) |
REUSE |
复用 PreparedStatement |
BATCH |
延迟执行,批量发送 |
public enum ExecutorType { SIMPLE, REUSE, BATCH }
// 3.根据SqlSessionFactory对象获取SqlSession对象
SqlSession sqlSession = factory.openSession(ExecutorType.BATCH, false);

关联查询
<resultMap id="userMap" type="User">
<id property="id" column="id"/>
<result property="userName" column="user_name"/>
<association property="dept" javaType="Dept">
<id property="id" column="d_id"/>
<result property="name" column="d_name"/>
</association>
</resultMap>
多表查询
分页查询
| 类型 | 说明 |
|---|---|
| 物理分页 | SQL 层 limit offset |
| 逻辑分页 | 查全量再 subList(不推荐) |
select * from user limit #{offset}, #{size}
问题:
-
每个 SQL 手写
-
不通用
PageHelper.startPage(pageNum, pageSize);
List<User> list = mapper.selectAll();
自动生成 SQL
select * from user limit ?, ?
原理
-
MyBatis 插件
-
拦截
Executor.query -
动态改写 SQL
在 MyBatis 中,插件机制是 拦截器(Interceptor)模式 ,允许你在 Executor / ParameterHandler / ResultSetHandler / StatementHandler 执行 SQL 的关键节点插入自定义逻辑。下面从源码层面详细拆解:
自动生成
mybatis-plus