MyBatis①概述、CRUD、常见问题

基础知识

1、概述

1.1、JDBC

Java 数据库连接(Java DataBase Connectivity,简称 JDBC)

  • 使用 Java 语言操作关系型数据库的 API。
  • SUN 公司定义的标准接口(JDBC),定义了操作关系型数据库的规则。

1.1.1、开发步骤

  1. 驱动管理
    • 注册驱动:Driver
    • 获取数据库连接:URL、用户名、密码
  2. 定义 SQL 模板语句
  3. 获取 SQL 执行对象:PreparedStatement,设置占位符参数。
  4. 执行 SQL,获得返回结果:受影响行数 / 结果集
  5. 释放资源

1.1.2、问题分析(❗)

  1. 重复编码:对于相同的 JDBC 操作(增删改查),在不同业务中
    • 通常只有 SQL 语句不同。
    • 驱动管理、设置参数、封装实体、释放资源等操作相同。
  2. SQL 硬编码:SQL 语句硬编码于 Java 代码中
    • 当业务变动时,需要修改代码中的 SQL 语句。
    • 不易于维护。
  3. 实体转换
    • 封装实体:执行 DQL 时,需要手动将查询结果集中的字段封装到 Java 实体中。
    • 参数设置:执行 DML 时,需要手动将实体数据设置到对应位置的占位符参数。

1.2、MyBatis

1.2.1、说明

原 apache 的开源项目 iBatis。

2010 年由 apache software foundation 迁移到 google code,改名 MyBatis。

2013 年 11 月迁移到 Github。

image-20210722182630574

  • 基于 Java 的持久层框架

  • 内部封装 JDBC

    • 开发者只需关注 SQL 本身
    • 无需关注 JDBC 访问细节(包括结果集的获取、实体封装和参数设置等工作)。
  • 基于 XML注解方式,建立原生类型、接口、POJO数据库记录之间的关系。

  • 支持自定义 SQL、存储过程、高级映射。

相关链接

1.2.2、优点

  • 简单易学,无第三方依赖。
  • SQL 与 Java 代码分离,耦合度低,可维护性高。
  • 提供映射标签,支持对象与数据库的 ORM 字段关系映射。
  • 提供对象关系映射标签,支持对象关系组建维护。
  • 支持动态 SQL

2、MyBatis 使用

2.1、搭建环境

以 user(user_id, name, password) 为例

  • 数据库:创建 user 表。
  • 项目搭建
    1. 导入依赖:MySQL 驱动、MyBatis
    2. 实体类:User
    3. 核心配置文件:通常命名 SqlMapConfig 或 mybatis-config
    4. 映射文件:UserMapper.xml

2.1.1、数据库表

image-20220405234013153

  • 主键设计
    • 逻辑主键:id,区分每个字段。
    • 业务主键:user_id,区分每个业务实体。
  • 除此之外,每个数据库表还应该有 create_timeupdate_time 字段,此处省略。

2.1.2、搭建项目

  1. 导入依赖:MySQL 驱动、MyBatis

    <!-- MySQL数据库连接驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.27</version>
    </dependency>
    <!-- MyBatis -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.7</version>
    </dependency>
    
  2. 实体类:User

    public class User {
        private String userId;
        private String name;
        private String password;
        // 构造方法
        // toString()、getter、setter
    }
    

2.1.3、核心配置文件(❗)

核心配置文件mybatis-config.xml

