终结篇 MyBatis原理统一解析 (一)

本文介绍通过JDBC查询数据库的基本步骤,并逐步优化这些步骤以实现更高效的数据库交互方式,最终演进到使用MyBatis框架。文章涵盖数据库连接管理、SQL语句配置、结果映射与缓存等方面。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. JDBC实现过程

我们先看看我们最熟悉也是最基础的通过JDBC查询数据库数据,一般需要以下七个步骤:

  1. 加载JDBC驱动;
  2. 建立并获取数据库连接;
  3. 创建 JDBC Statements 对象;
  4. 设置SQL语句的传入参数;
  5. 执行SQL语句并获得查询结果;
  6. 对查询结果进行转换处理并将处理结果返回;
  7. 释放相关资源(关闭Connection,关闭Statement,关闭ResultSet);

具体的实现代码:


    static List<Map<String,Object>> OriginalSQL() {
        Connection connection = null;
        ResultSet rs = null;
        PreparedStatement stmt = null;
        List< Map< String,Object>> resultList = new ArrayList< Map< String,Object>>();

        try {
            // 加载JDBC驱动
            Class.forName("com.mysql.jdbc.Driver").newInstance();
            String url = "jdbc:mysql://10.168.100.164:3306/test";

            String user = "root";
            String password = "root";

            // 获取数据库连接
            connection = DriverManager.getConnection(url,user,password);

            String sql = "select * from User where id = ? ";
            // 创建Statement对象(每一个Statement为一次数据库执行请求)
            stmt = connection.prepareStatement(sql);

            // 设置传入参数
            stmt.setString(1, "1");

            // 执行SQL语句
            rs = stmt.executeQuery();

            // 处理查询结果(将查询结果转换成List<Map>格式)
            ResultSetMetaData rsmd = rs.getMetaData();
            int num = rsmd.getColumnCount();

            while(rs.next()){
                Map map = new HashMap();
                for(int i = 0;i < num;i++){
                    String columnName = rsmd.getColumnName(i+1);
                    map.put(columnName,rs.getString(columnName));
                }
                resultList.add(map);
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                // 关闭结果集
                if (rs != null) {
                    rs.close();
                }
                // 关闭执行
                if (stmt != null) {
                    stmt.close();
                }
                if (connection != null) {
                    connection.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return resultList;

    }

2.JDBC演变到Mybatis过程

上面我们看到了实现JDBC有七个步骤,哪些步骤是可以进一步封装的,减少我们开发的代码量。

2.1 第一步优化:连接获取和释放

问题:
数据库连接频繁的开启和关闭本身就造成了资源的浪费,影响系统的性能。

解决:
数据库连接的获取和关闭我们可以使用数据库连接池来解决资源浪费的问题。通过连接池就可以反复利用已经建立的连接去访问数据库了。减少连接的开启和关闭的时间。

我们统一从DataSource里面获取数据库连接,DataSource具体由DBCP实现还是由容器的JNDI实现都可以,所以我们将DataSource的具体实现通过让用户配置来应对变化。

2.2 第二步优化:SQL统一处理

问题:
1. SQL语句基本都散落在各个JAVA类中
2. 动态书写SQL语句,容易出错,没有语法检查, 更新繁琐

解决:
SQL语句可以配置到xml里[或者注解里]
支持OGNL语法的sql动态语句
支持占位符#,和非占位符$

2.3 第三步优化:结果映射和结果缓存

问题: 结果需要手动缓存和映射
解决方式:
1. 使用配置支持将结果转换成一个JavaBean对象返回、一个Map返回、一个List返回等.
2. 可以配置缓存(一级, 二级), 对SQL执行结果的缓存来提升性能.


3. MyBatis框架整体设计

MyBatis框架整体设计

3.1 接口层-和数据库交互的方式

MyBatis和数据库的交互有两种方式:

  1. 使用传统的MyBatis提供的API;
  2. 使用Mapper接口;
3.1.1 使用传统的MyBatis提供的API

这是传统的传递Statement Id 和查询参数给 SqlSession 对象,使用 SqlSession对象完成和数据库的交互;MyBatis提供了非常方便和简单的API,供用户实现对数据库的增删改查数据操作,以及对数据库连接信息和MyBatis 自身配置信息的维护操作。

这里写图片描述

 SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
 SqlSession session = factory.openSession();
 User userDemo = session.selectOne("gupaoIT.dao.UserMapper.getUserById", 1);

上述使用MyBatis 的方法,是创建一个和数据库打交道的SqlSession对象,然后根据Statement Id 和参数来操作数据库.
这种方式固然很简单和实用,但是它不符合面向对象语言的概念和面向接口编程的编程习惯。由于面向接口的编程是面向对象的大趋势,MyBatis 为了适应这一趋势,增加了第二种使用MyBatis 支持接口(Interface)调用方式。

3.1.2 使用Mapper接口

MyBatis 将配置文件中的每一个 节点抽象为一个 Mapper 接口:

这个接口中声明的方法和< mapper> 节点中的< select|update|delete|insert> 节点项对应,即< select|update|delete|insert> 节点的id值为Mapper 接口中的方法名称,parameterType 值表示Mapper 对应方法的入参类型,而resultMap 值则对应了Mapper 接口表示的返回值类型或者返回结果集的元素类型。

Mapper接口和Mapper.xml配置文件之间的对应关系

UserMapper mapper = session.getMapper(UserMapper.class);
User u = mapper.getUserById(1);

根据MyBatis 的配置规范配置好后,通过SqlSession.getMapper(XXXMapper.class)方法,MyBatis 会根据相应的接口声明的方法信息,通过动态代理机制生成一个Mapper 实例.
底层还是通过SqlSession.select(“statementId”,parameterObject);或者SqlSession.update(“statementId”,parameterObject); 等等来实现对数据库的操作

以下是部分源码:

public class MapperMethod {

 //***
  public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    switch (command.getType()) {
      case INSERT: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.insert(command.getName(), param));
        break;
      }
      case UPDATE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.update(command.getName(), param));
        break;
      }
      case DELETE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.delete(command.getName(), param));
        break;
      }
      case SELECT:
      ........
   }

