1 MyBatis注解引言
MyBatis在设计之初是基于xml配置设计的,xml配置为MyBatis提供了丰富的功能特性,较高的灵活性和可维护性。但尽管如此,xml方式也存在着一些小小的不足:开发效率低。
解决方案:注解开发
MyBatis3之后提供了另外一种配置方式:注解。
通过注解可以将接口方法和要执行的sql语句定义在一起,提高了开发效率。但需要注意:
- 注解功能比xml有所不足
- 注解在开发复杂sql时,编码过于复杂
最佳实践:简单的单表查询SQL用注解,复杂多表关联查询SQL用xml
2 简单操作(CRUD)
public interface UserMapper {
@Insert("insert into t_user values(seq_user.nextval,#{username},#{password})")
public void insertUser(User u);
@Delete("delete from t_user where user_id = #{id}")
public void deleteUserById(Integer id);
@Update("update t_user set user_name = #{username},password=#{password} where user_id = #{userId}")
public void updateUser(User u);
@Select("select * from t_user where user_id = #{id}")
//设置实体类型的类对象 等同于标签中resultType属性
@ResultType(User.class)
public User selectUserById(Integer id);
@Select("select * from t_user")
@ResultType(User.class)
public List<User> selectAllUsers();
}
<!-- 一定要在mybatis.xml中加入-->
<!--指定带有注解的dao接口所在位置,不再需要添加mapper.xml-->
<mappers>
<mapper class="com.bz.dao.UserDao"></mapper>
</mappers>
总结:
- 直接在mapper方法上使用
@Insert
、@Delete
、@Update
、@Select
注解,代替xml标签 ResultType注解
等同于select标签中的resultType属性
3 解决列名和属性名不一致问题
当查询结果和实体属性不一致时,在xml中可以使用 resultMap
标签解决。在注解开发时,应该如何解决?
3.1 复用xml中resultMap标签
可以通过 ResultMap注解
复用xml中resultMap标签
-
在mapper.xml中定义
resultMap标签
UserMapper.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.bz.mapper.UserMapper"> <!-- 定义resultMap标签--> <resultMap type="com.bz.entity.User" id="userResultMap1"> <id column="user_id" property="userId"/> <result column="username" property="username"/> <result column="password" property="password"/> </resultMap> </mapper>
-
在mapper查询方法上通过
ResultMap注解
声明使用xml中的resultMap标签
UserMapper.java
@Select("select * from t_user") // @ResultType(User.class) //ResultType注解替换为ResultMap注解,ResultMap注解等同于xml中resultMap属性 @ResultMap("userResultMap1") public List<User> selectAllUsers();
-
在mybatis.xml中扫描xml
<mappers>
<mapper class="com.bz.dao.UserDao"></mapper>
<mapper resource="UserMapper.xml"></mapper>
</mappers>
3.2 使用Results注解
Results
注解等同于xml中的resultMap标签
。可以在Mapper.java中使用Results注解
代替xml中resultMap标签
。
UserMapper.java:
@Select("select * from t_user where user_id = #{id}")
//设置实体类型的类对象 等同于标签中resultType属性
// @ResultType(User.class)
/**
<resultMap id="userResultMap1" type="com.bz.entity.User">
<id column="user_id" property="userId"/>
<result column="user_name" property="username"/>
<result column="password" property="password"/>
</resultMap>
*/
//Results注解等同于xml中resultMap标签
@Results(id = "userResultMap2",
value = {
@Result(column = "user_id",property = "userId",id=true),
@Result(column = "user_name",property = "username"),
@Result(column = "password",property = "password")
}
)
public User selectUserById(Integer id);
注意:
-
ResultMap注解
也可以跟Results注解
配合使用:ResultMap注解配置成Results注解的id中 -
ResultMap注解
和Results注解
避免同时使用,如果同时使用Results注解
将被忽略
4 表连接查询
复习xml中1:N的表连接处理:
-
表
create table t_category( category_id int primary key , category_name varchar(20) not null unique ); create table t_goods( goods_id int primary key , goods_name varchar(20) not null, category_id int, foreign key (category_id) references t_category(category_id) ); insert into t_category values (1, '图书'); insert into t_category values (2, '手机'); insert into t_goods values (101, 'Java为什么这么难',1); insert into t_goods values (102, '活着', 1); insert into t_goods values (103, 'iphone12',2); insert into t_goods values (104,'iphone13',2);
连接查询sql
//根据1查询多的一方 select c.*,g.* from t_category c left join t_goods g on c.category_id = g.category_id
-
实体
public class Category implements Serializable { private Integer categoryId; private String categoryName; private List<Goods> goodsList; //无参构造 get/set } public class Goods implements Serializable { private Integer goodsId; private String goodsName; //无参构造 get/set }
-
数据转换
<resultMap type="com.bz.entity.Category" id="categoryResultMap"> <id column="category_id" property="categoryId"/> <result column="category_name" property="categoryName"/> <collection property="goodsList" javaType="java.util.List" ofType="com.bz.entity.Goods"> <id column="goods_id" property="goodsId"/> <result column="goods_name" property="goodsName"/> </collection> </resultMap>
注解方式表连接:
-
表
和xml方式下没有区别
-
实体
和xml方式下没有区别
-
数据转换
Results注解在表连接方面没办法替换掉resultMap标签。
仍然使用resultMap标签,使用ResultMap注解复用resultMap标签。
<resultMap type="com.bz.entity.Category" id="categoryResultMap"> <id column="category_id" property="categoryId"/> <result column="category_name" property="categoryName"/> <collection property="goodsList" javaType="java.util.List" ofType="com.bz.entity.Goods"> <id column="goods_id" property="goodsId"/> <result column="goods_name" property="goodsName"/> </collection> </resultMap>
接口中:
@ResultMap("categoryResultMap") //进行表连接查询 @Select("select c.*,g.* from t_category c left join t_goods g on c.category_id = g.category_id") public List<Category> selectAllCategory();
5 注解中的动态SQL
需求:根据学员姓名、报备老师姓名、口碑奖励查询学员,如果某部分条件没有值,则不再根据该条件查询。
java接口:
List<RecommendStudent> selectConditionStudents1(@Param("studentName")String studentName,@Param("teacherName")String teacherName,@Param("money")Double money);
mapper.xml:
<resultMap id="recommendStudentResultMap" type="recommendStudent">
<id column="student_id" property="studentId"/>
<result column="student_name" property="studentName"/>
<result column="age" property="age"/>
<result column="sex" property="sex"/>
<result column="mobile" property="mobile"/>
<result column="teacher_name" property="teacherName"/>
<result column="money" property="money"/>
</resultMap>
<select id="selectConditionStudents1" resultMap="recommendStudentResultMap">
select * from t_recommend_student
<where>
<if test="studentName != null and !studentName.isEmpty()">
student_name like concat('%',#{studentName},'%')
</if>
<if test="teacherName != null and !teacherName.isEmpty()">
and teacher_name like concat('%',#{teacherName},'%')
</if>
<if test="money != null">
and money = #{money}
</if>
</where>
</select>
5.1 脚本SQL
将mapper.xml中动态sql语句
使用
@Select({"<script>",
"select * from t_recommend_student",
"<where>",
" <if test=\"studentName != null and !studentName.isEmpty()\"> ",
"student_name like concat('%',#{studentName},'%')",
"</if>",
"<if test=\"teacherName != null and !teacherName.isEmpty()\">",
"and teacher_name like concat('%',#{teacherName},'%')",
"</if>",
"<if test=\"money != null\">",
"and money = #{money}",
"</if>",
"</where>",
"</script>"})
List<RecommendStudent> selectConditionStudents2(@Param("studentName")String studentName,@Param("teacherName")String teacherName,@Param("money")Double money);
5.2 编程式动态SQL(了解)
5.2.1 编程式动态sql思路
- 定义一个类,类中定义一个和mapper方法形参列表相同的方法
- 方法返回String,在方法中通过if条件判断生成动态的sql语句
- 在mapper方法上通过 SelectProvider 注解,配置生成sql的方法
-
将根据参数生成动态sql的代码抽取成一个方法
public interface RecommendStudentMapper { @ResultMap("recommendStudentResultMap") List<RecommendStudent> selectConditionStudents3(@Param("studentName")String studentName, @Param("teacherName")String teacherName, @Param("money")Double money); //RecommendStudentMapperSqlProvider做成接口内部类,方便使用 public static class RecommendStudentMapperSqlProvider{ public String selectConditionStudents3(@Param("studentName")String studentName, @Param("teacherName")String teacherName, @Param("money")Double money){ String sql = "select * from t_recommend_student"; boolean checkUsername = studentName != null && !studentName.isEmpty(); boolean checkTeacherName = teacherName != null && !teacherName.isEmpty(); boolean checkMoney = money != null; if(checkUsername || checkTeacherName || checkMoney){ sql += " where "; if (checkUsername) { sql += "student_name like concat('%',#{studentName},'%')"; } if(checkTeacherName){ if(checkUsername){ sql += " and "; } sql += "teacher_name like concat('%',#{teacherName},'%')"; } if(checkMoney){ if(checkUsername || checkTeacherName){ sql += " and "; } sql += "money = #{money}"; } } return sql; } } }
-
在接口方法上调用生成动态sql的方法
public interface RecommendStudentMapper { @ResultMap("recommendStudentResultMap") /* type:生成动态sql方法所在类的类对象 method:生成动态sql方法的名 */ @SelectProvider(type = RecommendStudentMapperSqlProvider.class,method = "selectConditionStudents3") @ResultMap("recommendStudentResultMap") List<RecommendStudent> selectConditionStudents3(@Param("studentName")String studentName, @Param("teacherName")String teacherName, @Param("money")Double money); ... }
5.2.2 更多的动态SQL注解
SelectProvider注解专为查询方法配置生成sql语句的方法。如果是增、删、改方法,则使用 @InsertProvider
、@DeleteProvider
、@UpdateProvider
注解。
@DeleteProvider(type=生成动态sql方法所在类.class,method="生成sql方法名")
@InsertProvider(type=生成动态sql方法所在类.class,method="生成sql方法名")
@UpdateProvider(type=生成动态sql方法所在类.class,method="生成sql方法名")
@SelectProvider(type=生成动态sql方法所在类.class,method="生成sql方法名")
四个注解除了标注的方法类型不同,在使用中并没有别的差异!!!
5.2.3 List参数的特殊要求
如果mapper方法形参列表只有一个List参数,生成动态SQL语句的方法形参列表要做特殊处理!!!
- 如果List形参未使用注解,则动态SQL方法的List形参前要添加
@Param("list")
- 如果List形参使用了注解,则动态SQL方法的List形参前要添加
@Param("相同的注解名")
5.2.4 SQL类简化动态SQL
SQL类:简化注解动态SQL的生成。
public String selectConditionStudents4(@Param("studentName")String studentName,
@Param("teacherName")String teacherName,
@Param("money")Double money){
SQL sql = new SQL();//生成动态SQL的工具
sql.SELECT("*")
.FROM("t_recommend_student");
if(studentName != null && !studentName.isEmpty()){
sql.WHERE("student_name like concat('%',#{studentName},'%')");
}
if (teacherName != null && !teacherName.isEmpty()) {
sql.WHERE("teacher_name like concat('%',#{teacherName},'%')");
}
if (money != null) {
sql.WHERE("money = #{money}");
}
return sql.toString();
}
6 其它注解
6.1 缓存相关注解
@CacheNamespace//类似于mapper.xml的cache标签
public interface UserMapper {
}
@CacheNamespaceRef(name = "另外一个Mapper的namespace")
public interface UserMapper {
}
6.2 Options注解
封装了insert、delete、update、select标签中一些属性,通过Options注解可以设置这些属性值。