实现Mybatis-CommonMapper通用增删改查功能记录(二-简单的SQL构造)
前言
在看了别人的项目后,构思自己的通用Mapper,想法是类似于mybatis-plus使用继承的方式添加日常的增删改查功能。例如下面的使用方式:
public interface UserMapper extends CommonMapper<User> {}
mybatis-provider
搜索教程,看到mybatis3加入了几个provier注解,分别是:@SelectProvider,@InsertProvider,@UpdateProvider,@DeleteProvider,其中文档说明允许构建动态SQL,这意味着我们可以基于此类注解实现动态增删改查SQL的功能。
基础使用示例
展示单个/多个参数的基础实现
public interface UserMapper {
User selectUser(String id);
@SelectProvider(type = UserSelectProvider.class,method = "selectUserByEmail")
User selectUserByEmail(@Param("email") String email);
@SelectProvider(type = UserSelectProvider.class, method = "selectUserByName")
User selectUserByName(@Param("name") String name, @Param("orderByColum")String orderByColum);
}
package org.nott.web.provider;
import org.apache.ibatis.jdbc.SQL;
public class UserSelectProvider {
public String selectUserByEmail(String email) {
return new SQL() {{
SELECT("*");
FROM("user");
WHERE("email like #{email}");
}}.toString();
}
public String selectUserByName(String name,String orderByColum){
return new SQL(){{
SELECT("*");
FROM("user");
WHERE("name like #{name}");
ORDER_BY(orderByColum);
}}.toString();
}
}
注:3.5.6版本后可以指定模板defaultSqlProviderType,省略指定xxProvider注解中的type/value,id
单元测试

编写CommonProvider
根据上述例子,可以动态构建SQL,只需指定SQL中可变的字段和条件,实现通用的CommonMapper提供出通用的增删改查方法。我没有使用defaultSqlProviderType,而是将上述四个provider注解分别供四个provider进行选择。
条件构造
看到上面mybatis提供的例子,想到应该写一个条件构造,最简单的需要动态的建立select条件,这里定义了一个条件构造SimpleSqlConditionBuilder,继承的SqlQuery只是用来定义里面的条件方法内容。 先写了等于和不等于进行下面简单的select方法的测试。
@Data
public class SimpleSqlConditionBuilder implements SqlQuery{
List<SqlConditions> sqlConditions = new ArrayList<>();
Integer limit;
public static SimpleSqlConditionBuilder create(Class tClass){
return new SimpleSqlConditionBuilder();
}
@Override
public SimpleSqlConditionBuilder eq(String colum, Object val) {
SqlConditions conditions = new SqlConditions(colum,val,SqlOperator.EQ);
this.sqlConditions.add(conditions);
return this;
}
@Override
public SimpleSqlConditionBuilder neq(String colum, Object val) {
SqlConditions conditions = new SqlConditions(colum,val,SqlOperator.NEQ);
this.sqlConditions.add(conditions);
return this;
}
public SimpleSqlConditionBuilder limit(Integer value){
this.limit = value;
return this;
}
}
// 用于构建sql 条件 运算符 值 的运算条件
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SqlConditions {
private String colum;
private Object value;
// 运算符枚举
private SqlOperator sqlOperator;
}
BaseSelectProvider
这里定义了一个BaseSelectProvider供SelectProvider注解的type选中,内容是封装一般的查找方法,先个selectOne方法试试。(其中,BaseSelectProvider这里的泛型是有伏笔的,后面会踩坑)
public class BaseSelectProvider<T> {
/**
* 获取当前类的泛型
*/
private Class genericSuperClass;
public BaseSelectProvider(){
Type genericSuperType = this.getClass().getGenericSuperclass();
if(genericSuperType == null){
throw new GenericClassException("Generic class not found");
}
// Type[] types = ((ParameterizedType) genericSuperType).getActualTypeArguments();
Type[] p = ((ParameterizedType) genericSuperType).getActualTypeArguments();
System.out.println(Arrays.toString(p));
this.genericSuperClass = (Class) p[0];
}}
// 创建mybatis SQL对象,获得sql语句
public String buildSql(SimpleSqlConditionBuilder simpleSqlConditionBuilder){
// 使用当前范型class获取对象信息,表名等
Class<?> aClass = this.genericSuperClass;
Class<?> aClass = this.genericSuperClass;
boolean annotationPresent = aClass.isAnnotationPresent(CustTableName.class);
String tableName;
if(annotationPresent){
CustTableName custTableName = (CustTableName) aClass.getAnnotation(CustTableName.class);
tableName = custTableName.name();
}else {
String simpleName = aClass.getSimpleName();
// 大驼峰转下划线处理
tableName = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, simpleName);
}
...
}
public String selectOne(){
SimpleSqlConditionBuilder.create(this.genericSuperClass.getClass()).limit(1);
return buildSql(null);
}
CommonMapper
这里就是我们提供给外面继承的CommonMapper,类似于mbatis-plus的BaseMapper
public interface CommonMapper<T> {
@SelectProvider(type = BaseSelectProvider.class,method = "selectOne")
public T selectOne();
}
测试
web测试模块UserMapper继承CommonMapper

编写测试方法

这里失败了,还记得CommonMapper里的方法上面的注解 @SelectProvider(type = BaseSelectProvider.class,method = "selectOne"),这里BaseSelectProvider压根不知道泛型是什么,所以它的泛型获取到是Object类,不能转换到CommonMapper上提供的<T>,所以报错了。
修改
后面我修改成传入T t对象作为参数,因此调用的时候每个方法都需要加入t对象,会显得很笨重,有点不合理。

小结
虽然后面新写的条件构造也通过单元测试,但是感觉还需要修改。
下次找到别的解决方法再同步更新下一篇文章,顺便把根据主键操作方法添加上,目前想到的解决方法是把当前线程中的dao中的信息(dao的class、dao泛型)放入ThreadLocal,BaseSelectProvider执行方法时获取进行实例化,这就不用使用成员变量。
参考
mybatis官方文档:mybatis.org/mybatis-3/z…
转载自:https://juejin.cn/post/7367611991362437172