MyBatis mapper

1. 前言

本小节,我们将一起学习 MyBatis mapper。

上一节中我们以 JDBC 的方式使用了 MyBatis,但在实际应用中是不会选择这种方式来执行 SQL 的,MyBatis提供了 mapper 这种优雅且易维护的方式来帮助我们更好地去使用 SQL。

2. 定义

慕课解释:mapper 是 Java 方法和 SQL 语句之间的桥梁

Java 接口方法与 SQL 语句以及 mapper 之间的关系如下图所示:
图片描述

3. 新建 mapper

mapper 只是一个抽象的概念,它其实就是 Java 里面的一个接口类,我们不需要实现这个接口类,MyBatis 会通过动态代理自动帮我们执行接口方法所对应的 SQL 语句。

接下来,我们以UserMapper为例,来看一看 mapper 究竟是如何定义和组成的。

首先,在 com.imooc.mybatis 包下新建 mapper包,并在 mapper 包下新建接口类UserMapper.java。如下:

package com.imooc.mybatis.mapper;

public interface UserMapper {
}

MyBatis 提供了注解XML两种方式来连接接口方法和 SQL 语句。

3.1 注解方式

我们为 UserMapper 添加一个方法selectUsernameById,该方法的作用为通过用户 id 查询用户名称,如下:

package com.imooc.mybatis.mapper;

public interface UserMapper {
  String selectUsernameById(Integer id);
}

selectUsernameById 方法接受 id 参数(用户 id),返回用户名称(String 类型)。

有了方法定义后,我们再通过注解为该方法添加上对应的 SQL 语句:

package com.imooc.mybatis.mapper;

import org.apache.ibatis.annotations.Select;

public interface UserMapper {
  @Select("SELECT username FROM imooc_user WHERE id = #{id}")
  String selectUsernameById(Integer id);
}

Select注解对应 SQL 的 select 查询,注解中的语句就是相应的 SQL 语句,当然这并非真实的 SQL 语句,具体的差异性我们后续章节再说。

3.2 XML 方式

XML 方式是更加强大和易用的一种方式,虽然它没有注解那么方便,但是功能更强、更易维护,是 MyBatis 官方推荐的一种方式。

在 mapper 包中,我们新建另一个文件UserMapper.xml,并添加如下内容:

<?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="com.imooc.mybatis.mapper.UserMapper">
</mapper>

mapper 标签对应一个 mapper 接口类,这里应该对应 UserMapper,所以在 mapper 标签里面我们还需要加上 namespace 这个属性,它的值为 UserMapper 的类全路径,这样 UserMapper.xml 配置文件就与 UserMapper.java 对应起来了。

提示namespace 命名空间是每一个 mapper 文件所独有的,它唯一标识着一个 mapper

注意: 在这里,.xml 配置文件必须与其对应的接口在同一个包内。

二者在目录中的位置如下:

src/main/java/com/imooc/mybatis/mapper
├── UserMapper.java
└── UserMapper.xml

在 UserMapper 接口中,我们再新增一个方法selectUserAgeById,该方法的作用是通过用户 id 查询用户年龄。如下:

package com.imooc.mybatis.mapper;

import org.apache.ibatis.annotations.Select;

public interface UserMapper {
  @Select("SELECT username FROM imooc_user WHERE id = #{id}")
  String selectUsernameById(Integer id);

  Integer selectUserAgeById(Integer id);
}

与之对应的 xml 文件中,我们也需要添加上对应的 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="com.imooc.mybatis.mapper.UserMapper">
  <select id="selectUserAgeById" resultType="java.lang.Integer">
    SELECT age FROM imooc_user WHERE id = #{id}
  </select>
</mapper>

在 mapper 标签中,我们新增了 select 标签,对应 SQL 中的 select 查询;select 标签中有两个必填属性,第一个是 id ,它对应接口的方法名,即 selectUserAgeById,通过它 MyBatis 才能将二者对应起来,第二个是 resultType,它对应 SQL 语句的返回类型,与接口方法的返回值相同,为 Integer 类型。

好了,注解和 XML 的两种方式的简单使用已经介绍完毕了,这里仍然有一个可以完善的点,我们可以为 UserMapper 类打上一个 Mapper注解,虽然这个注解并不是必须的,但是增强了代码的可读性。如下:

// 省略
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper {
  // 省略其它诸多代码
}

