MyBatis系列(五):MyBatis 注解方式的基本用法

1. @Select 注解

1.1 使用 Sql 语句设置别名方式

假设现在有个需求:根据 id 查询角色信息。使用注解方式该如何实现呢?

首先,在接口 SysRoleMappper 中添加如下方法:

@Select({"SELECT id,role_name roleName,enabled,create_by createBy,create_time createTime","FROM sys_role","WHERE id = #{id}"})
SysRole selectById(Long id);

上面的代码也可以写成如下格式:

@Select({"SELECT id,role_name roleName,enabled,create_by createBy,create_time createTime FROM sys_role WHERE id = #{id}"})
SysRole selectById(Long id);

以上 2 种方式都是传递字符串数组的形式,我们还可以用直接传递字符串的形式:

@Select("SELECT id,role_name roleName,enabled,create_by createBy,create_time createTime FROM sys_role WHERE id = #{id}")
SysRole selectById(Long id);

使用注解方式同样需要考虑表字段和 Java 属性字段映射的问题,使用注解方式主要有 3 种方式来实现。

第 1 种方式是通过 Sql 语句设置别名,上面的代码就用的是这种方式。

1.2 使用 mapUnderscoreToCamelCase 配置方式

打开 src/main/resources 目录下我们之前新建的 mybatis-config.xml 文件,添加如下配置:

<settings>
    <!-- 其他配置 -->
    <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

该配置打开后,MyBatis 会按照“下划线转驼峰“规则自动映射,即将数据库列 role_name 自动转换为属性 roleName。

此时,上面的代码可以修改为:

@Select("SELECT id,role_name,enabled,create_by,create_time FROM sys_role WHERE id = #{id}")
SysRole selectById(Long id);

虽然也可以写为如下格式,但是不推荐这么使用:

@Select("SELECT * FROM sys_role WHERE id = #{id}")
SysRole selectById(Long id);

1.3 使用 resultMap 方式

在 xml 中,我们使用过 resultMap 来配置映射:

<resultMap id="sysUserMap" type="com.zwwhnly.mybatisaction.model.SysUser">
    <id property="id" column="id"/>
    <result property="userName" column="user_name"/>
    <result property="userPassword" column="user_password"/>
    <result property="userEmail" column="user_email"/>
    <result property="userInfo" column="user_info"/>
    <result property="headImg" column="head_img" jdbcType="BLOB"/>
    <result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
</resultMap>

在注解方式中,有一个对应的注解 @Results 来配置映射:

@Results({@Result(property = "id", column = "id", id = true),
        @Result(property = "roleName", column = "role_name"),
        @Result(property = "enabled", column = "enabled"),
        @Result(property = "createBy", column = "create_by"),
        @Result(property = "createTime", column = "create_time")
})
@Select("SELECT id,role_name,enabled,create_by,create_time FROM sys_role WHERE id = #{id}")
SysRole selectById2(Long id);

代码简单讲解:

1)@Results 注解对应着 xml 中的 resultMap 标签

2)@Result 对应着 xml 中的 result 标签

3)@Result(property = "id", column = "id", id = true)对应着 xml 中的<id property="id" column="id"/>

也许有人会问,我在 xml 中,为 resultMap 设置了一个 id,这样我就能复用该 resultMap 了,在注解方式中,支持吗?

带着这个疑问,让我们来试着修改下代码:

@Results(id = "roleResultMap", value = {@Result(property = "id", column = "id", id = true),
        @Result(property = "roleName", column = "role_name"),
        @Result(property = "enabled", column = "enabled"),
        @Result(property = "createBy", column = "create_by"),
        @Result(property = "createTime", column = "create_time")
})
@Select("SELECT id,role_name,enabled,create_by,create_time FROM sys_role WHERE id = #{id}")
SysRole selectById2(Long id);

结果发现代码编译错误,找不到 id 属性。

按下 Ctrl+B,发现 @Results 的源码如下:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Results {
    Result[] value() default {};
}

从源码我们可以发现,这个注解没有 id 属性,难道我们每个方法上都要加上重复的映射吗?

答案当然是否定的,不过在 MyBatis 3.3.0 及以前的版本中,注解定义的 @Results 不能共用,需要在每一个方法上都写一遍。但是从 MyBatis 3.3.1 版本开始,@Results 注解增加了一个 id 属性,设置了 id 属性后,就可以通过 id 属性引用同一个 @Results 配置了。

看过之前几篇博客的读者可能知道,我们的 MyBatis 刚好使用的是 3.3.0 版本,所以刚好不支持设置 id 属性,哈哈。

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.3.0</version>
</dependency>

修改 MyBatis 的版本为 3.3.1(如果没有设置自动导入变化的话,需要手动点下 Import Changes):

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.3.1</version>
</dependency>

这时会发现,上面原本编译报错的代码可以编译通过了。

此时 @Results 的源码如下,相比于之前的代码,增加了 id 属性:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Results {
    String id() default "";
Result[] value() <span class="hljs-keyword">default</span> {};

}

那如何引用这个 @Results 注解呢?请看如下代码:

@ResultMap("roleResultMap")
@Select("SELECT * FROM sys_role")
List<SysRole> selectAll();

说明:当配合 XML 方式使用的时候,这里引用的 id 值还可以是 XML 中 resultMap 元素的 id 属性值。

为了使篇幅不至于过长,这里不再贴出这 3 个方法的单元测试代码和输出日志,相信看过前几篇博客的读者已经可以自己写出单元测试代码了,也可以参考文末的源码地址下载下源码。

2. @Insert 注解

