我是靠谱客的博主 香蕉咖啡豆,最近开发中收集的这篇文章主要介绍万字 MyBatis 带你从入门到精通!!!MyBatis,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

文章目录

  • MyBatis
    • 1. 入门使用
      • 1.1 导入相关 Maven 依赖
      • 1.2 创建并配置核心文件
      • 1.3 编写 MyBatis 工具类
      • 1.4 编写实体类
      • 1.5 编写实体类对应的 Mapper 接口
      • 1.6 编写 Mapper 配置文件
      • 1.7 测试
      • 1.8 一些可能的问题
    • 2. CRUD
      • 2.1 Select
      • 2.2 Update、Delete、Insert —(增删改需要提交事务)
      • 2.3 参数传递
      • 2.4 模糊查询
    • 3. 生命周期和生命周期
      • 3.1 SqlSessionFactoryBuilder:
      • 3.2 SqlSessionFactory:
      • 3.3 SqlSession
    • 4. 配置解析—核心配置文件
      • 4.1 属性
      • 4.2 设置
      • 3.3 类型别名
      • 4.4 环境配置
      • 4.5 映射器
      • 4.6 其他配置
    • 5. 初识 resultMap
      • 5.1 遇到的问题:数据库中的字段名 和 实体类中的字段名 不一致。
      • 5.2 解法办法1:在 SQL 语句中加别名。
      • 5.3 解法办法2:resultMap(结果集映射)
    • 6. 分页
      • 6.1 使用Mybatis实现分页
      • 6.2 使用分页插件—PageHelper
    • 7. 使用注解开发
      • 7.1 基本使用
      • 7.3 有参数的情况
    • 8. 再遇 resultMap —— 高级结果映射
      • 8.1 构建环境
      • 8.2 复杂类型 association —— 多对一
        • 8.2.1 查询结果映射
        • 8.2.2 嵌套查询
      • 8.3 复杂类型集合 collection —— 一对多
        • 8.3.1 查询结果映射
        • 8.3.2 嵌套查询
      • 8.4
      • 8.5 总结
    • 9. 日志
    • 10. 动态 SQL
      • 10.1 if
      • 10.2 choose
      • 10.3 trim、where、set
        • 10.3.1 where
        • 10.3.2 set
        • 10.3.3 trim
      • 10.4 foreach
      • 10.5 总结
    • 10. 缓存
    • 11. MyBatis 总结
      • 点个关注,不再迷路

MyBatis

首先,什么是 MyBatis ?

官方介绍的很清楚:MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

1. 入门使用

在 Maven 项目中的使用。

先看下结构,避免一些不必要的问题。

1.1 导入相关 Maven 依赖

    <!--导入依赖-->
    <dependencies>
        <!--mysql驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <!--mybatis-->
        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.2</version>
        </dependency>
        <!--junit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>

1.2 创建并配置核心文件

在 resources 中创建一个 mybatis-config.xml. (名字可随意)

**注意:**这个配置中的数据库连接信息要换成你自己数据库的。

<?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核心配置文件 -->
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                <property name="username" value="root"/>
                <property name="password" value="1024"/>
            </dataSource>
        </environment>
    </environments>
<!-- Mapper 配置文件的位置 -->
    <mappers>
        <mapper resource="com/yuanxion/dao/UserMapper.xml"/>
    </mappers>
</configuration>

1.3 编写 MyBatis 工具类

编写 MyBatis 工具类可以方便我们使用 MyBatis ,就不需要我们每次都去写这一堆代码。

