Mapper映射文件解析

166 阅读6分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第27天,点击查看活动详情

Mapper映射文件

MyBatis的重中之重就是Mapper映射文件,所有的增删改查都依靠映射文件来完成。

实现增删改查

在前面我们已经使用了MyBatis的查询功能,接下来分别实现一下数据的新增、修改和删除。

\

首先在EmployeeMapper接口中声明三个方法:

public interface EmployeeMapper {

    void addEmp(Employee employee);
    void updateEmp(Employee employee);
    void deleteEmpById(Integer id);
}

声明了方法后,就可以在EmployeeMapper.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.wwj.mybatis.mapper.EmployeeMapper">
    <insert id="addEmp" parameterType="com.wwj.mybatis.bean.Employee">
        insert into tbl_employee(last_name, gender, email)
        values (#{lastName}, #{gender}, #{email})
    </insert>
    <update id="updateEmp">
        update tbl_employee
        set last_name=#{lastName},
            gender=#{gender},
            email=#{email}
        where id = #{id}
    </update>
    <delete id="deleteEmpById">
        delete
        from tbl_employee
        where id = #{id}
    </delete>
</mapper>

都是一些基础操作,没什么好说的,唯一需要注意的是 parameterType 用于指定方法的参数类型,也可以不指定。

\

最后编写业务代码:

@Test
public void test() throws IOException {
    String resource = "mybatis-config.xml";
    InputStream stream = Resources.getResourceAsStream(resource);
    // 构建sqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(stream);
    // 获取sqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();
    // 获取Mapper接口的实现
    EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
    // 调用方法
    Employee employee = new Employee(null, "jack", '1', "jack@qq.com");
    employeeMapper.addEmp(employee);
    // 提交
    sqlSession.commit();
}

需要注意,通过 openSession() 得到的SqlSession对象是不会自动提交数据的,所谓在进行非查询的操作时,我们一定要调用 commit() 方法进行手动提交,当然你也可以在调用 openSession(true) 时传入 true 来得到一个能够自动提交的SqlSession对象。

\

接下来是更新操作:

Employee employee = new Employee(1, "jack", '1', "jack@qq.com");
employeeMapper.updateEmp(employee);

最后是删除操作:

employeeMapper.deleteEmpById(2);

MyBatis还支持非查询方法直接返回一些值以作为操作是否成功执行的判断条件,支持的类型如下:

  • int、Integer
  • long、Long
  • boolean、Boolean

其中int和long会返回影响的行数,boolean会返回操作是否成功(影响的行数大于0则为true,否则为false),比如:

public interface EmployeeMapper {

    int addEmp(Employee employee);
    long updateEmp(Employee employee);
    boolean deleteEmpById(Integer id);
}

获取自增主键的值

在刚才的新增操作中,因为数据表的id列是自增的,所以我们无需设置id的值,但有时候,我们需要知道新增的数据id是多少,这个时候该怎么办呢?

我们只需要在 insert 标签中设置useGeneratedKeys属性为true即可:

<insert id="addEmp" parameterType="com.wwj.mybatis.bean.Employee"
        useGeneratedKeys="true" keyProperty="id">
  insert into tbl_employee(last_name, gender, email)
  values (#{lastName}, #{gender}, #{email})
</insert>

keyProperty属性用于指定主键id需要被封装到哪个属性上,此时我们执行新增操作:

Employee employee = new Employee(null, "jack", '1', "jack@qq.com");
employeeMapper.addEmp(employee);
System.out.println(employee);

id值将会被自动封装到Employee的id属性上,结果如下:

Employee(id=3, lastName=jack, gender=1, email=jack@qq.com)

参数处理

我们发现,只要在接口上声明参数,就能够在映射文件中通过 #{} 取出参数值,那么这一过程又有哪些讲究呢?

\

对于单个参数的情况,MyBatis不会做特殊的处理,比如这样的一个接口方法:

Employee getEmpById(Integer id);

映射文件:

<select id="getEmpById" resultType="employee">
  select *
  from tbl_employee
  where id = #{id}
</select>

这里就可以通过 #{id} 取出id值,事实上,对于一个参数的情况,你可以随意编写参数名,比如 #{a} 、 #{1} 都是没有问题的。

\

若是接口方法有多个参数,比如:

Employee getEmpByIdAndLastName(Integer id,String lastName);

这个时候映射文件中的参数名就不能随便写了:

<select id="getEmpByIdAndLastName" resultType="com.wwj.mybatis.bean.Employee">
  select *
  from tbl_employee
  where id = #{param1}
  and last_name = #{param2}
</select>

对于多个参数,MyBatis会将参数封装到一个Map集合中,并以param1、param2、...作为每个参数的键,而 #{} 实质上是从Map中取出参数值,所以需要指定键为param1和param2;我们还可以通过参数值的索引进行获取:

<select id="getEmpByIdAndLastName" resultType="com.wwj.mybatis.bean.Employee">
  select *
  from tbl_employee
  where id = #{0}
  and last_name = #{1}
</select>

原理是一样的。

\

相信大家能够发现这种方式的弊端,首先不够见名知意,其次,当参数过多时,会发生顺序上的混乱,为此,我们需要使用 命名参数  。

命名参数能够自定义MyBatis在封装参数值时的键名,用法如下:

Employee getEmpByIdAndLastName(@Param("id") Integer id, @Param("lastName") String lastName);

此时我们就能够通过这两个参数名获取参数值了:

<select id="getEmpByIdAndLastName" resultType="com.wwj.mybatis.bean.Employee">
  select *
  from tbl_employee
  where id = #{id}
  and last_name = #{lastName}
</select>

\

若是接口方法中的参数是一个Bean:

Employee getEmpById(Employee employee);

则在映射文件中可以直接通过Bean中的属性名获取属性值。

\

而如果接口方法中的参数是一个Map:

Employee getEmpById(Map<String,Object> map);

则在映射文件中仍然可以直接通过Map中的键名获取键值。

\

最后需要注意,如果接口方法中的参数是List、Set或者数组,则MyBatis会将集合或者数组中的数据封装到Map中,而且键名也会特殊处理,比如:

Employee getEmpById(List<Integer> ids);

那么若是想取出集合中的第一个id值,必须这样取: #{list[0]}  ,键名定义如下:

  • Collection:Collection的子类集合都可以通过 collection 获取值
  • List:List集合可以通过 collection 或 list 获取值
  • Set:Set集合可以通过 collection 或 set 获取值
  • 数组:数组可以通过 array 获取值

\

对于参数的取值,前面我们一直使用 #{} 获取,而事实上,我们还能够通过 ${} 获取参数值,那么它俩有啥区别呢?

它俩其实没有太大区别,唯一的差异在于使用 ${} 取值能够在生成的sql语句上查看到参数的值,而使用 #{} 就只能看到占位符,所以使用 #{} 的安全性更高,若是想输出MyBatis生成的sql语句,需要在全局配置文件中配置:

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

#{} 输出的sql:

select * from tbl_employee where id = ? and last_name = ?

${} 输出的sql:

select * from tbl_employee where id = 1 and last_name = jack

select标签

在数据表的操作中,数查询操作最为频繁,所以我们来详细了解一下用于查询的select标签。

\

对于单个数据的查询结果,我们只需要指定resultType即可,倘若查询到的是多个值,该如何编写呢?

首先是接口方法:

List<Employee> getEmpsByLastNameLike(@Param("lastName") String lastName);

然后编写映射文件:

<select id="getEmpsByLastNameLike" resultType="com.wwj.mybatis.bean.Employee">
  select *
  from tbl_employee
  where last_name like #{lastName}
</select>

resultType只需要指定为集合中的元素类型即可,编写业务代码:

List<Employee> emps = employeeMapper.getEmpsByLastNameLike("%c%");
for (Employee emp : emps) {
    System.out.println(emp);
}

执行结果:

Employee(id=1, lastName=jack, gender=1, email=jack@qq.com)
Employee(id=3, lastName=jack, gender=1, email=jack@qq.com)

若是想将一条数据封装成一个Map集合,其中键为列名,值为数据值,该如何实现呢?

首先是接口方法:

Map<String,Object> getEmpByIdReturnMap(@Param("id") Integer id);

编写配置:

<select id="getEmpByIdReturnMap" resultType="java.util.Map">
  select *
  from tbl_employee
  where id = #{id}
</select>

将resultType指定为Map类型,当然也可以使用别名 map ,编写业务代码:

Map<String, Object> map = employeeMapper.getEmpByIdReturnMap(1);
System.out.println(map);

执行结果:

{gender=1, last_name=jack, id=1, email=jack@qq.com}

若是想将多条数据都封装到一个Map里,其中键为当前数据的主键,值为数据对象,则首先编写接口方法:

@MapKey("id")
Map<Integer,Employee> getEmpsByLastNameReturnMap(@Param("lastName") String lastName);

其次进行配置:

<select id="getEmpsByLastNameReturnMap" resultType="java.util.Map">
  select *
  from tbl_employee
  where last_name like #{lastName}
</select>

resultType仍然指定为Map类型,只需要在接口方法标上@MapKey("id")注解即可,意为指定Employee中的id属性为Map的键名,编写业务代码:

Map<Integer, Employee> map = employeeMapper.getEmpsByLastNameReturnMap("%c%");
System.out.println(map);

执行结果:

{1={gender=1, last_name=jack, id=1, email=jack@qq.com}, 
3={gender=1, last_name=jack, id=3, email=jack@qq.com}}