2.1 不需要返回主键

和 XML 中的使用方式几乎一样,代码如下:

@Insert({"INSERT INTO sys_role(id, role_name, enabled, create_by, create_time)",
        "VALUES (#{id},#{roleName},#{enabled},#{createBy},#{createTime,jdbcType=TIMESTAMP})"})
int insert(SysRole sysRole);

2.2 返回自增主键

如果需要返回数据库的自增主键,代码如下:

@Insert({"INSERT INTO sys_role(role_name, enabled, create_by, create_time)",
        "VALUES (#{roleName},#{enabled},#{createBy},#{createTime,jdbcType=TIMESTAMP})"})
@Options(useGeneratedKeys = true, keyProperty = "id")
int insertUseGeneratedKeys(SysRole sysRole);

和 XML 中的使用方式差不多,@Options(useGeneratedKeys = true, keyProperty = "id")等价于 XML 中的useGeneratedKeys="true" keyProperty="id"

2.3 返回非自增主键

在之前的博客中,我们知道 selectKey 既支持主键自增的数据库,比如 MySql,也支持主键不自增的数据库,如 Oracle,在 XML 中的写法是这样的:

<selectKey keyColumn="id" resultType="long" keyProperty="id" order="AFTER">
    SELECT LAST_INSERT_ID()
</selectKey>

那么使用注解方式该如何实现呢?代码如下所示:

@Insert({"INSERT INTO sys_role(role_name, enabled, create_by, create_time)",
        "VALUES (#{roleName},#{enabled},#{createBy},#{createTime,jdbcType=TIMESTAMP})"})
@SelectKey(statement = "SELECT LAST_INSERT_ID()", keyColumn = "id", keyProperty = "id", resultType = Long.class, before = false)
int insertUseSelectKey(SysRole sysRole);

before = false 相当于 XML 中的 order="AFTRE",这是 MySql 数据库的配置。

before = true 相当于 XML 中的 order="BEFORE",这是 Oracle 数据库的配置。

注意事项:不同的数据库 statement 的值会不同,上面中的值适用于 MySql 数据库,使用其他类型的数据库时要注意修改。

3. @Update 注解

和 XML 中的使用方式几乎一样,代码如下:

@Update({"UPDATE sys_role", "SET role_name = #{roleName},enabled = #{enabled},create_by=#{createBy},",
        "create_time=#{createTime,jdbcType=TIMESTAMP}", "WHERE id=#{id}"})
int updateById(SysRole sysRole);

4. @Delete 注解

和 XML 中的使用方式几乎一样,代码如下:

@Delete("DELETE FROM sys_role WHERE id = #{id}")
int deleteById(Long id);

5. Provider 注解

MyBatis 提供了 4 种 Provider 注解,分别是 @SelectProvider、@InsertProvider、@UpdateProvider 和 @DeleteProvider。

我们以 @SelectProvider 为例了解下 Provider 注解的使用方法。

首先在 com.zwwhnly.mybatisaction.mapper 包下新建如下类:

package com.zwwhnly.mybatisaction.mapper;

import org.apache.ibatis.jdbc.SQL;

public class SysPrivilegeProvider {
public String selectById(final Long id) {
return new SQL() {
{
SELECT("id,privilege_name,privilege_url");
FROM("sys_privilege");
WHERE("id = #{id}");
}
}.toString();
}
}

以上代码也可以写成如下方式:

public String selectById(final Long id) {
    return "SELECT id,privilege_name,privilege_url FROM sys_privilege WHERE id = #{id}";
}

然后在接口 SysPrivilegeProvider 中添加如下方法:

@SelectProvider(type = SysPrivilegeProvider.class, method = "selectById")
SysPrivilege selectById(Long id);

最后在 src/test/java 下的 com.zwwhnly.mybatisaction.mapper 包下新建测试类 SysPrivilegeMapperTest:

package com.zwwhnly.mybatisaction.mapper;

import com.zwwhnly.mybatisaction.model.SysPrivilege;
import org.apache.ibatis.session.SqlSession;
import org.junit.Assert;
import org.junit.Test;

public class SysPrivilegeMapperTest extends BaseMapperTest {
@Test
public void testSelectById() {
SqlSession sqlSession = getSqlSession();

    <span class="hljs-keyword">try</span> {
        <span class="hljs-type">SysPrivilegeMapper</span> <span class="hljs-variable">sysPrivilegeMapper</span> <span class="hljs-operator">=</span> sqlSession.getMapper(SysPrivilegeMapper.class);
        <span class="hljs-type">SysPrivilege</span> <span class="hljs-variable">sysPrivilege</span> <span class="hljs-operator">=</span> sysPrivilegeMapper.selectById(<span class="hljs-number">1L</span>);

        Assert.assertNotNull(sysPrivilege);
        Assert.assertEquals(<span class="hljs-string">"用户管理"</span>, sysPrivilege.getPrivilegeName());
    } <span class="hljs-keyword">finally</span> {
        sqlSession.close();
    }
}

}

运行测试代码,测试通过,输出日志如下:

DEBUG [main] - ==> Preparing: SELECT id,privilege_name,privilege_url FROM sys_privilege WHERE (id = ?)
DEBUG [main] - ==> Parameters: 1(Long)

TRACE [main] - <== Columns: id, privilege_name, privilege_url

TRACE [main] - <== Row: 1, 用户管理, /users

DEBUG [main] - <== Total: 1

6. 源码及参考

源码地址:https://github.com/zwwhnly/mybatis-action.git,欢迎下载。

刘增辉《MyBatis 从入门到精通》