public class MyBatisUtils {
    private static SqlSessionFactory sqlSessionFactory;
    static{
        try {
            //使用Mybatis第一步:获取sqlSessionFactory对象
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
    //有了 SqlSessionFactory 之后,我们就可以从中获得 SqlSession 的实例了。
    // SqlSession 完全包含了面向数据库执行 SQL 命令所需的所有方法。
    public static SqlSession getSqlSession(){
        return sqlSessionFactory.openSession();
    }
}

1.4 编写实体类

这个实体类是数据库中所要操作表的对应的实体类。
类名、字段名,都要和数据库表中的一样,并提供相应的 get、set 方法。

public class User {
    private int id;
    private String name;
    private String pwd;

    public User() {
    }

    public User(int id, String name, String pwd) {
        this.id = id;
        this.name = name;
        this.pwd = pwd;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + ''' +
                ", pwd='" + pwd + ''' +
                '}';
    }
}

1.5 编写实体类对应的 Mapper 接口

编写实体类对应的 Mapper 接口,并在这个借口中添加需要进行的操作,比如查询出表中所有的数据。

public interface UserMapper {
    //查询所有用户信息
    List<User> getUserList();
}

1.6 编写 Mapper 配置文件

原来是要实现上面的Mapper接口,现在在 Mapper 配置文件绑定和配置。

<?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">

<!-- namespace=绑定一个对应的Dao/Mapper接口 -->
<mapper namespace="com.yuanxion.dao.UserMapper">
    
    <!-- select查询语句 -->
    <select id="getUserList" resultType="com.yuanxion.pojo.User">
       select * from user
   </select>
    
</mapper>

select 标签中:

  • id : 对应的 namespace 绑定的接口中的方法名。

  • resultType:Sql 语句执行之后的返回值。

1.7 测试

测试一下数据库连接和查询:

@Test
    public void test() {
        
        //1. 得SqlSession对象
        //因为前面已经写好了工具类,直接使用工具类来获取即可。
        SqlSession sqlSession = MyBatisUtils.getSqlSession();

        //2. 从 sqlSession 中获取所需实体类的 Mapper 
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        
        //3. 使用该 Mapper 调用我们在 Mapper 配置文件中配置的查询方法
        List<User> userList = userMapper.getUserList();

        //4. 遍历我们的查询结果并输出
        for (User user : userList) {
            System.out.println(user);
        }

        //5. 关闭SqlSession
        sqlSession.close();

    }

如果没什么问题,我们就可以链接并查询成功了。

1.8 一些可能的问题

如果发生了异常,可能是如下原因中的一种:

  1. 核心配置文件中的数据库连接信息没有改成自己的。
  2. 核心配置文件中的 Mapper 配置文件的位置 没有改成自己的。
  3. 方法名或返回类型写错了。
  4. 没有导入相应的 Maven 资源
  5. 字符集问题。
  6. 配置文件中的写了中文注释。
    虽然我写了中文注释并没有什么问题,但是发现有些人报错的原因在这里。可以删除试试。
  7. 更多可能的问题,根据报错信息可以去百度、谷歌等查询解决办法。

2. CRUD

2.1 Select

2.1.1 在对应 Mapper 类 UserMapper 中添加接口

//根据用户 ID 查询用户
User getUserById(int id);

2.1.2 在 Mapper 配置文件中编写 SQL 语句

<!-- 根据 ID 查询用户 -->
    <select id="getUserById" parameterType="int"  resultType="com.yuanxion.pojo.User">
       select * from user where id = #{id}
    </select>

select 标签中的:

  • id : 对应的 namespace 绑定的接口中的方法名。
  • resultType:Sql 语句执行之后的返回值。
  • parameterType:这个SQL所对应方法传入的参数的类型。

方法传入的参数用 #{参数名} 接收,类似于一个占位符。

比如这个方法中传入的是 id,那这里就使用 #{id} 接收。

2.1.3 测试

@Test
public void testSelect() {
    //第一步:获得SqlSession对象
    SqlSession sqlSession = MyBatisUtils.getSqlSession();

    //方式一:getMapper
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    User user = userMapper.getUserById(1);

    System.out.println(user);

    //关闭SqlSession
    sqlSession.close();

}

2.2 Update、Delete、Insert —(增删改需要提交事务)

**注意:**增删改这3个操作需要提交事务!!(sqlSession.commit();)

2.2.1 在对应 Mapper 类 UserMapper 中添加接口

public interface UserMapper {
    //根据用户 ID 查询用户
    User getUserById(int id);

    //新增一个用户
    void addUser(User user);

    //修改一个用户
    void updateUser(User user);

    //根据 id 删除一个用户
    void deleteUser(int id);

}

2.2.2 在 Mapper 配置文件中编写 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">

    <!-- namespace=绑定一个对应的Dao/Mapper接口 -->
    <mapper namespace="com.yuanxion.dao.UserMapper">

    <!-- 新增一个用户 -->
    <insert id="addUser"  parameterType="com.yuanxion.pojo.User">
       insert into user (id, name, pwd) values (#{id}, #{name}, #{pwd});
    </insert>

    <!-- 修改一个用户 -->
    <update id="updateUser" parameterType="com.yuanxion.pojo.User">
        update user set name=#{name},pwd=#{pwd}  where id = #{id} ;
    </update>

    <!-- 根据 id 删除一个用户 -->
    <delete id="deleteUser" parameterType="int">
       delete from user where id = #{id};
    </delete>

</mapper>
  • parameterType 传入的参数类型可以是一个对象,#{参数} 可以直接从传入的对象中获取。

    比如例子中传入的是 User 对象,#{id} 就可以直接获取传入 User 对象的 id。

2.2.3 测试

我们前面两步写好之后,增、删、改 这些操作都差不多。

但是一定要注意的是:增、删、改 之后要提交事务。(sqlSession.commit();)

    @Test
    public void testAddUser() {
        //第一步:获得SqlSession对象
        SqlSession sqlSession = MyBatisUtils.getSqlSession();

        //方式一:getMapper
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        //创建一个 User 对象
        User user = new User(33, "3333", "3333");
        //添加一个User对象
        userMapper.addUser(user);

        //注意:增、删、改 之后,要提交事务。
        sqlSession.commit();

        //关闭SqlSession
        sqlSession.close();

    }


@Test
    public void testUpdateUser() {
        //第一步:获得SqlSession对象
        SqlSession sqlSession = MyBatisUtils.getSqlSession();

        //方式一:getMapper
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        //创建一个 User 对象
        User user = new User(33, "4444", "4444");
        //添加一个User对象
        userMapper.updateUser(user);

        //注意:增、删、改 之后,要提交事务。
        sqlSession.commit();

        //关闭SqlSession
        sqlSession.close();

    }

@Test
    public void testDeleteUser() {
        //第一步:获得SqlSession对象
        SqlSession sqlSession = MyBatisUtils.getSqlSession();

        //方式一:getMapper
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        //删除一个User对象
        userMapper.deleteUser(33);

        //注意:增、删、改 之后,要提交事务。
        sqlSession.commit();

        //关闭SqlSession
        sqlSession.close();

    }

2.3 参数传递

可以使用 Map 传参

当数据库中的表或者实体类字段过多,可以使用 Map 来传参。

//参数类型改为 Map
int addUser(Map<String,Object> map);
<!--对象中的属性,可以直接取出来。我们只需传递map的key-->
<insert id="addUser" parameterType="map">
	insert into mybatis.user (id, pwd) values (#{id},#{passWord});
</insert>
    @Test
    public void addUser2(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();

        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        Map<String, Object> map = new HashMap<String, Object>();
        
        map.put("id",6666);
        map.put("passWord","6666");

        userMapper.addUser(map);

        sqlSession.commit();
        sqlSession.close();
    }

2.4 模糊查询

模糊查询有两种写法:

  1. 在 Java 程序传入参数时,加上通配符 % %

    List<User> userList = mapper.getUserNameLike("%段%");
    
  2. 在 SQL 语句中拼接上通配符 % %

    select * from user where name like "%"#{value}"%"
    

3. 生命周期和生命周期

不同作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题。

3.1 SqlSessionFactoryBuilder:

**生命周期:**一旦创建了 SqlSessionFactory,就不再需要它了。

作用域:最佳作用域是方法作用域(也就是局部方法变量)。

3.2 SqlSessionFactory:

生命周期:一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。

作用域:最佳作用域是应用作用域。最简单的就是使用单例模式或者静态单例模式实现。

3.3 SqlSession

生命周期: 一次请求。也就是每个请求,可以打开一个 SqlSession,返回一个响应后,就关闭这个SqlSession。

作用域:因为SqlSession 的实例是线程不安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域

注意:每次使用完一个 SqlSession ,要记得关闭,不然会一直占用资源。可以把关闭操作放到 finally 块中。

一个确保 SqlSession 关闭的标准模式的实例:

try (SqlSession session = sqlSessionFactory.openSession()) {
  // 你的应用逻辑代码
}

4. 配置解析—核心配置文件

核心配置文件:mybatis-config.xml (可以不取这个名字。)

  • MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。

几个比较重要的配置:属性、设置、类型别名、环境配置、映射器。

4.1 属性

属性(properties)

我们可以通过properties属性来实现引用配置文件。

这些属性都是可外部配置且可动态替换的,既可以在典型的 Java 属性文件中配置,亦可通过 properties 元素的子元素来传递。

演示:

我们在资源文件夹resource中加入一个 db.properties 文件,并编写相关信息:

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8
username=root
password=1024

然后在配置文件 mybatis-config.xml 中就可以直接引用

<?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核心配置文件 -->
<configuration>

    <!--引入外部配置文件-->
    <properties resource="db.properties"/>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
    
<!-- Mapper 配置文件的位置 -->
    <mappers>
        <mapper resource="com/yuanxion/dao/UserMapper.xml"/>
    </mappers>
</configuration>

文件结构

并且,还可以通过 properties 元素的子元素来传递

<?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核心配置文件 -->
<configuration>
    
    <!--引入外部配置文件,并可通过子元素来传递-->
    <properties resource="db.properties">
        <property name="username" value="root"/>
        <property name="pwd" value="1024"/>
    </properties>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>

    <!-- Mapper 配置文件的位置 -->
    <mappers>
        <mapper resource="com/yuanxion/dao/UserMapper.xml"/>
    </mappers>
</configuration>

**注意1:**如果 db.properties 和 配置文件中配置了同一个字段,会优先使用外部配置文件db.properties 的。

注意2:这核心配置文件中,标签是有顺序规定的。不过不用去记顺序,放错的顺序会有提示,根据提示修改即可。

4.2 设置

设置(settings):MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。

比如可以设置:是否开启驼峰命名自动映射、指定MyBatis所用日志的具体实现 等。

更多设置可查看:https://mybatis.org/mybatis-3/zh/configuration.html

3.3 类型别名

**类型别名 (typeAliases):**可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。

比如我们在 mybatis-config.xml 中这样配置:

<!--可以给实体类起别名-->
<typeAliases>
	<typeAlias type="com.yuanxion.pojo.User" alias="User"/>
</typeAliases>

这样配置之后,在 Mapper配置文件中 用这个 com.yuanxion.pojo.User 对象就可以直接使用别名 User :

<!-- 未在mybatis-config.xml中配置别名时,需要使用全限定类名 -->
<select id="getUserList" resultType="com.yuanxion.pojo.User">
	select * from user
</select>

<!-- 在mybatis-config.xml中配置别名后,使用别名即可 -->
<select id="getUserList" resultType="User">
	select * from user
</select>

可以指定一个包,MyBatis 会在包名下面搜索需要的 Java Bean。

也就是说会扫描我们指定的实体类的包,然后自动给包内的实体类起别名,别名默认为 首字母小写的类名

<!--可以给指定一整个包,给实体类自动起别名,别名默认为 首字母小写的类名。-->
<typeAliases>
    <package name="com.yuanxion.pojo"/>
</typeAliases>

如果是指定了一个包,并且还想自定义别名的话,可以在实体类上增加 @Alias(“xxx别名”) 注解:

public class User {

}

4.4 环境配置

环境配置(environments)MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中。

例如,开发、测试和生产环境需要有不同的配置;或者想在具有相同 Schema 的多个生产数据库中使用相同的 SQL 映射 等场景。

<environments default="development">
	<environment id="development">
	<transactionManager type="JDBC"/>
		<dataSource type="POOLED">
			<property name="driver" value="${driver}"/>
			<property name="url" value="${url}"/>
			<property name="username" value="${username}"/>
			<property name="password" value="${password}"/>
		</dataSource>
	</environment>
</environments>

**不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。**简单来说就是 想连接几个数据库,就需要创建几个 SqlSessionFactory 实例。

关键点:

  • 默认使用的环境 ID(比如:default=“development”)。
  • 每个 environment 元素定义的环境 ID(比如:id=“development”)。
  • 事务管理器的配置(比如:type=“JDBC”)。
  • 数据源的配置(比如:type=“POOLED”)。

Mybatis 默认的事务管理器JDBC ,默认使用的连接池是 : POOLED。

不过如果使用的是 Spring + MyBatis,则没有必要配置事务管理器,因为 Spring 模块会使用自带的管理器来覆盖前面的配置。

4.5 映射器

映射器 (mappers):我们配置了上述一系列 MyBatis 的行为之后,要来定义 SQL 映射语句了,而首先,我们需要告诉 MyBatis 到哪里去找到这些语句,也就是要告诉 MyBatis 到哪里去找映射文件。

简单来说就是 注册绑定我们写的的Mapper文件

可以使用相对于类路径的资源引用,或完全限定资源定位符(包括 file:/// 形式的 URL),或类名和包名等方式。

方式一: 使用相对于类路径的资源引用【推荐使用】

<!-- 每一个Mapper.XML都需要在Mybatis核心配置文件中注册 -->
<mappers>
    <mapper resource="com/yuanxion/dao/UserMapper.xml"/>
</mappers>

**方式二:**使用映射器接口实现类的完全限定类名

<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
    <mapper class="com.yuanxion.dao.UserMapper"/>
</mappers>

需要注意的是:

  1. 接口和他的Mapper配置文件必须同名
  2. 接口和他的Mapper配置文件必须在同一个包下

**方式三:**使用扫描包进行注入绑定

<!--将包内的映射器接口实现类全部注册为映射器!-->
<mappers>
    <package name="com.yuanxion.dao"/>
</mappers>

同样需要注意的是:

  1. 接口和他的Mapper配置文件必须同名
  2. 接口和他的Mapper配置文件必须在同一个包下

4.6 其他配置

需要使用其他配置时,可去官网查看:https://mybatis.org/mybatis-3/zh/configuration.html

5. 初识 resultMap

5.1 遇到的问题:数据库中的字段名 和 实体类中的字段名 不一致。

比如 :
数据库中的字段是:id、name、pwd ;

实体类中的字段是:id、name、password 。

这样的话,当我们去查询并输出时会发现,虽然还是查询到数据,password 却为null。

因为 数据库中的字段名 和 实体类中的字段名 不一致时,无法将数据映射到不一致的字段上。

5.2 解法办法1:在 SQL 语句中加别名。

比如数据库中字段是 pwd,实体类中字段是 password,那么我们在写SQL语句时,给 pwd 起一个别名为 passw 即可。

<select id="getUserById" resultType="com.yuanxion.pojo.User">
    select id,name,pwd as password from user where id = #{id}
</select>

5.3 解法办法2:resultMap(结果集映射)

编写对应数据库表和实体类的 resultMap 。

然后将 SQL语句上 resultType 改为 resultMap,并绑定对应的 resultMap 的 id。

<!-- 结果集映射 -->
<resultMap id="UserMap" type="com.yuanxion.pojo.User">
    <!-- column数据库中的字段,property实体类中的属性 -->
    <result column="id" property="id"/>
    <result column="name" property="name"/>
    <result column="pwd" property="password"/>
</resultMap>

<select id="getUserById" resultMap="UserMap">
    select * from user where id = #{id}
</select>

注意,对于那些 数据库中的字段名 和 实体类中的字段名 一致的字段,在 resultMap 中可以省略不写:

<!-- 结果集映射 -->
<resultMap id="UserMap" type="com.yuanxion.pojo.User">
    <!-- column数据库中的字段,property实体类中的属性 -->
    <!-- 因为 id、name 字段一致,默认会映射,可以省略不写 -->
    <result column="pwd" property="password"/>
</resultMap>

<select id="getUserById" resultMap="UserMap">
    select * from user where id = #{id}
</select>

resultMap 元素是 MyBatis 中最重要最强大的元素,上面只是简单的结果映射,高级结果映射会复杂些,后面再说。

6. 分页

首先回顾一下在数据库中我们分页的语法:

SELECT * from user limit startIndex,pageSize;

简单理解就是:从第 startIndex 个开始,找 pageSize 个符合条件的数据,包含 startIndex 这个。

比如:

找出从第 0 个开始,找到 1 条符合条件的数据:

SELECT * from user limit 0,1; 

找出从第 3 个开始,找到 4 条符合条件的数据:

SELECT * from user limit 3,4; 

而在我们的程序中,分页的方法有很多,下面说两种:

6.1 使用Mybatis实现分页

使用方法其实和其他 Mybatis 中的 SQL 没什么区别,就是传参的时候注意一下。

用 map 传参比较方便(对因为象中的属性,可以直接取出来。我们只需传递map的key即可找到对应参数。当然也可以用其他的方式传参)。

这里说一下核心的地方:

  1. mapper 接口

    List<User> getUserByLimit(Map<String,Integer> map);
    
  2. Mapper.xml 文件

    <select id="getUserByLimit" parameterType="map" resultMap="UserMap">
        select * from user limit #{startIndex},#{pageSize}
    </select>
    
  3. 使用测试:

    @Test
    public void getUserByLimit(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    
        //用 map 存放分页条件
    	HashMap<String, Integer> map = new HashMap<String, Integer>();
        map.put("startIndex",1);
        map.put("pageSize",2);
    
        //分页查询
        List<User> userList =  mapper.getUserByLimit(map);
        
        //遍历查看查询结果
        for (User user : userList) {
        System.out.println(user);
        }
    
        sqlSession.close();
    }
    

6.2 使用分页插件—PageHelper

使用插件来实现分页就很简单了,这里说的的是 PageHelper ,当然也可以使用其它分页插件。

PageHelper:https://pagehelper.github.io/

下面简单说一下使用方法:

  1. 在 pom.xml 中导入 PageHelper 的依赖

    <dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper</artifactId>
        <version>5.1.10</version>
    </dependency>
    
  2. 在 MyBatis 核心配置 MyBatis-config.xml 中配置拦截器插件
    (提示:如果是 Spring 和 MyBatis 一起使用,那在二者的整合配置文件中配置即可。)

     <!-- 配置文件中的位置:environments 前面一个-->
    <plugins>
            <!-- com.github.pagehelper为PageHelper类所在包名,无需修改 -->
            <plugin interceptor="com.github.pagehelper.PageInterceptor">
                <!-- 使用下面的方式配置参数,后面会有所有的参数介绍 -->
                <property name="helperDialect" value="mysql"/>
            </plugin>
        </plugins>
    
  3. 调用方法
    就是在正常查询语句之前,设置一下分页参数即可:

        @Test
        public void testPageHelper() {
            SqlSession sqlSession = MyBatisUtils.getSqlSession();
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    
            //设置要查询第几页的数据,以及每页的大小
            //比如要查询第2页的数据,每页2个数据
            PageHelper.startPage(2, 2);
    
            //设置完之后,就可以查询了
            List<User> userList = userMapper.getUserList();
    
            //遍历查看查询到的数据
            for (User user : userList) {
                System.out.println(user);
            }
    
            //关闭SqlSession
            sqlSession.close();
    
        }
    

    使用起来还是很简单很方便的,不过这里只是简单的示范了一下使用,更多是使用方法和使用细节,可以去查看官方文档:https://pagehelper.github.io/

    大家不要一听官方文档就觉得很难,其实官方文档大部分都很通俗易懂。

7. 使用注解开发

简单的 SQL 语句,我们可以直接在相应 Mapper接口中的方法 上使用注解来实现。

7.1 基本使用

  1. 首先还是要在核心配置文件中还是要绑定相应接口的。
    比如我们要给实现 UserMapper 接口中的方方法,那就要绑定 UserMapper :

    <!--绑定接口-->
    <mappers>
        <mapper class="com.yuanxion.dao.UserMapper"/>
    </mappers>
    
  2. 在 UserMapper 接口中的 getUserList() 上使用注解:

    @Select("select * from user")
    List<User> getUserList();
    
  3. 就可以使用 getUserList() 方法了。

7.3 有参数的情况

有参数的时候,需要在方法参数前加上 @Param(“xxx”) 绑定字段:

@Select("select * from students where garde = #{garde} and age = #{agedeff}")
List<Student> getStudentsByGardeAndAge(@Param("garde") int garde,@Param("agedeff") int age);

注意1: 参数是基本数据类型或者String类型才要加 @Param(“xxx”) 注解;如果是引用类型的参数,不需要加。

注意2:

  1. @Select 注解中 #{} 中用的是 @Param() 中的字段名
  2. @Param() 中的字段名可以和数据字段名中不一样(不过推荐还写一样的)。

看个图可能更清楚:

(另外如果只有一个参数,可以省略@Param注解 ,不过推荐还是加上。)

8. 再遇 resultMap —— 高级结果映射

回顾:

上面我们已经简单的使用过 resultMap ——结果映射。

他的作用简单来说,就像它的名字一样,结果映射,也就是将我们从数据库中查询到的 列数据 映射到我们的 实体类中的 字段 上去。

比如

实体类的属性是:id,name,password;

数据库中的列是:id,name,pwd;

那么 resultMap 可以把我们从数据库中查询出的每条数据中的 id,name,pwd 依次分别对应到一个实体类的 id,name,password 上。

以上是简单的使用,很简单,没什么问题。

问题来了,上面都是数据库中的一个 列 对应实体类的中一个 属性,那么:

  1. 如果实体类中的字段是一个复杂类型,比如对象 student ,那么该怎么映射?
  2. 如果实体类中的字段是一个复杂类型的集合,比如 ArrayList ,那么又该怎么映射?

放心,resultMap 已经帮我们解决了这些问题。

来看一下 resultMap 中的子元素:

我们来说一下其中个常用的:

  1. **resutlt:**用于完成普通列的映射(上面已经使用过了)。
  2. **id:**用于完成主键值的映射(用法和 result 一样,唯一区别就是 id 是用于标识 主键,可提高整体性能)。
  3. **association:**一个复杂类型的关联,可用于嵌套结果映射。
  4. **collection:**多个复杂类型的集合,同样可用于嵌套结果映射。

其中 resutlt 和 id 用法简单就不再反复说了,下面结合案例重点说一下 associationcollection

8.1 构建环境

先建数据库表。

一个学生表,每个学生有一个老师;

一个老师表,一个老师有多个学生。

建表 SQL:

CREATE TABLE `teacher`(
`id` INT NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
PRIMARY KEY(`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8


CREATE TABLE `student`(
`id` INT NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
`tid` INT DEFAULT NULL,
PRIMARY KEY(`id`),
KEY `fktid`(`tid`),
CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8


INSERT INTO teacher(`id`,`name`)VALUES (1,'张三老师');
INSERT INTO teacher(`id`,`name`)VALUES (2,'李四老师');


INSERT INTO `student`(`id`,`name`,`tid`) VALUES(1,'王秀儿',1);
INSERT INTO `student`(`id`,`name`,`tid`) VALUES(2,'孙六六',1);
INSERT INTO `student`(`id`,`name`,`tid`) VALUES(3,'段诗云',1);
INSERT INTO `student`(`id`,`name`,`tid`) VALUES(4,'叶菲儿',1);
INSERT INTO `student`(`id`,`name`,`tid`) VALUES(5,'红红',2);
INSERT INTO `student`(`id`,`name`,`tid`) VALUES(6,'静静',2);

8.2 复杂类型 association —— 多对一

数据库表如上:相对于学生,是多个学生对一个老师。

实体类:

Teacher 实体类

public class Teacher {
    private int id;
    private String name;
    
 	//记得生成 无参、有参构造器,get、set方法,toString方法
    
}

Student 实体类

public class Student {
    private int id;
    private String name;
    private Teacher teacher;

    //记得生成无参、有参构造器,get、set方法,toString方法

}

(MyBatis 的环境搭建就不重复写了,这里主要是学 association 。)

需求:查询所有学生的信息以及对应老师的信息。

先看这个需求的原始 SQL 语句:

SELECT s.id sid, s.name sname, t.name tname,t.id tid
FROM student s,teacher t
WHERE s.tid = t.id

8.2.1 查询结果映射

我们可以先从数据库中查询出结果,也就是四个列的数据:sid,sname,tname,tid。

而学生实体类的字段为:id、name、teacher。

那么我们就需要将结果中的:

sid 映射到 id 上;

sname 映射到 name 上;

tname 和 tid 一起映射到 teacher 上。

具体实现就是:

<!-- association 之 查询结果映射 -->
    <select id="GetStudentAndTeacher" resultMap="StudentAndTeacher">
        SELECT s.id sid, s.name sname, t.name tname,t.id tid
        FROM student s,teacher t
        WHERE s.tid = t.id
    </select>

    <!-- resultMap -->
    <resultMap id="StudentAndTeacher" type="com.yuanxion.pojo.Student">
        <result property="id" column="sid"/>
        <result property="name" column="sname"/>
        <!--复杂类型 使用 association 来映射 -->
        <association property="teacher" javaType="com.yuanxion.pojo.Teacher">
            <result property="id" column="tid"/>
            <result property="name" column="tname"/>
        </association>
    </resultMap>

解释一下:

简单来说就是,先查询出所有字段,然后使用 resultMap 自定义映射;其中普通字段直接映射;而复杂类型,其实就是用多个字段映射成一个对象。

8.2.2 嵌套查询

思路其实就是嵌套子查询:先查出所有学生信息,再根据学生信息中的 tid 找到相应老师。

具体实现:

<!-- association 之 嵌套查询 -->
<!--1. 查询出所有的学生-->
<select id="GetStudentAndTeacher2" resultMap="StudentAndTeacher2">
    SELECT * FROM student
</select>


<!-- 2.resultMap映射结果 -->
<resultMap id="StudentAndTeacher2" type="com.yuanxion.pojo.Student">
    <result property="id" column="id"/>
    <result property="name" column="name"/>
    <!--复杂类型 使用 association 来映射结果是从一个子查询中获取-->
    <association property="teacher" column="tid" javaType="com.yuanxion.pojo.Teacher" select="getTeacher" />
</resultMap>

<!-- 嵌套子查询,为复杂类型赋值 -->
<select id="getTeacher" resultType="com.yuanxion.pojo.Teacher">
    SELECT * FROM teacher
    WHERE
    id = #{tid}
</select>

解释一下:

简单来说就是嵌套子查询,子查询的整个结果作为复杂类型字段。

8.3 复杂类型集合 collection —— 一对多

collection 其实和 association 用法差不多,来看一下。

数据库表还是上面:相对于老师,是一个老师对多个学生。

实体类:

Teacher 实体类

public class Teacher {
    private int id;
    private String name;
    //一个老师对应多个学生
    private ArrayList<Student> students;
    
 	//记得生成 无参、有参构造器,get、set方法,toString方法
    
}

Student 实体类

public class Student {
    private int id;
    private String name;
    private tid;

    //记得生成无参、有参构造器,get、set方法,toString方法

}

(MyBatis 的环境搭建同样就不重复写了,这里主要是学 collection。)

需求:查询所有学生的信息以及对应老师的信息。

先看这个需求的原始 SQL 语句:

select s.id sid, s.name sname, t.name tname,t.id tid
from student s,teacher t
where s.tid = t.id and t.id = #{tid}

8.3.1 查询结果映射

我们可以先从数据库中查询出结果,也就是四个列的数据:tname,tid,sid,sname。

而老师实体类的字段为:id、name、ArrayList students

那么我们就需要将结果中的:

tid 映射到 id 上;

tname 映射到 name 上;

将每一对 sid 和 snname 映射到集合中的每一个对象上。

<!-- collection  之 查询结果映射 -->
<select id="getTeacherAndStudent1" resultMap="StudentAndTeacher">
    select s.id sid, s.name sname, t.name tname,t.id tid
    from student s,teacher t
    where s.tid = t.id and t.id = #{tid}
</select>

<!-- resultMap -->
<resultMap id="StudentAndTeacher" type="com.yuanxion.pojo.Teacher">
    <result property="id" column="tid"/>
    <result property="name" column="tname"/>
    <!--复杂类型集合 使用 collection 来映射 -->
    <collection property="students" ofType="com.yuanxion.pojo.Student">
        <result property="id" column="sid"/>
        <result property="name" column="sname"/>
        <result property="tid" column="tid"/>
    </collection>
</resultMap>

collection 和 association 的区别在于:

association 类型元素是 javaType = ”复杂类型所对应的实体类“

collection 类型元素是 ofType= ” 集合中复杂类型所对应的实体类“

8.3.2 嵌套查询

思路其实就是嵌套子查询:先查出所有老师;再根据老师id,去学生表中查出该老师的所有学生,放在一个集合中。

<!-- collection 之 嵌套查询 -->
<!--1. 查询出所有的老师-->
<select id="getTeacherAndStudent2" resultMap="TeacherAndStudent2">
    SELECT *
    FROM teacher
    WHERE id = #{tid}
</select>


<!-- 2.resultMap映射结果 -->
<resultMap id="TeacherAndStudent2" type="com.yuanxion.pojo.Teacher">
    <result property="id" column="id"/>
    <result property="name" column="name"/>
    <!--复杂类型集合 使用 collection 来映射,结果是从一个子查询中获取-->
    <collection property="students" column="id" javaType="ArrayList" ofType="Student" select="getStudent" />
</resultMap>

<!-- 嵌套子查询,为集合中的所有复杂类型赋值 -->
<select id="getStudent" resultType="com.yuanxion.pojo.Student">
    SELECT * FROM student
    WHERE
    tid = #{tid}
</select>

简单来说就是嵌套子查询,子查询中的所有结果作为复杂类型集合。

8.4

8.5 总结

关于 association 和 collection:

  1. association: 用于实体类的字段是 复杂类型。比如老师对象:teacher。
  2. collection :用于实体类的字段是 复杂类型的集合。比如学生对象的集合:ArrayList students 。
  3. JavaType :用来指定实体类中普通字段的类型。比如老师实体类的类型:Teacher。
  4. ofType :用来指实体类中集合字段中集合元素的类型。 比如学生对象的集合中元素的类型:Student。

关于 查询结果映射 和 嵌套查询:

  1. 查询结果映射:
    ①先查询出所有字段;
    ②普通字段,直接映射;
    ③复杂类型字段,先将相应的普通字段映射成一个对象,再将这个对象映射到实体类中的复杂类型字段上。
  2. 嵌套查询:
    ①普通字段,直接查询出来,并映射。
    ②复杂类型字段,嵌套一个子查询,用子查询查询出数据,再映射到实体类中的复杂类型字段上。

9. 日志

日志可以帮助我们了解和记录查询的执行过程。

比如对于,MyBatis,开启日志之后,我们可以看到执行的 SQL 语句;发生错误时,便于我们排错。

MyBatis 支持的日志:

  1. SLF4J
  2. LOG4J
  3. LOG4J2
  4. JDK_LOGGING
  5. COMMONS_LOGGING
  6. STDOUT_LOGGING
  7. NO_LOGGING

其中常用的是 LOG4JSTDOUT_LOGGING

STDOUT_LOGGING 使用很简单,在核心配置文件 mybatis-config.xml 中加上下面这段配置即可:

<settings>
    <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

LOG4J 使用使用相对复杂些,但其实也简单:

  1. 导入 log4j 的包

    <!-- https://mvnrepository.com/artifact/log4j/log4j -->
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
    
  2. 编写 log4j 的配置文件:log4j.properties 或 log4j.xml

    (这里面配置很多,具体可以去百度查看)

  3. 在在核心配置文件 mybatis-config.xml 将日志配置成 log4j

    <settings>
        <setting name="logImpl" value="LOG4J"/>
    </settings>
    

实际开发中,可能用 log4j 比较多。

我们这里是为了方便学习下一个知识点(动态SQL),使用 STDOUT_LOGGING 就够用了,

10. 动态 SQL

动态 SQL 是 MyBatis 的强大特性之一。

动态 SQL 可以根据方法传入的不同参数,生成不同的 SQL 。

动态 SQL 可以简化我们拼装 SQL 的操作,而无需在 Java 代码去判断和拼接。

下面就来看一下 动态 SQL 。

先搭建一下环境:

数据库:

CREATE TABLE `blog` (
  `id` VARCHAR(50) NOT NULL COMMENT '博客id',
  `title` VARCHAR(100) NOT NULL COMMENT '博客标题',
  `author` VARCHAR(30) NOT NULL COMMENT '博客作者',
  `state` VARCHAR(30) NOT NULL COMMENT '状态',
  `tag` VARCHAR(30) NOT NULL COMMENT '标签'
) ENGINE=INNODB DEFAULT CHARSET=utf8

实体类:

public class Blog {
    private int id;
    private String title;
    private String author;
    private String state;
    private String tag;
 
    //构造函数、get & set 方法,toString方法。
}

10.1 if

if 可根据我们方法传入的不同条件动态生成不同的 SQL

演示:

BlogMapper 中方法:

public interface BlogMapper {
    ArrayList<Blog> findActiveBlogWithCondition(Map map);
}

BlogMapper.xml 中的动态 SQL :

<select id="findActiveBlogWithCondition" parameterType="map" resultType="com.yuanxion.pojo.Blog">
  SELECT * FROM BLOG
  WHERE state = '上架'
  <if test="title != null">
    AND title like #{title}
  </if>
  <if test="tag != null">
    AND tag like #{tag}
  </if>
</select>

我们的参数放在一个 map 中,根据我们写的动态 SQL 语句:

  1. 当我们的 map 中没有参数时,SQL 语句是

    SELECT * 
     FROM BLOG  
     WHERE state = '上架' ;
    
  2. 我们的 map 中有 <“tag”, “算法”> 一个键值对时,SQL 语句是

    SELECT * 
    FROM BLOG
    WHERE state = '上架' 
    AND tag like  '算法';
    
  3. 我们的 map 中有 <“tag”, “算法”>,<“title”, “Java”> 时,SQL 语句是

    SELECT * 
    FROM BLOG
    WHERE state = '上架'
    AND title like 'Java'
    AND tag like  '算法';
    

10.2 choose

上面的 if 会使用所有传入的满足条件的条件,但有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用

针对这种情况,MyBatis 提供了 choose 元素,它有点**像when 有一个条件满足时,会拼接这个满足条件 when 中的语句,不会再拼接 otherwise 中的语句。

演示:

BlogMapper 中方法:

public interface BlogMapper {
    ArrayList<Blog> findActiveBlogWithOneCondition(Map map);
}

BlogMapper.xml 中的动态 SQL :

<select id="findActiveBlogWithOneCondition" parameterType="map" resultType="com.yuanxion.pojo.Blog">
    SELECT * FROM BLOG
    WHERE state = '上架'
    <choose>
        <when test="title != null">
            AND title like #{title}
        </when>
        <when test="author != null">
            AND author like #{author}
        </when>
        <otherwise>
            AND tag like 'Java'
        </otherwise>
    </choose>
</select>

我们的参数放在一个 map 中,根据我们写的动态 SQL 语句:

  1. 当我们的 map 中没有参数时,SQL 语句是

    SELECT * 
    FROM BLOG
    WHERE state = '上架' 
    AND tag like 'Java'
    

    when 里面条件没满足,就会默认拼接 otherwise 中的语句。

  2. 我们的 map 中有 <“author”, “猿兄”> 一个键值对时,SQL 语句是

    SELECT * 
    FROM BLOG
    WHERE state = '上架' 
    AND author like '猿兄';
    

    when 有一个条件满足时,会拼接这个满足条件 when 中的语句,不会再拼接 otherwise 中的语句。

  3. 我们的 map 中有 <“author”, “猿兄”>,<“title”, “Mybatis解析”> 时,SQL 语句是

    SELECT * 
    FROM BLOG
    WHERE state = '上架'
    AND title like 'Mybatis解析';
    

    when 有多个条件满足时,会拼接满足条件 when 中,位置最上面的语句,不会再拼接其他 when 中的语句 以及 otherwise 中的语句。

10.3 trim、where、set

10.3.1 where

来看个问题,我们上面的动态语句中,where 后面是有个默认条件 state = ‘上架’ 的:

<select id="findActiveBlogWithCondition" parameterType="map" resultType="com.yuanxion.pojo.Blog">
  SELECT * FROM BLOG
  WHERE state = '上架'
  <if test="title != null">
    AND title like #{title}
  </if>
  <if test="tag != null">
    AND tag like #{tag}
  </if>
</select>

如果这个 state = ‘上架’ 也是可选条件,那么就变成了下面这种写法:

<select id="findBlogWithCondition" parameterType="map" resultType="com.yuanxion.pojo.Blog">
  SELECT * FROM BLOG
  WHERE 
  <if test="state != null">
    state like #{state}
  </if>
  <if test="title != null">
    AND title like #{title}
  </if>
  <if test="tag != null">
    AND tag like #{tag}
  </if>
</select>

这样的写法,如果每次查找条件中都有 state 条件那就没问题;

可是如果条件中没有第一个 state 条件,动态生成的 SQL 就会出问题。

比如,只给了 tag = ‘Java’ 条件,那么动态生成的 SQL就变成了:

SELECT * FROM BLOG
WHERE 
AND tag like 'Java'

可以看到,WHERE 后面多了一个 AND 。

这是不是就不太智能了?

放心,其实将 WHERE 改为 元素即可:

    <select id="findBlogWithCondition" parameterType="map" resultType="com.yuanxion.pojo.Blog">
        SELECT * FROM BLOG
        <where>
            <if test="state != null">
                state like #{state}
            </if>
            <if test="title != null">
                AND title like #{title}
            </if>
            <if test="tag != null">
                AND tag like #{tag}
            </if>
        </where>
    </select>

元素,如果第一个条件不存在,他拼接后面的 SQL 时,会自动删除掉 WHERE 子句开头多余的 AND 或 OR

10.3.2 set

set 元素和 where 元素功能差不多。

where 元素是删除自动删除 WHERE 子句开头多余的 AND 或 OR;

set 元素是自动删除 SET 子句结尾多余的 “,” 。

<update id="updateBlog" parameterType="map">
    update blog
    <set>
        <if test="state != null">
                state like #{state},
            </if>
        <if test="title != null">
            title = #{title},
        </if>
        <if test="author != null">
            author = #{author}
        </if>
    </set>
    where id = #{id}
</update>

10.3.3 trim

当上面的 where 元素和 set 元素不能满足你的动态SQL 时,可以使用自定义的 trim 元素。

trim 属性:

属性描述
prefix给sql语句拼接的前缀
suffix给sql语句拼接的后缀
prefixOverrides去除sql语句前面的关键字或者字符,该关键字或者字符由prefixOverrides属性指定,假设该属性指定为"AND",当sql语句的开头为"AND",trim标签将会去除该"AND"
suffixOverrides去除sql语句后面的关键字或者字符,该关键字或者字符由suffixOverrides属性指定

比如我们可以用 trim 自定义成一个和 set 一样的功能的元素:

<update id="updateBlog" parameterType="map">
    update blog
    <trim prefix="SET" suffixOverrides=",">
        <if test="state != null">
                state like #{state},
            </if>
        <if test="title != null">
            title = #{title},
        </if>
        <if test="author != null">
            author = #{author}
        </if>
    </trim>
    where id = #{id}
</update>

10.4 foreach

动态 SQL 的可以利用 foreach 元素对集合进行遍历。

比如,基于上面的环境,我们想实现是一个方法,查找指定id的一些博客:

Mapper:

public interface BlogMapper {
    ArrayList<Blog> findBlogByIds(List<Integer> list);
}

Mapper.xml:

<select id="findBlogByIds" parameterType="list" resultType="com.yuanxion.pojo.Blog">
        SELECT *
        FROM BLOG
        WHERE ID in
        <foreach collection="list" item="id" index="index"
                 open="(" separator="," close=")">
            #{id}
        </foreach>
    </select>

foreach 元素允许指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。

它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素会智能的添加,不会错误地添加多余的分隔符。

提示 你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。

那我们上面写的 feach 的动态SQL 来说:

  1. 如果 list 中只有一个值 1,那么动态生成的 SQL 就是:

    SELECT *
    FROM BLOG
    WHERE ID in (1)
    
  2. 如果 list 中只有3个值 1、3、4,那么动态生成的 SQL 就是:

    SELECT *
    FROM BLOG
    WHERE ID in (1,3,4)
    
  3. … …

10.5 总结

简单来说,动态 SQL 其实就是可以根据不同的条件,动态不同的SQL ;让我们可以简化拼装 SQL 的操作,而无需在 Java 代码中去判断和拼接。

10. 缓存

MyBatis 内置了一个事务性缓存机制。

(这个了解即可,真要使用缓存的话,还是要使用 Redis 或 Memcached 实现缓存。)

MyBatis 的缓存有两个级别:一级缓存二级缓存

**一级缓存(默认):**本地的会话缓存;可以理解为 SqlSession级别的缓存,也就是这个级别的缓存只在一个 SqlSession 中有效,然后这个 SqlSession 关闭了,这个缓存就没了。

**二级缓存:**全局缓存;可以理解为 SqlSessionFactory 级别的缓存,也就是这个级别的缓存,在所有这个 SqlSessionFactory 生成的 SqlSession 中共享。当一个 SqlSession 被关闭之前,会将其缓存保存。

默认是开启一级缓存,如果想开启二级缓存,只需要在你的 Mapper.xml 映射文件中添加一行:

<cache/>

加上之后的默认效果:

  • 映射语句文件中的所有 select 语句的结果将会被缓存。
  • 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
  • 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
  • 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
  • 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
  • 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。

提示 :缓存只作用于 cache 标签所在的映射文件中的语句。如果你混合使用 Java API 和 XML 映射文件,在共用接口中的语句将不会被默认缓存。你需要使用 @CacheNamespaceRef 注解指定缓存作用域。

然后上述的效果,可以通过 cache 元素的属性来修改,比如:

<cache
  eviction="FIFO"
  flushInterval="60000"
  size="512"
  readOnly="true"/>

这个配置的意思是:创建了一个清除策略为 FIFO 的缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。

清除策略是当缓存空间不足时,删除已有的缓存,来清理空间的策略,可用的清除策略有:

  • LRU – 最近最少使用:移除最长时间不被使用的对象。
  • FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
  • SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。
  • WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。

**注意:**因为增删改操作,可能会改变原来的数据,所以增删改操作会刷新缓存!

然后缓存的方式除掉上面的方式,还可以自定义缓存方式,了解就好;当然,有兴趣的可以去研究研究。(我就不去研究自定义缓存了,我选择用 Redis /手动狗头。)

11. MyBatis 总结

首先 MySQL 的基本使用很简单。

基本使用流程:

  1. 在pom.xml 中导入相关依赖

  2. 创建核心配置文件:mybatis-config.xml

    2.1 在配置文件中配置数据库连接信息;可以引入外部资源文件中的数据库连接信息。

  3. 创建实体类

  4. 创建实体类对应的 Mapper 接口,在接口中定义相关方法。

  5. 创建 Mapper 接口对于的 Mapper.xml 映射文件
    5.1 在核心配置文件 mybatis-config.xml 中绑定,或者说注册这个 Mapper.xml 映射文件。

    5.2 在映射文件中用 namespace 声明所有绑定的对应的 Mapper 接口。
    5.3 实现 Mapper 接口中定义的方法。

  6. 开始使用
    6.1 读取核心配置文件 Mapper.xml ,生成相应的 SqlSessionFactory;
    6.2 使用这个SqlSessionFactory 生成相应的 SqlSession 。
    6.2 使用这个 SqlSession 去调用接口中的定义的方法。
    (其中 6.1、6.2 可以封装成一个 Utils,方便多次使用。)

并且知道了,MyBatis 不止可以通过映射文件来开发还可以通过注解来开发

不过复杂的 SQL 语句用注解开发不方便,还得同 Mapper.xml 映射文件来开发。

然后又知道了 resultMap——结果集映射

其中

  1. 简单的字段,可以用 resutlt 直接映射。
  2. 复杂类型的字段,要用 association 来映射。
  3. 复杂类型集合的字段,要用 collection 来映射。

这里面,顺便还知道了复杂查询的两种解决方法:

  1. 查询结果映射:先查询出所有结果;简单字段直接映射;复杂字段,先映射成对应的对象再整个映射。
  2. 嵌套查询:其实就是分步查询,或者说嵌套子查询。

然后还知道了怎么开启日志,来查看具体的执行信息。

再然后,知道了动态SQL,简单来说也就是根据传入的参数的不同,动态生成不同的 SQL 。

  1. if
  2. choose
  3. foreach

最后的最后还了解一下MyBatis的缓存

  1. 一级缓存 :本地会话缓存。
  2. 二级缓存:全局缓存。

至此,MyBatis 大部分内容应该都说到了。

如果有没有提到的内容,而项目又需要的话,可以去官方文档查阅:中文官方文档

最后的最后还要再说一句,真正想要精通 MyBatis ,还得要多去使用多去思考,在不断使用中和思考中,才能学会更多!

点个关注,不再迷路

不要明知会后悔却还不去努力!

这里是猿兄,为你分享程序员的世界。

非常感谢各位**优秀的程序员**们能看到这里,如果觉得文章还不错的话,求点赞???? 求关注???? 求分享????对我来说真的 非常有用!!!

持续更新文章,欢迎微信搜索关注: 猿兄 第一时间获取最新文章。

关注后回复【电子书】,有我为大家准备的各类技术书籍。

最后

以上就是香蕉咖啡豆为你收集整理的万字 MyBatis 带你从入门到精通!!!MyBatis的全部内容,希望文章能够帮你解决万字 MyBatis 带你从入门到精通!!!MyBatis所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(56)

评论列表共有 0 条评论

立即
投稿
返回
顶部