4. 使用 mapper

mapper 定义好以后,我们接下来会介绍如何使用它。在上一节中,我们介绍了 MyBatis 配置式的简单使用,那么使用 mapper 其实很简单,只需在配置式使用的基础上增加几处配置和代码就行了。

4.1 mapper 配置

首先,我们需要在 mybatis-config.xml 配置文件中添加上对应的 mapper 配置:

<?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>
  <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC"/>
      <dataSource type="POOLED">
        <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/imooc?useSSL=false"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
      </dataSource>
    </environment>
  </environments>
  <!--  mapper 对应的配置  -->
  <mappers>
    <mapper class="com.imooc.mybatis.mapper.UserMapper"/>
  </mappers>
</configuration>

注意,mapper 可以有多个,对应的标签项应是 mappers,在 mappers 下面才有一个个的 mapper,如上面的 UserMapper;通过mapper 标签中的 class 属性,我们指定其对应的接口类,class 属性值为 UserMapper 的类全路径。

4.2 代码调用

有了配置以后,我们则可以在代码中调用 mapper 方法从而执行 SQL 得到结果了。在 pattern 包下,我们新建一个文件,名为StartWithMapper.java,并向其中添加如下代码:

package com.imooc.mybatis.pattern;

import com.imooc.mybatis.mapper.UserMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;

@SuppressWarnings({"Duplicates"})
public class StartWithMapper {
  public static void main(String[] args) throws IOException, SQLException {
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession session = sqlSessionFactory.openSession();
    // 得到 mapper
    UserMapper mapper = session.getMapper(UserMapper.class);
    // 调用注解的SQL
    String username = mapper.selectUsernameById(1);
    System.out.println("username: " + username);
    // 调用XML的SQL
    Integer age = mapper.selectUserAgeById(1);
    System.out.println("age: " + age);
    // 关闭会话
    session.close();
  }
}

使用流程我们已经写在了代码注释中了,请务必阅读一下。

与上一节的区别在于,我们不再通过 session 得到连接从而执行 SQL 了,而是在 session 中得到配置好的 UserMapper,再通过调用UserMapper 的方法执行 SQL 从而得到结果。

执行这段代码,它会在控制台上打印如下信息(截取了部分重要信息):

13:13:50.104 [main] DEBUG com.imooc.mybatis.mapper.UserMapper.selectUsernameById - ==>  Preparing: SELECT username FROM imooc_user WHERE id = ? 
13:13:50.205 [main] DEBUG com.imooc.mybatis.mapper.UserMapper.selectUsernameById - ==> Parameters: 1(Integer)
13:13:50.344 [main] DEBUG com.imooc.mybatis.mapper.UserMapper.selectUsernameById - <==      Total: 1
username: peter
13:13:50.351 [main] DEBUG com.imooc.mybatis.mapper.UserMapper.selectUserAgeById - ==>  Preparing: SELECT age FROM imooc_user WHERE id = ? 
13:13:50.351 [main] DEBUG com.imooc.mybatis.mapper.UserMapper.selectUserAgeById - ==> Parameters: 1(Integer)
13:13:50.354 [main] DEBUG com.imooc.mybatis.mapper.UserMapper.selectUserAgeById - <==      Total: 1
age: 18

4.3 总结

从程序输出的信息中可以看出,UserMapper 的调用都成功了, 结合上一小节中 JDBC 的使用,可以清晰地感受到 MyBatis 完美的将 Java 对象与 SQL 语句分离,并通过 mapper 充当二者的桥梁,极大的提升了代码和 SQL 语句的维护性。

不同于原生 JDBC 的使用方式,MyBatis 会自动的通过 resultType 等配置来帮我们实现数据库类型到 Java 类型的转换,帮我们节省了大量的工作,当然 MyBatis 的功能远不止如此,我们将在后续的小节中一一揭晓。

5. 小结

  • mapper 是 MyBatis 的核心概念,是 MyBatis 解耦 Java 对象与 SQL 语句的桥梁。
  • MyBatis 官方文档中明确强调注解方式 SQL 无法发挥 MyBatis 的全部功能,但是可以方便地演示一些 demo。
  • 如果单独使用 MyBatis,那么 mapper 接口必须和 .xml 配置文件在同一个包中,但是如果使用 spring 等工具就可以不必受此限制。