# 前言

最初设计时,MyBatis 是一个 XML 驱动的框架。配置信息是基于 XML 的,而且映射语句也是定义在 XML 中的。而到了 MyBatis 3,有新的可用的选择了。MyBatis3 构建在基于全面而且强大的 Java 配置 API 之上。这个配置 API 是基于 XML 的 MyBatis 配置的基础,也是新的基于注解配置的基础。注解提供了一种简单的方式来实现简单映射语句,而不会引入大量的开销。

注意:不幸的是,Java 注解限制了它们的表现和灵活。最强大的 MyBatis 映射不能用注解来构建,那并不可笑,基于 Java 注解的配置离不开它的特性。

# 注解开发

之前需要编写对应的映射器并绑定到接口上,通过接口执行 sql 语句。注解开发实现无需 xml 映射器配置,直接使用注解在接口上进行配置。

在前言已经说过了,注解的表达能力和灵活性有限,并不是说完全抛弃 xml 配置。

复习一下接口映射:

之前使用 xml 需要编写映射器:在 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">
<!-- 映射 StudentMapper 接口,namespace 是接口的全类限定名 -->
<mapper namespace="com.cyan.mappers.StudentMapper">
    <select id="selectStudent" resultType="com.cyan.entity.Student">
        select * from student
    </select>
</mapper>

当然,使用全类限定名太麻烦,可以再 xml 中加上别名

<typeAliases>
	<package name="com.cyan.entity" />
    <package name="com.cyan.mappers" />
</typeAliases>

现在使用注解实现:

@Insert("insert into student(sid, name) value(#{sid}, #{name})")
int addStudent(Student s);

同时修改一下配置文件的映射器注册:

<mappers>
    <mapper class="com.cyan.mapper.StudentMapper"/>
</mappers>

使用的时候,还是直接向 SqlSession 传入接口 Class 即可:

StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);

# 自定义映射

通过 @Result 进行自定义映射。

@Results({
        @Result(id = true, column = "sid", property = "sid"),
        @Result(column = "name", property = "name")
})
@Select("select * from student")
List<Student> getAllStudent();

左边的 column 其实就是查询结果组成的表的一列,所以我们为 Student 再加一个属性 sex ,我想让性别的结果映射到 Student 对象的 name 属性上,让 name 结果映射为 sex。

@Results({
        @Result(id = true, column = "sid", property = "sid"),
        @Result(column = "sex", property = "name"),
        @Result(column = "name", property = "sex")
})
@Select("select * from student")
List<Student> getAllStudent();

我知道这种映射没有卵用,我只是举个🌰。

# 复杂查询

上一篇讲了一对多的复杂查询,teacher 对象有一个学生列表的属性,这里用注解实现:

@Select("select * from student inner join teach on student.sid = teach.sid where tid = #{tid}")
List<Student> getStudentByTid(int tid);
@Reuslt({
    @Result(id = true, column = "tid", property = "tid"),
    @Result(column = "name", property = "name"),
    @Result(column = "tid", property = "studentList", many = 
           @Many(select = "getStudentByTid"))
})
@Select("Select * from teacher where tid = #{tid}")
Teacher getTeacherByTid(int tid);

子查询结果作为 @Result 注解的一个 many 结果,代表子查询的所有结果都归入此集合中(也就是之前的 collection 标签)。

@Result 也提供了 @One 子注解来实现一对一的关系表示,类似于之前的 assocation 标签:

@Results({
        @Result(id = true, column = "sid", property = "sid"),
        @Result(column = "sex", property = "name"),
        @Result(column = "name", property = "sex"),
        @Result(column = "sid", property = "teacher", one =
            @One(select = "getTeacherBySid")
        )
})
@Select("select * from student")
List<Student> getAllStudent();

如果觉得在 Java 代码中编写映射规则不美观,可以使用 @ResultMap 标签,指定 xml 文件里的 resultMap 的 ID,就可以在该 ResultMap 编写映射规则了。

# 指定构造器与参数

使用 @ConstructorArgs 参数来指定构造器

@ConstructorArgs({
        @Arg(column = "sid", javaType = int.class),
        @Arg(column = "name", javaType = String.class)
})
@Select("select * from student where sid = #{sid} and sex = #{sex}")
Student getStudentBySidAndSex(@Param("sid") int sid, @Param("sex") String sex);

参数列表中,参数个数超过两个时就必须要使用 @Param 来指定参数名称,不然 Mybatis 不明确哪个参数是什么。

如果传入的是一个对象,就需要在 sql 语句中指定属性来自哪:

@Insert("insert into student(sid, name, sex) values(#{sid}, #{student.name}, #{student.sex})")
int addStudent(@Param("sid") int sid, @Param("student")  Student student);

上文提到缓存机制,在注解开发中就需要使用:

@CacheNamespace(readWrite = false)
public interface MyMapper {
    
    @Select("select * from student")
    @Options(useCache = false)
    List<Student> getAllStudent();
}

使用 @CacheNamespace 注解直接定义在接口上即可,然后我们可以通过使用 @Options 来控制单个操作的缓存启用。

# 参考

https://www.hxstrive.com/subject/mybatis/196.htm

https://www.yuque.com/qingkongxiaguang/javaweb/gn0syt#815f8cec