MyBatis引用Mapper 接口这种调用方式,纯粹是为了满足面向接口编程的需要。(其实还有一个原因是在于,面向接口的编程,使得用户在接口上可以使用注解来配置SQL语句,这样就可以脱离XML配置文件,实现“0配置”)。

3.2 数据处理层

数据处理层可以说是MyBatis的核心,从大的方面上讲,它要完成两个功能:

1 . 通过传入参数构建动态SQL语句;
2. SQL语句的执行以及封装查询结果集成List< E>;

3.2.1 参数映射和动态SQL语句生成

动态生成SQL语句:
MyBatis 通过传入的参数值,使用 Ognl 来动态地构造SQL语句,使得MyBatis 有很强的灵活性和扩展性。

参数映射:
指的是对于java 数据类型和jdbc数据类型之间的转换:这里有包括两个过程:查询阶段,我们要将java类型的数据,转换成jdbc类型的数据

3.2.2 SQL语句的执行以及封装查询结果集成List< E>

动态SQL语句生成之后,MyBatis 将执行SQL语句,并将可能返回的结果集转换成List< E> 列表。
MyBatis 在对结果集的处理中,支持结果集关系一对多和多对一的转换,
并且有两种支持方式,
一种为嵌套查询语句的查询,还有一种是嵌套结果集的查询。

3.3 框架支撑层

  1. 事务管理机制
  2. 连接池管理机制: 支持数据库连接池, 建议使用datasource自己配置
  3. 缓存机制: 包括一级缓存(sqlsession级别), 二级缓存(mapper级别,不建议使用,或者替换为自己的redis实现)
  4. SQL语句的配置方式;
    传统的MyBatis 配置SQL语句方式就是使用XML文件进行配置的,但是这种方式不能很好地支持面向接口编程的理念,为了支持面向接口的编程,MyBatis 引入了Mapper接口的概念,面向接口的引入,对使用注解来配置SQL语句成为可能,用户只需要在接口上添加必要的注解即可,不用再去配置XML文件了,但是,目前的MyBatis 只是对注解配置SQL语句提供了有限的支持,某些高级功能还是要依赖XML配置文件配置SQL 语句。

3.4 引导层

引导层是配置和启动MyBatis配置信息的方式。
MyBatis 提供两种方式来引导MyBatis :基于XML配置文件的方式和基于Java API 的方式。
引导层最大的对象Configration


转自:https://www.jianshu.com/p/ec40a82cae28

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值