本文是SpringBoot第25讲,上文主要介绍了Spring集成MyBatis访问MySQL,采用的是XML配置方式;我们知道除了XML配置方式,MyBatis还支持注解方式。本文主要介绍SpringBoot+MyBatis注解方式。
MyBatis的相关知识体系。
具体可以参考 SpringBoot第24讲:SpringBoot集成MySQL - MyBatis XML方式
在构建知识体系时:我们最重要的目标并不是如何使用注解方式,而是要理解:
我们从最基本的增删改操作开始,对比xml方式进行理解。
对于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")
}
)
对于查询,用@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);
xml配置查询时定义的ResultMap是可以复用的,那么我们上面通过@Results定义在某个方法上的,如何复用呢?
比如查询所有用户返回用户实体@Results是和查询单个用户一致的,那么我们可以通过@ResultMap指定返回值对应关系
@ResultMap("UserResult1")
@Select(SELECT_USER_SQL)
User findAll1();
由此你可以猜到,@ResultMap定义在哪个方法上并没有什么关系,因为它会被优先通过注解解析为数据库字段与Java字段的映射关系。
用户和角色存在着一对多的关系,上面的查询只是查询了用户的基本信息,如何关联查询(查询用户同时返回角色信息)呢?
我们看下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注解。
涉及插入操作的主要注解有:@Insert, @SelectKey等。
对于插入操作,在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 +
@Insert({""})
@Options(useGeneratedKeys = true, keyProperty = "id")
int save(User user);
上述
@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数据库,使用其他类型的数据库时要注意修改。
涉及更新操作的主要注解有:@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);
涉及删除操作的主要注解有:@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);
其实你可以发现通过注解方式,对于有一些需要通过动态构建查询条件的操作是非常不方便的。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);
让我们通过几个问题,进一步理解MyBatis注解方式。
@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()
。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();
纯注解方式为何很少大规模呢? 说说我的一些看法
, 代码的阅读体验和维护极为不佳;这也是mybatis-plus等工具改进的地方。
todo