MyBatis 属于 SSM 框架的持久层部分,是业务开展进行的基础。本文对 MyBatis 框架的内容及使用进行了分析介绍,重点分析了如何通过 MyBatis 的注解开发,实现数据持久化。

代码部分已经上传至 GitHub:

1. MyBatis 入门

1.1 MyBatis 概述

首先要清楚,什么是框架:

  • 框架是软件开发中的一套解决方法,封装细节,提高开发效率。
  • 三层架构:表现层、业务层、持久层。
    • 表现层:展示数据。
    • 业务层:处理业务需求。
    • 持久层:与数据库交互。
  • image-20210218155512995

目前最常用的框架:

  • SSM 框架体系。包括 Spring, SpringMVC 和 MyBatis。
  • Spring 用于技术整合。SpringMVC 用于 Web 层表现。MyBatis 用于数据持久化。

那么,MyBatis 是怎样的框架呢:

  • MyBatis 是一个基于 Java 的持久层框架,封装了 JDBC,开发者不需要关注加载驱动、创建连接、创建 statement 等操作。
  • 也是采用 XML 或注解的方式,完成各种配置,并通过 Java 对象和 statement 中 sql 的动态参数进行映射,最终生成 SQL 语句。最后,MyBatis 框架执行 SQL 并将结果映射为 Java 对象返回。
  • 使用 ORM 思想实现结果集封装。ORM 是 Object Relational Mapping 对象关系映射,就是将数据库表和实体类和实体类的属性对应起来,通过操纵实体类就实现操纵数据库表。

回顾 JDBC,下面是一个例子:

public static void main(String[] args) { 
    Connection connection = null; 
    PreparedStatement preparedStatement = null; 
    ResultSet resultSet = null; 
    try {
        //加载数据库驱动 
        Class.forName("com.mysql.jdbc.Driver"); 
        //通过驱动管理类获取数据库链接 
        connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8","ro ot", "root"); 
        //定义sql 语句 ?表示占位符 
        String sql = "select * from user where username = ?";
        //获取预处理statement 
        preparedStatement = connection.prepareStatement(sql); 
        //设置参数,第一个参数为 sql 语句中参数的序号(从 1 开始),第二个参数为设置的参数值
        preparedStatement.setString(1, "王五"); 
        //向数据库发出 sql 执行查询,查询出结果集 
        resultSet = preparedStatement.executeQuery(); 
        //遍历查询结果集 
        while(resultSet.next()) {
            System.out.println(resultSet.getString("id")+resultSet.getString("username"));
        }
    } catch (Exception e) { 
        e.printStackTrace();
    } finally { 
        //释放资源 
        if (resultSet != null) { 
            try {
                resultSet.close();
            } catch (SQLException e) { 
                e.printStackTrace();
            } 
        }
        if (preparedStatement != null) { 
            try {
                preparedStatement.close();
            } catch (SQLException e) { 
                e.printStackTrace();
            } 
        }
        if (connection != null) { 
            try {
                connection.close();
            } catch (SQLException e) { 
                // TODO Auto-generated catch block 
                e.printStackTrace();
            } 
        } 
    } 
}

那么 JDBC 存在的问题有哪些呢:

  1. 数据库连接创建、释放会造成资源浪费。

  2. SQL 语句在代码中硬编码,造成代码不易维护,修改 SQL 还需要修改 Java 代码。

  3. 使用 PreparedStatement 向占位符 ? 传递参数存在硬编码,修改 SQL 还需要修改 Java 代码。

  4. 结果集解析也存在硬编码,SQL 变化导致代码变化,不易维护。如果将数据库记录封装为 POJO 对象解析更加方便。

1.2 MyBatis 环境搭建

MyBatis 环境搭建主要包括四步:

  • 创建 Maven 工程并导入坐标。
  • 创建实体类和 DAO 的接口。
  • 创建 MyBatis 的主配置文件 SqlMapConfig.xml。
  • 创建映射配置文件 IUserDao.xml。

image-20210218191540991

环境搭建的时候要注意的内容:

  • Mapper 和 DAO 其实是一个意思,命名的时候应该保持一致。
  • MyBatis 映射配置文件位置必须和 DAO 接口的包结构相同。同时,mapper 标签的 namespace 属性取值必须是 DAO 接口的全类名。id 属性取值必须是 DAO 接口的方法名。按照这个要求写,所有功能就由 MyBatis 自动实现。

下面是编写测试类文件:

public class MybatisTest { 
    public static void main(String[] args)throws Exception {
        //1.读取配置文件 
        InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml"); 
        //2.创建 SqlSessionFactory 的构建者对象 
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); 
        //3.使用构建者创建工厂对象 
        SqlSessionFactory SqlSessionFactory factory = builder.build(in); 
        //4.使用 SqlSessionFactory 生产SqlSession 对象 
        SqlSession session = factory.openSession(); 
        //5.使用 SqlSession 创建 dao 接口的代理对象 
        IUserDao userDao = session.getMapper(IUserDao.class); 
        //6.使用代理对象执行查询所有方法 
        List<User> users = userDao.findAll(); 
        for (User user : users) { 
            System.out.println(user);
        }
        //7.释放资源 
        session.close(); 
        in.close();
    }
}

