mybatis中vfs设置 mybatis eviction_SQL

MyBatis 是一款优秀的持久层框架,封装 JDBC 操作,基于 XML 或注解方式使用,支持自定义 SQL、存储过程、高级映射。

基础知识

  • Java 基础
  • MySQL 数据库
  • JDBC
  • Maven、JUnit 工具

1、概述

1.1、JDBC

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

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

1.1.1、开发步骤

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

1.1.2、问题分析(❗)

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

1.2、MyBatis

1.2.1、说明

原 apache 的开源项目 iBatis。

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

2013 年 11 月迁移到 Github。

  • 基于 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、数据库表

mybatis中vfs设置 mybatis eviction_SQL_02

  • 主键设计
  • 逻辑主键: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>
  1. 实体类: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 处都需设置)

mybatis中vfs设置 mybatis eviction_xml_03

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。
  1. 参数省略
  • 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();
}