作者:梁开权,叩丁狼高级讲师。
各位小伙伴们大家好,逍遥又跟大家见面了,在上次分享中给大家分享了如何通过注解的方式来操作MyBatis,也都仅仅局限于静态SQL语句,这次给大家分享想如何通过注解的方式使用动态SQL
准备工作
事先建好表/domain/mapper/QueryObject
@Setter@Getter@ToString
public class Employee {
private Long id; //代理主键
private String name; //员工姓名
private String age; //员工年龄
}
public interface EmployeeMapper {
//查询符合条件的总行数
Integer selectForCount(EmployeeQueryObject qo);
//查询符合条件的对象
List<?> selectForList(EmployeeQueryObject qo);
}
@Setter@Getter
public class EmployeeQueryObject {
private int currentPage; //当前页
private int pageSize; //页面容量
private String name; //员工姓名的模糊查询
private Integer minAge; //员工年龄的最小值范围查询
private Integer maxAge; //员工年龄的最大值范围查询
//取值前跳过的行数
public int getStart() {
return (currentPage - 1) * pageSize;
}
}
方式1:
直接在mapper接口的方法中贴@Select注解在配置value属性,该属性中的SQL语句使用script标签包起来,表示里面使用了脚本语言
public interface EmployeeMapper {
//查询符合条件的总行数
@Select("<script>" +
"select count(0) from employee" +
"<where>" +
"<if test='name != null'>" +
"and name like concat('%', #{name}, '%')"+
"</if>"+
"<if test='minAge != null'>" +
"and age >= #{minAge}"+
"</if>"+
"<if test='maxAge != null'>" +
"and age <= #{maxAge}"+
"</if>"+
"</where>" +
"</script>")
Integer selectForCount(EmployeeQueryObject qo);
//查询符合条件的对象
@Select("<script>" +
"select id, name, age from employee" +
"<where>" +
"<if test='name != null'>" +
"and name like concat('%', #{name}, '%')"+
"</if>"+
"<if test='minAge != null'>" +
"and age >= #{minAge}"+
"</if>"+
"<if test='maxAge != null'>" +
"and age <= #{maxAge}"+
"</if>"+
"</where>" +
"limit #{start}, #{pageSize}" +
"</script>")
List<?> selectForList(EmployeeQueryObject qo);
}
以上代码中存在了大量的重复代码,比如from语句和where的语句都是一样的,此时是这种直接注解中写SQL的方式是不能做抽取的,怎么办?我们可以使用另外的一种方式,把生成SQL语句的代码写在某个类的某个方法中,再告诉框架找某个类的某个方法拿到SQL语句即可
方式2:
自己定义一个类,在类中设计两个方法分别用于拿到selectForCount和selectForList的SQL语句,在此之前先介绍MyBatis的SQL对象,该对象用于生成最终的SQL语句
SQL对象
此时我们就可以通过自己动手创建SQL对象,从而拿到我们要的SQL语句
//员工对象的SQL提供者
public class EmployeeSQLProvider {
//拿到查询总行数的SQL语句
public String getCountSql(EmployeeQueryObject qo) {
//创建SQL对象并设置select语句要查询的列
SQL sql = new SQL().SELECT("count(0)");
addFrom(sql); //添加from语句
addWhere(sql, qo); //添加where语句
return sql.toString();
}
//拿到查询集合对象的SQL语句
public String getLimitSql(EmployeeQueryObject qo) {
SQL sql = new SQL().SELECT("id, name, age");
addFrom(sql);
addWhere(sql, qo);
return sql.toString();
}
//抽取拼接的from语句的方法
private void addFrom(SQL sql) {
sql.FROM("employee"); //往SQL对象中添加from语句
}
//抽取拼接的where语句的方法
private void addWhere(SQL sql, EmployeeQueryObject qo) {
//往SQL对象中动态的添加添加
if (StringUtils.hasLength(qo.getName())) {
sql.WHERE("e.name like #{name}");
}
if (qo.getMinAge() != null) {
sql.WHERE("e.age >= #{minAge}");
}
if (qo.getMaxAge() != null) {
sql.WHERE("e.age <= #{maxAge}");
}
}
}
有了上面的哪个SQL提供者,我们就可以告诉框架找EmployeeSQLProvider对象中的方法来拿到SQL语句,所以我们在mapper接口的方法中使用注解@SelectProvider,并且告诉该注解找EmployeeSQLProvider类中的哪个方法拿SQL语句即可,所以方式2就变成一下的样子
public interface EmployeeMapper {
//type表示提供SQL的类,method表示该类中的哪个方法
@SelectProvider(type= EmployeeSQLProvider.class, method="getCountSql")
Integer selectForCount(EmployeeQueryObject qo);
@SelectProvider(type= EmployeeSQLProvider.class, method="getLimitSql")
List<?> selectForList(EmployeeQueryObject qo);
}
对比方式1大家应该不难发现方式2在配置上的简洁性,把复杂的逻辑操作拿到了代码中,同时也能抽取重复的代码,相对来讲方式2要更好些,大家学会了吗?本期就先分享到这来,我们下期再会
WechatIMG9.jpeg