SpringBoot第25讲:SpringBoot集成MySQL - MyBatis 注解方式

SpringBoot第25讲:SpringBoot集成MySQL - MyBatis 注解方式

本文是SpringBoot第25讲,上文主要介绍了Spring集成MyBatis访问MySQL,采用的是XML配置方式;我们知道除了XML配置方式,MyBatis还支持注解方式。本文主要介绍SpringBoot+MyBatis注解方式。

文章目录

  • SpringBoot第25讲:SpringBoot集成MySQL - MyBatis 注解方式
    • 1、准备知识
    • 2、基本查改删操作
      • 2.1、查询操作
        • 1、@Results和@Result注解
        • 2、@Select和@Param注解
        • 3、@ResultMap注解
        • 4、表关联查询
      • 2.2、插入操作
        • 1、@Insert注解
        • 2、返回Insert后实体的主键值
      • 2.3、更新操作
        • 1、@Update 注解
      • 2.4、删除操作
        • 1、@Delete 注解
    • 3、Provider注解
    • 4、进一步理解
      • 4.1、其它注解
      • 4.2、xml方式和注解方式融合
      • 4.3、为什么纯注解方式不是最佳选择?
    • 5、示例源码

1、准备知识

MyBatis的相关知识体系。

具体可以参考 SpringBoot第24讲:SpringBoot集成MySQL - MyBatis XML方式

在构建知识体系时:我们最重要的目标并不是如何使用注解方式,而是要理解:

  1. 对于有原有xml方式改为注解方式(一定要有对比),如何写?
    1. 基本的CRUD怎么用注解写?
    2. 对于复杂的动态SQL如何写?
    3. 对于表关联的如何写?
  2. 为什么xml方式依然是比注解方式使用广泛?
    1. xml方式和注解方式混合使用?
  3. 注解方式是如何工作的呢?

2、基本查改删操作

我们从最基本的增删改操作开始,对比xml方式进行理解。

2.1、查询操作

1、@Results和@Result注解

对于xml配置查询时定义的ResultMap, 在注解中如何定义呢?

<resultMap type="springboot.mysql.mybatis.entity.User" id="UserResult1">
  <id     property="id"       	column="id"      		/>
  <result property="userName"     column="user_name"    	/>
  <result property="password"     column="password"    	/>
  <result property="email"        column="email"        	/>
  <result property="phoneNumber"  column="phone_number"  	/>
  <result property="description"  column="description"  	/>
  <result property="createTime"   column="create_time"  	/>
  <result property="updateTime"   column="update_time"  	/>
resultMap>

使用注解方式,用@Results注解对应

@Results(
        id = "UserResult1",
        value = {
                @Result(id = true, property = "id", column = "id"),
                @Result(property = "userName", column = "user_name"),
                @Result(property = "password", column = "password"),
                @Result(property = "email", column = "email"),
                @Result(property = "phoneNumber", column = "phone_number"),
                @Result(property = "description", column = "description"),
                @Result(property = "createTime", column = "create_time"),
                @Result(property = "updateTime", column = "update_time")
        }
)

2、@Select和@Param注解

对于查询,用@Select注解;对于参数, 使用@Param注解

所以根据用户ID查询用户,使用注解方式写法如下:

String SELECT_USER_SQL = "select u.id, u.password, u.user_name, u.email, u.phone_number, u.description, u.create_time, u.update_time from tb_user u";

@Results(
        id = "UserResult1",
        value = {
                @Result(id = true, property = "id", column = "id"),
                @Result(property = "userName", column = "user_name"),
                @Result(property = "password", column = "password"),
                @Result(property = "email", column = "email"),
                @Result(property = "phoneNumber", column = "phone_number"),
                @Result(property = "description", column = "description"),
                @Result(property = "createTime", column = "create_time"),
                @Result(property = "updateTime", column = "update_time")
        }
)
@Select({SELECT_USER_SQL, " where id = #{id}"})
User findById1(@Param("id") Long id);

3、@ResultMap注解

xml配置查询时定义的ResultMap是可以复用的,那么我们上面通过@Results定义在某个方法上的,如何复用呢?

比如查询所有用户返回用户实体@Results是和查询单个用户一致的,那么我们可以通过@ResultMap指定返回值对应关系

@ResultMap("UserResult1")
@Select(SELECT_USER_SQL)
User findAll1();

由此你可以猜到,@ResultMap定义在哪个方法上并没有什么关系,因为它会被优先通过注解解析为数据库字段与Java字段的映射关系。

4、表关联查询

用户和角色存在着一对多的关系,上面的查询只是查询了用户的基本信息,如何关联查询(查询用户同时返回角色信息)呢?

我们看下xml配置方式是如何做到的?