可以学习到,MyBatis 需要的步骤包括:

  • 读取配置文件。
  • 创建 SqlSessionFactory 的构建者对象,并创建工厂对象。
  • 创建 dao 接口代理对象。
  • 使用代理对象执行 SQL。

2. MyBatis 注解开发

实际上,注解开发才是 MyBatis 最常用的部分。因此,学习 MyBatis 注解开发是最重要的部分。通过注解开发,可以减少编写 Mapper 映射文件。实际工作中,注解开发和映射开发(配置 xml)需要共同使用。例如样例工程就是,SqlMapConfig.xml 需要保存,而 IUserDaoConfig.xml 则不再需要,直接将 IUserDao 接口替换为注解开发版本。

2.1 常用注解

常用注解包括:

  • @Insert:新增。
  • @Update:更新。
  • @Delete:删除。
  • @Select:查询。
  • @Result:结果集封装。
  • @Results:与 @Result 共同使用,封装多个结果集。
  • @ResultMap:引用 @Results 定义的封装。
  • @One:一对一结果集封装。
  • @Many:一对多结果集封装。
  • @SelectProvidor:动态 SQL 映射。
  • @CacheNamespace:注解二级缓存使用。

2.2 环境构建

首先构建坐标:

image-20210303103941912

随后创建实体类:com.bluestragglers.domain.User.class

  • 包括 Set、Get、toSring 方法。
  • 继承、实现序列化接口 Serializable。

image-20210303104308287

随后编写 DAO 接口:com.bluestragglers.dao.IUserDao.class

  • 不需要编写对应的 daoconfig.xml 文件了,因为这个采用了注解开发。

image-20210303110104008

最后编写 SqlMapConfig.xml 文件,实现与 MySQL 的连接。

image-20210303105905500

最后,写一个测试类测试一下:

image-20210303112024661

测试结果:

image-20210303112040900

2.3 单表 CRUD

单表 CRUD 是最基本的操作。

首先在 IUserDao 中加入单表添加操作:

image-20210303164843544

然后在 test 文件夹新建测试类 AnnotationCRUDTest:

image-20210303164921552

最后,运行测试:

image-20210303164947087

进行模糊查询:两种写法都可以。一个是字符串拼接,一个是参数占位符。

image-20210303170740180

image-20210303171114114

另一种测试:将属性全部改名,与数据库不一致,从而进行 CRUD 测试。

image-20210303200506965

可以发现,直接测试的话,结果与属性不能相互对应。

image-20210303200445329

使用 @Results 标签,配合内部的 @Result,就可以实现多表 CRUD 的返回值设置,从而可以将查询得到的值与属性值相匹配。

image-20210303200617475

再次进行测试,可以发现查询与属性相匹配了。

image-20210303201125834

为了重复使用 @Result 的配置,我们可以加入 id 属性。这样,其他地方就可以通过 @ResultMap(value={}) 实现重复使用了。

image-20210303201625842

image-20210303201633532

通过测试,可以看到可以重复使用。

image-20210303201648743

2.4 多表 CRUD

为了实现多表查询,构建另一个表:账户 Account

image-20210303204359935

image-20210303203305040

image-20210303204416636

在 @Result 标签内,可以看到,有 One 和 Many 属性,也就是多对一和多对多属性:

image-20210303204713731

image-20210303204810532

其中,还可以配置 fetchType,一般,对一的时候设置为 EAGER(立即加载),对多时设置为 LAZY(延迟加载):

image-20210303205014528

重新完成编写,加入了 user 的多表查询,并采用了 @One 标签实现了多对一(MyBatis 也叫一对一)查询:

image-20210303205350483

image-20210303205506876

完成了多对一关系映射,再来尝试多对多关系映射(MyBatis 也叫一对多关系映射):

image-20210303210550585

image-20210303210737810

多表查询,无论是一对多,还是一对一,都只要关注两个属性:select 和 fetchType。select 是方法名,fetchType 是加载时机。

2.5 缓存配置

MyBatis 自动配置一级缓存,所以不用管理一级缓存。

二级缓存,可以保证后续一样的查询操作不会真正操作两次,而是会检查缓存,直接调用缓存的信息进行返回。

开启二级缓存,需要在 SqlMapConfig.xml 的 中加入下面的内容:

image-20210304093120383

随后,在 IUserDao 类前加入 @CacheNamespace(blocking = true)

image-20210304093245607

最后,编写测试类进行测试:

image-20210304093304935

通过二级缓存,实现了一次查询,关闭后再重启,依旧能够获取答案:

image-20210304094444631

参考内容

本文参考了以下内容:

  1. SSM 框架合集