(存放于 resources 目录)

  • 约束信息:从 官方文档 复制即可

  • 配置信息

    • setttings:开启命名映射,将数据库列名(xxx_yyy)自动映射为 Java 属性名(xxxYyy)

    • environments:需选择默认环境(可参考 JDBC

      • 事务管理器:JDBC
      • 数据源:驱动、URL、用户名、密码
    • mappers:注意用 / 分隔符

      <?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>
          <settings>
              <setting name="mapUnderscoreToCamelCase" value="true"/>
          </settings>
          <environments default="development">
              <environment id="development">
                  <transactionManager type="JDBC"/>
                  <dataSource type="POOLED">
                      <property name="driver" value="数据库驱动"/>
                      <property name="url" value="jdbc:mysql://主机号:端口号/数据库名?参数"/>
                      <property name="username" value="用户名"/>
                      <property name="password" value="密码"/>
                  </dataSource>
              </environment>
          </environments>
          <mappers>
              <mapper resource="映射文件的全限类名"/>
          </mappers>
      </configuration>
      

2.2、映射文件

以查询所有用户为例,基于原生 MyBatis 实现。

2.2.1、映射文件模板

:文件存放于 resources 目录,模板从 官方文档 复制即可。

  • 约束信息

  • 映射信息

    • 命名空间:唯一区分 SQL 映射文件。

    • SQL 标签

      • id:唯一区分该命名空间下的 SQL 语句,与 Mapper 接口的方法同名即可。

      • resultType:结果类型,即结果集所要封装的实体。

      • parameterType:参数类型,占位符为 #{}

        <?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="">
            <insert id="" resultType="" parameterType="">
            </insert>
        </mapper>
        

2.2.2、UserMapper.xml(❗)

注意:CRUD 操作对应不同的 SQL 标签。

<?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="userMapper">
    <select id="listUsers" resultType="indi.jaywee.pojo.User">
        SELECT user_id, name, password
        FROM study_mysql.t_user
    </select>
    <insert id="insertUser" parameterType="indi.jaywee.pojo.User">
        INSERT INTO study_mysql.t_user(user_id, name, password)
            VALUE (#{userId}, #{name}, #{password})
    </insert>
    <delete id="deleteUserByUserId" parameterType="java.lang.String">
        DELETE
        FROM study_mysql.t_user
        WHERE user_id = #{userId}
    </delete>
    <update id="updateUser" parameterType="indi.jaywee.pojo.User">
        UPDATE study_mysql.t_user
        SET name     = #{name},
            password = #{password}
        WHERE user_id = #{userId}
    </update>
</mapper>

2.3、CRUD 代码(❗)

2.3.1、说明

  • 环境仅需搭建一次,后续操作修改映射文件即可。
  • MyBatis 默认不自动提交事务,DML 需要手动提交事务
  • 方法参数列表的第 2 个参数,对应 SQL 语句的 parameterType

2.3.2、工具类(❗)

MyBatis 中的 SQL 执行对象是SqlSession(相当于 JDBC 的 Statement)

  • 工厂模式:SqlSession 实例由 SqlSessionFactory 工厂创建。
  • 建造者模式:SqlSessionFactory 由 SqlSessionFactoryBuilder 构建。

工具类:构建 SqlSessionFactory 工厂,提供 SqlSession 实例。

  1. 加载核心配置文件。

  2. 构建 sqlSessionFactory 工厂对象。

  3. 通过工厂创建 sqlSession 实例。

    public class MyBatisUtils {
        private static final SqlSessionFactory SQL_SESSION_FACTORY;
    
        static {
            String resource = "mybatis-config.xml";
            InputStream is = null;
            try {
                // 加载核心配置文件
                is = Resources.getResourceAsStream(resource);
            } catch (IOException e) {
                e.printStackTrace();
            }
            // 构建sqlSessionFactory
            SQL_SESSION_FACTORY = new SqlSessionFactoryBuilder().build(is);
        }
    
        public static SqlSession getSqlSession() {
            // 创建sqlSession实例
            return SQL_SESSION_FACTORY.openSession();
        }
    }
    

2.3.3、使用步骤

  1. 获取 sqlSession 实例(从工具类中获取)。

  2. 执行 SQL,获得返回结果。

  3. * 提交事务

  4. 释放资源。

    @Test
    public void testInsert() {
        User user = new User("u_demo", "demo", "123456");
    
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
    
        int row = sqlSession.insert("userMapper.insertUser", user);
        System.out.println(row);
    
        sqlSession.commit();
        sqlSession.close();
    }
    
    @Test
    public void testDelete() {
        String userId = "u_demo1";
    
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
    
        int row = sqlSession.delete("userMapper.deleteUserByUserId", userId);
        System.out.println(row);
    
        sqlSession.commit();
        sqlSession.close();
    }
    
    @Test
    public void testUpdate() {
        User user = new User("u_demo2", "demo666", "666666");
    
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
    
        int row = sqlSession.update("userMapper.updateUser", user);
        System.out.println(row);
    
        sqlSession.commit();
        sqlSession.close();
    }
    
    @Test
    public void testQuery() {
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
    
        List<User> userList = sqlSession.selectList("userMapper.listUsers");
        System.out.println(userList);
    
        sqlSession.close();
    }
    

3、常见问题

3.1、常见报错

java.io.IOException

Could not find resource resources/mybatis-config.xml

  • 报错:无法找到 MyBatis 核心配置文件。
  • 原因:工具类 MyBatisUtils 中的 resource 变量值有误

org.apache.ibatis.binding.BindingException

Type interface XxxMapper is not known to the MapperRegistry.

  • 报错:Mapper 未知。
  • 原因:没有在 MyBatis 核心配置文件中注册 Mapper(注意路径分隔符为 /)。

java.lang.ExceptionInInitializerError

  • 报错:初始化失败,无法找到 XxxMapper.xml 配置文件。

  • 原因:资源配置文件没有存放在 resources 目录下。

  • 说明

    1. Maven 项目在构建编译时,默认只会将 resources 目录下的资源配置文件导出到 target 目录中。
    2. 在实际项目中,可能会把资源配置文件放在 resources 以外的位置。
      (如 Mapper.xml 和接口类放在一起)
    3. 这些资源配置文件不会被导出,导致资源配置文件读取失败。
  • 解决:两种方案

    1. resources 目录下建立与 Java 代码的同级目录,存放资源配置文件。

    2. 项目 pom.xml设置过滤

      <build>
          <resources>
              <resource>
                  <directory>src/main/resources</directory>
                  <includes>
                      <include>**/*.properties</include>
                      <include>**/*.xml</include>
                  </includes>
                  <filtering>true</filtering>
              </resource>
              <resource>
                  <directory>src/main/java</directory>
                  <includes>
                      <include>**/*.properties</include>
                      <include>**/*.xml</include>
                  </includes>
                  <filtering>true</filtering>
              </resource>
          </resources>
      </build>
      

无效的 2 字节、3 字节等问题

  • 原因:在 XML 中使用中文注释。

  • 说明:字符编码问题。

    • IntelliJ IDEA 默认使用 GBK 对资源配置文件编码,以上的 XML 文件使用 UTF-8
    • 此时,若在 XML 文件中使用中文注释会报此异常。
  • 解决:将 IntelliJ IDEA 的字符编码设为 UTF-8(开启 IDEA 的小窗口、进入项目的设置,2 处都需设置)

    image-20210723012530332

3.2、SQL 注入问题

(回顾 JDBC - 2.5.1

Web 攻击技术:通过特殊字符 恶意拼接 SQL,对 SQL 语句进行转义,从而非法运行 SQL

解决思路:通过占位符的方式,取代 SQL 拼接。

JDBC MyBatis
拼接字符串 statement $
预处理 SQL preparedStatement + 占位符? #{value}

3.3、参数问题(❗)

3.3.1、说明

  1. 参数对应问题
    • Mapper 接口方法的参数,对应 Mapper.xml 映射文件的 parameterType。
    • Mapper 接口方法的返回值,对应 Mapper.xml 映射文件的 resultType。
  2. 参数省略
    • parameterType 为简单类型(基本类型、String)时可省略,MyBatis 会自动识别类型。
    • resultType 通常为自定义 pojo,不能省略。

3.3.2、多个参数

接口方法的参数列表有多个简单类型参数(基本类型、String)

以获取登录用户为例,演示以下 2 种处理方式。

  • @Param 注解
  • Map 集合

@Param 注解

  • Mapper 接口:在简单类型参数前添加 @Param 注解。

    User getLoginUser(@Param("name") String name,
                      @Param("password") String password);
    
  • Mapper.xml:SQL 语句的 #{} 填写注解值。

    <select id="getLoginUser" resultType="user">
        SELECT user_id, name, password
        FROM study_mysql.t_user
        WHERE name = #{name}
          AND password = #{password}
    </select>
    

Map 集合

  • Mapper 接口:将简单类型参数封装成 Map 集合

    User getLoginUser1(Map<String, String> userMap);
    
  • Mapper.xml:SQL 语句的 #{} 填写 Map 的 Key 值。

    <select id="getLoginUser1" resultType="user">
        SELECT user_id, name, password
        FROM study_mysql.t_user
        WHERE name = #{name}
          AND password = #{password}
    </select>
    
  • 测试:封装 map,调用接口方法。

    @Test
    public void testGetLoginUser1() {
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    
        HashMap<String, String> map = new HashMap<>();
        map.put("name", "Jaywee");
        map.put("password", "123456");
    
        User user = mapper.getLoginUser1(map);
        System.out.println(user);
    
        sqlSession.close();
    }
    
posted @ 2021-07-22 18:54  Jaywee  阅读(234)  评论(0编辑  收藏  举报

👇