<resultMap type="springboot.mysql.mybatis.entity.User" id="UserResult">
  <id     property="id"       	column="id"      		/>
  <result property="userName"     column="user_name"    	/>
  <result property="password"     column="password"    	/>
  <result property="email"        column="email"        	/>
  <result property="phoneNumber"  column="phone_number"  	/>
  <result property="description"  column="description"  	/>
  <result property="createTime"   column="create_time"  	/>
  <result property="updateTime"   column="update_time"  	/>
  <collection property="roles" ofType="springboot.mysql.mybatis.entity.Role">
    <result property="id" column="id"  />
    <result property="name" column="name"  />
    <result property="roleKey" column="role_key"  />
    <result property="description" column="description"  />
    <result property="createTime"   column="create_time"  	/>
    <result property="updateTime"   column="update_time"  	/>
  collection>
resultMap>

使用注解方式, 可以通过@Results+@Many注解

@Results(
        id = "UserResult",
        value = {
                @Result(id = true, property = "id", column = "id"),
                @Result(property = "userName", column = "user_name"),
                @Result(property = "password", column = "password"),
                @Result(property = "email", column = "email"),
                @Result(property = "phoneNumber", column = "phone_number"),
                @Result(property = "description", column = "description"),
                @Result(property = "createTime", column = "create_time"),
                @Result(property = "updateTime", column = "update_time"),
                @Result(property = "roles", column = "id", many = @Many(select = "springboot.mysql.mybatis.anno.dao.IRoleDao.findRoleByUserId", fetchType = FetchType.EAGER))
        }
)

其中findRoleByUserId是通过user表中的id查找Role, 具体方法如下

@Results(
            id = "RoleResult",
            value = {
                    @Result(id = true, property = "id", column = "id"),
                    @Result(property = "name", column = "name"),
                    @Result(property = "roleKey", column = "role_key"),
                    @Result(property = "description", column = "description"),
                    @Result(property = "createTime", column = "create_time"),
                    @Result(property = "updateTime", column = "update_time")
            }
    )
    @Select("select r.id, r.name, r.role_key, r.description, r.create_time, r.update_time from tb_role r, tb_user_role ur where r.id = ur.user_id and ur.user_id = #{userId}")
    List<Role> findRoleByUserId(Long userId);

对于一对一的可以使用@One注解。

2.2、插入操作

涉及插入操作的主要注解有:@Insert, @SelectKey等。

1、@Insert注解

对于插入操作,在xml配置可以定义为:

<insert id="save" parameterType="springboot.mysql.mybatis.xml.entity.User" useGeneratedKeys="true" keyProperty="id">
 		insert into tb_user(
 			<if test="userName != null and userName != ''">user_name,if>
			<if test="password != null and password != ''">password,if>
 			<if test="email != null and email != ''">email,if>
			<if test="phoneNumber != null and phoneNumber != ''">phone_number,if>
 			<if test="description != null and description != ''">description,if>
 			create_time,
			update_time
 		)values(
 			<if test="userName != null and userName != ''">#{userName},if>
			<if test="password != null and password != ''">#{password},if>
 			<if test="email != null and email != ''">#{email},if>
 			<if test="phoneNumber != null and phoneNumber != ''">#{phoneNumber},if>
 			<if test="description != null and description != ''">#{description},if>
 			sysdate(),
			sysdate()
 		)
insert>

特别是,这里通过判断条件更新的情况应该如何在注解中写呢?

可以通过@Insert + "}) @Options(useGeneratedKeys = true, keyProperty = "id") int save(User user);

2、返回Insert后实体的主键值

上述@Options(useGeneratedKeys = true, keyProperty = "id") 表示什么意思呢?

表示,如果数据库提供了自增列生成Key的方式(比如这里的id),并且需要返回自增主键时,可以通过这种方式返回实体。

那么,如果id的自增不使用数据库自增主键时, 在xml中可以使用SelectKey:

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

对应着注解:

@SelectKey(statement = "SELECT LAST_INSERT_ID()", keyColumn = "id", keyProperty = "id", resultType = Long.class, before = false)
  • before = false, 相当于XML中的order=“AFTRE”,这是MySql数据库的配置。
  • before = true, 相当于XML中的order=“BEFORE”,这是Oracle数据库的配置。

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

2.3、更新操作

涉及更新操作的主要注解有:@Update等。

1、@Update 注解

对于xml的更新操作如下:

<update id="update" parameterType="springboot.mysql.mybatis.xml.entity.User">
  update tb_user
  <set>
    <if test="userName != null and userName != ''">user_name = #{userName},if>
    <if test="email != null and email != ''">email = #{email},if>
    <if test="phoneNumber != null and phoneNumber != ''">phone_number = #{phoneNumber},if>
    <if test="description != null and description != ''">description = #{description},if>
    update_time = sysdate()
  set>
  where id = #{id}
update>

