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

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

相关推荐
郭小李6 小时前
mybatis 动态 sql
java·sql·mybatis
知守观6 小时前
技术考古:iBATIS 2.3.4 时代的持久层,是如何用 Map 玩转 307 个存储过程的?
java·后端·mybatis
captain3767 小时前
MyBatis Plus(1)
java·服务器·mybatis
代码不停13 小时前
MyBatis进阶
spring boot·mybatis
captain3761 天前
MyBatis(入门5)其他查询操作
java·数据库·spring boot·mybatis
captain3761 天前
MyBatis(进阶1)
mybatis
郭小李2 天前
mybatis的 Mapper.xml语法
xml·java·mybatis
m0_639906942 天前
MyBatis 实现分页查询|逻辑分页、物理分页、PageHelper 原理
mybatis
captain3762 天前
MyBatis操作数据库(入门2)
java·数据库·spring boot·后端·spring·mybatis
正儿八经的少年4 天前
MyBatis 一对一、一对多 SQL
数据库·sql·mybatis