<update id="updatePassword" parameterType="springboot.mysql.mybatis.xml.entity.User">
  update tb_user
  <set>
    password = #{password}, update_time = sysdate()
  set>
  where id = #{id}
update>

对应的注解写法如下:

@Update({"update tb_user set password = #{password}, update_time = sysdate()", " where id = #{id}"})
int updatePassword(User user);

@Update({""})
int update(User user);

2.4、删除操作

涉及删除操作的主要注解有:@Delete等。

1、@Delete 注解

对于xml的删除操作如下:

<delete id="deleteById" parameterType="Long">
  	delete from tb_user where id = #{id}
delete>

<delete id="deleteByIds" parameterType="Long">
  	delete from tb_user where id in
  	<foreach collection="array" item="id" open="(" separator="," close=")">
    		#{id}
  	foreach>
delete>

对应的注解写法如下:

@Delete("delete from tb_user where id = #{id}")
int deleteById(Long id);

@Delete({""})
int deleteByIds(Long[] ids);

3、Provider注解

其实你可以发现通过注解方式,对于有一些需要通过动态构建查询条件的操作是非常不方便的。MyBatis的作者们自然就想到了动态构建SQL,动态构建SQL的方式是配合@Provider注解来完成的。

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

这里以@SelectProvider为例来根据Id查询User:

1、定义包含自定义生成的动态SQL的类,比如UserDaoProvider

/**
 * @author qiwenjie
 */
public class UserDaoProvider {

    public String findById(final Long id) {
        SQL sql = new SQL();
        sql.SELECT("u.id, u.password, u.user_name, u.email, u.phone_number, u.description, u.create_time, u.update_time");
        sql.FROM("tb_user u");
        sql.WHERE("id = " + id);
        return sql.toString();
    }
}

2、通过@SelectProvider注解关联到定义的类和方法

@ResultMap("UserResult")
@SelectProvider(type = UserDaoProvider.class, method = "findById")
User findById2(Long id);

4、进一步理解

让我们通过几个问题,进一步理解MyBatis注解方式。

4.1、其它注解

  • @CacheNamespace :为给定的命名空间 (比如类) 配置缓存。对应xml中的
  • @CacheNamespaceRef :参照另外一个命名空间的缓存来使用。属性:value,应该是一个名空间的字 符串值(也就是类的完全限定名) 。对应xml中的标签。
  • @ConstructorArgs :收集一组结果传递给一个劫夺对象的构造方法。属性:value,是形式参数 的数组。
  • @Arg :单独的构造方法参数 , 是 ConstructorArgs 集合的一部分。属性: id,column,javaType,typeHandler。id 属性是布尔值, 来标识用于比较的属性,和XML元素相似。对应xml中的标签。
  • @Case :单独实例的值和它对应的映射。属性: value,type,results。Results 属性是结果数组,因此这个注解和实际的 ResultMap 很相似,由下面的 Results 注解指定。对应xml中标签
  • @TypeDiscriminator : 一组实例值被用来决定结果映射的表 现。 属性: column, javaType, jdbcType, typeHandler,cases。cases 属性就是实例的数组。对应xml中标签
  • @Flush: 在MyBatis 3.3以上版本,可以通过此注解在Mapper接口中调用SqlSession#flushStatements()

4.2、xml方式和注解方式融合

xml方式和注解方式是可以融合写的, 我们可以将复杂的SQL写在xml中

比如将resultMap定义在xml中

<resultMap type="springboot.mysql.mybatis.xml.entity.User" id="UserResult3">
  <id     property="id"       		column="id"      		/>
  <result property="userName"     column="user_name"    	/>
  <result property="password"     column="password"    	/>
  <result property="email"        column="email"        	/>
  <result property="phoneNumber"  column="phone_number"  	/>
  <result property="description"  column="description"  	/>
  <result property="createTime"   column="create_time"  	/>
  <result property="updateTime"   column="update_time"  	/>
  <collection property="roles" ofType="springboot.mysql.mybatis.xml.entity.Role">
    <result property="id" 					column="id"  />
    <result property="name" 				column="name"  />
    <result property="roleKey" 			column="role_key"  />
    <result property="description" 	column="description"  />
    <result property="createTime"   column="create_time"  	/>
    <result property="updateTime"   column="update_time"  	/>
  collection>
resultMap>

在方法中用 @ResultMap

@ResultMap("UserResult3")
@Select("select u.id, u.password, u.user_name, u.email, u.phone_number, u.description, u.create_time, u.update_time from tb_user u")
User findAll1();

4.3、为什么纯注解方式不是最佳选择?

纯注解方式为何很少大规模呢? 说说我的一些看法

  • 1、对于复杂的SQL,特别是按照条件动态生成方式极为不便,即便有

你可能感兴趣的:(深入理解Spring生态,spring,boot,mysql,mybatis,注解,项目实战)