1.tkMapper与springboot整合
springboot的版本为2.1.7.RELEASE,tkmapper的pom为
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>2.1.5</version>
</dependency>
application.yml中mybatis的配置为
mybatis:
# 如果有自定义SQL,mapper接口对应的.xml文件
mapper-locations: classpath:mapper/*.xml
定义Mapper接口要实现的父接口
/**
* Mapper包含通用SQL,MySqlMapper是对针MySQL的补充
*/
public interface BaseMapper<T> extends Mapper<T>, MySqlMapper<T> {}
mapper接口要使用@Mapper修饰并继承BaseMapper
@Mapper
public interface SysUserMapper extends BaseMapper<SysUser> {
/**
* 自定义的SQL,写在.xml中
* @return
*/
List<SysUser> mySelect();
}
2.tkMapper支持的SQL
tkMapper支持单表查询的所有场景,常见方法有
public void test() {
// 插入
SysUser sysUser = new SysUser();
int count = mapper.insert(sysUser);
// 批量插入,count为操作行数
List<SysUser> userList = new ArrayList<>();
userList.add(new SysUser());
userList.add(new SysUser());
count = mapper.insertList(userList);
// 通过主键查询
sysUser = mapper.selectByPrimaryKey(10000);
// 查询匹配且只返回一个,很多匹配时报错
sysUser = mapper.selectOne(sysUser);
// 查询所有匹配
sysUser.setName("张三");
userList = mapper.select(sysUser);
// 自定义的SQL
userList = mapper.mySelect();
// Selective表示有这个值的话就更新,否则不更新
count = mapper.updateByPrimaryKey(sysUser);
count = mapper.updateByPrimaryKeySelective(sysUser);
// QBC查询
Example example = new Example(SysUser.class);
// 模糊查询
Example.Criteria criteria1 = example.createCriteria();
criteria1.andLike("name", "%n%");
// 精确查询
Example.Criteria criteria2 = example.createCriteria();
criteria2.andEqualTo("code", "123");
// 2个查询条件
example.or(criteria2);
// 排序
example.orderBy("name").asc();
// 去重
example.setDistinct(true);
// 返回字段
example.selectProperties("id", "name", "code");
userList = mapper.selectByExample(example);
}
对应的model应该有注解修饰
@Table(name = "sys_user")
public class SysUser {
/**
* 主键,@GeneratedValue为自增规则
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
/**
* 姓名
*/
private String name;
/**
* 员工编码
*/
private String code;
}
3.tkMapper的源码解析
Mybatis支持@SelectProvider注解,tkMapper正是运用了该技术,通过解析持久类,动态生成SQL,重新为MappedStatment设置SqlSource。
我们看到在执行时每个SQL都已转换成MappedStatment,tkMapper是怎么做到的呢?这里可以分2步,第一步通过解析注解生成SQL,第二步是为MappedStatment赋值。

这些都是从MapperScannerConfigurer开始的,因为该类会扫描所有被@Mapper修饰的接口,由于继承了BeanDefinitionRegistryPostProcessor接口,可以动态的对Bean的内容进行修改。
我们先看一下它的核心代码:
private MapperHelper mapperHelper = new MapperHelper();
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
// 调用mybatis的后置注册处理,扫描mapper类注册为MapperFactoryBean对象
super.postProcessBeanDefinitionRegistry(registry);
this.mapperHelper.ifEmptyRegisterDefaultInterface();
String[] names = registry.getBeanDefinitionNames();
GenericBeanDefinition definition;
for (String name : names) {
BeanDefinition beanDefinition = registry.getBeanDefinition(name);
if (beanDefinition instanceof GenericBeanDefinition) {
definition = (GenericBeanDefinition) beanDefinition;
// 判断注册的bean是MapperFactoryBean,设置beanClass为 tk.mybatis.spring.mapper.MapperFactoryBean
// 设置MapperHelper属性
if (StringUtil.isNotEmpty(definition.getBeanClassName())
&& definition.getBeanClassName().equals("org.mybatis.spring.mapper.MapperFactoryBean")) {
definition.setBeanClass(MapperFactoryBean.class);
definition.getPropertyValues().add("mapperHelper", this.mapperHelper);
}
}
}
}
其中MapperHelper是将所有用mapper接口的父类中被@SelectProvider修饰的类,也就是SQL模板作为MapperTemplate存在registerMapper中。到这里我们的第一步就完成了。

我们先看一下它的核心代码:
public class MapperHelper {
private List<Class<?>> registerClass;
private Map<Class<?>, MapperTemplate> registerMapper;
private Config config;
public void registerMapper(String mapperClass) {
try {
// 实例化Mapper接口
registerMapper(Class.forName(mapperClass));
} catch (ClassNotFoundException e) {
throw new MapperException("注册通用Mapper[" + mapperClass + "]失败,找不到该通用Mapper!");
}
}
public void registerMapper(Class<?> mapperClass) {
if (!registerMapper.containsKey(mapperClass)) {
registerClass.add(mapperClass);
// 注册Mapper的Class和通过通用Mapper接口获取对应的MapperTemplate,后文详解#2
registerMapper.put(mapperClass, fromMapperClass(mapperClass));
}
// 自动注册继承的接口
Class<?>[] interfaces = mapperClass.getInterfaces();
if (interfaces != null && interfaces.length > 0) {
for (Class<?> anInterface : interfaces) {
// 递归注册Mapper接口继承的其他接口如BaseMapper接口
registerMapper(anInterface);
}
}
}
private MapperTemplate fromMapperClass(Class<?> mapperClass) {
// 获得接口中的方法,BaseMapper接口中没有方法,SelectOneMapper接口中有selectOne方法
Method[] methods = mapperClass.getDeclaredMethods();
Class<?> templateClass = null;
Class<?> tempClass = null;
Set<String> methodSet = new HashSet<String>();
for (Method method : methods) {
// 判断接口的方法中是否有SelectProvider注解修饰
if (method.isAnnotationPresent(SelectProvider.class)) {
SelectProvider provider = method.getAnnotation(SelectProvider.class);
tempClass = provider.type();
methodSet.add(method.getName());
} else if (method.isAnnotationPresent(InsertProvider.class)) {
InsertProvider provider = method.getAnnotation(InsertProvider.class);
tempClass = provider.type();
methodSet.add(method.getName());
} else if (method.isAnnotationPresent(DeleteProvider.class)) {
DeleteProvider provider = method.getAnnotation(DeleteProvider.class);
tempClass = provider.type();
methodSet.add(method.getName());
} else if (method.isAnnotationPresent(UpdateProvider.class)) {
UpdateProvider provider = method.getAnnotation(UpdateProvider.class);
tempClass = provider.type();
methodSet.add(method.getName());
}
if (templateClass == null) {
// 将注解中的type属性值赋值给templateClass变量,如BaseSelectProvider
templateClass = tempClass;
} else if (templateClass != tempClass) {
throw new MapperException("一个通用Mapper中只允许存在一个MapperTemplate子类!");
}
}
// 判断templateClass变量是否为null或者判断templateClass变量值是否继承自MapperTemplate如BaseSelectProvider
if (templateClass == null || !MapperTemplate.class.isAssignableFrom(templateClass)) {
templateClass = EmptyProvider.class;
}
MapperTemplate mapperTemplate = null;
try {
// 通过构造反射实例化MapperTemplate如BaseSelectProvider,构造参数为Mapper的Class(如SelectOneMapper.class)、MapperHelper.class
mapperTemplate = (MapperTemplate) templateClass.getConstructor(Class.class, MapperHelper.class).newInstance(mapperClass, this);
} catch (Exception e) {
throw new MapperException("实例化MapperTemplate对象失败:" + e.getMessage());
}
// 注册方法
for (String methodName : methodSet) {
try {
// 注册templateClass中的方法如BaseSelectProvider中的selectOne方法
mapperTemplate.addMethodMap(methodName, templateClass.getMethod(methodName, MappedStatement.class));
} catch (NoSuchMethodException e) {
throw new MapperException(templateClass.getCanonicalName() + "中缺少" + methodName + "方法!");
}
}
// 返回Mapper模板对象
return mapperTemplate;
}
}
MapperScannerConfigurer在后置处理时,注册了tk.mybatis.spring.mapper.MapperFactoryBean,为每个mapper接口设置了MappedStatment。到这里我们的第二步就完成了。
@Override
protected void checkDaoConfig() {
// 调用父类org.mybatis.spring.mapper.MapperFactoryBean,解析Mapper的注解,为Configuration注册Mapper
// 如解析类注解注册Cache,解析方法注解注册MappedStatment(包括parameterType,SqlSource,ResultMap等)
super.checkDaoConfig();
// 判断Mapper接口是否继承了通用Mapper,getObjectType()为当前Mapper接口
if (mapperHelper.isExtendCommonMapper(getObjectType())) {
// 处理Configuration,为MappedStatment重新设置SqlSource
mapperHelper.processConfiguration(getSqlSession().getConfiguration(), getObjectType());
}
}
这里是MapperFactoryBean调用MapperHelper的核心代码:
public void processConfiguration(Configuration configuration, Class<?> mapperInterface) {
String prefix;
if (mapperInterface != null) {
prefix = mapperInterface.getCanonicalName();
} else {
prefix = "";
}
// 遍历所有的MappedStatment
for (Object object : new ArrayList<Object>(configuration.getMappedStatements())) {
if (object instanceof MappedStatement) {
MappedStatement ms = (MappedStatement) object;
if (ms.getId().startsWith(prefix) && isMapperMethod(ms.getId())) {
// 判断是否解析注解生成的SqlSource
if (ms.getSqlSource() instanceof ProviderSqlSource) {
// 重新为MappedStatment设置SqlSource
setSqlSource(ms);
}
}
}
}
}
public void setSqlSource(MappedStatement ms) {
// 从缓存中获得MapperTemplate,如BaseSelectProvider
MapperTemplate mapperTemplate = msIdCache.get(ms.getId());
try {
if (mapperTemplate != null) {
mapperTemplate.setSqlSource(ms);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public void setSqlSource(MappedStatement ms) throws Exception {
if (this.mapperClass == getMapperClass(ms.getId())) {
throw new RuntimeException("请不要配置或扫描通用Mapper接口类:" + this.mapperClass);
}
// 从缓存中获得方法,如selectOne方法
Method method = methodMap.get(getMethodName(ms));
try {
//第一种,直接操作ms,不需要返回值
if (method.getReturnType() == Void.TYPE) {
method.invoke(this, ms);
}
//第二种,返回SqlNode
else if (SqlNode.class.isAssignableFrom(method.getReturnType())) {
SqlNode sqlNode = (SqlNode) method.invoke(this, ms);
DynamicSqlSource dynamicSqlSource = new DynamicSqlSource(ms.getConfiguration(), sqlNode);
setSqlSource(ms, dynamicSqlSource);
}
//第三种,返回xml形式的sql字符串
else if (String.class.equals(method.getReturnType())) {
          // 方法反射调用,如selectOne方法,后文详解#3
String xmlSql = (String) method.invoke(this, ms);
          // 通过LanguageDriver创建SqlSource
SqlSource sqlSource = createSqlSource(ms, xmlSql);
// 替换原有的SqlSource,后文详解#4
setSqlSource(ms, sqlSource);
} else {
throw new RuntimeException("自定义Mapper方法返回类型错误,可选的返回类型为void,SqlNode,String三种!");
}
//cache
checkCache(ms);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e.getTargetException() != null ? e.getTargetException() : e);
}
}
这里还有一个细节,SQL是怎么动态生成的呢,每个MapperTemplate的注册方法都有xml格式的SQL,以BaseSelectProvider的selectOne为例,原理是通过解析model类上的注解,并缓存到entityClassMap。

public String selectOne(MappedStatement ms) {
// 从MappedStatment中拿到MapperClass,如com.wjz.StudentMapper.class
// 再拿到StudentMapper的泛型类型,如Student.class
Class<?> entityClass = getEntityClass(ms);
// 修改返回值类型为实体类型
setResultType(ms, entityClass);
StringBuilder sql = new StringBuilder();
// 拼接查询的字段
sql.append(SqlHelper.selectAllColumns(entityClass));
// 拼接表名,表名从EntityTable中的name属性中拿
sql.append(SqlHelper.fromTable(entityClass, tableName(entityClass)));
// 拼接where条件
sql.append(SqlHelper.whereAllIfColumns(entityClass, isNotEmpty()));
return sql.toString();
}
public Class<?> getEntityClass(MappedStatement ms) {
String msId = ms.getId();
if (entityClassMap.containsKey(msId)) {
// 从缓存中获得
return entityClassMap.get(msId);
} else {
Class<?> mapperClass = getMapperClass(msId);
Type[] types = mapperClass.getGenericInterfaces();
for (Type type : types) {
if (type instanceof ParameterizedType) {
ParameterizedType t = (ParameterizedType) type;
if (t.getRawType() == this.mapperClass || this.mapperClass.isAssignableFrom((Class<?>) t.getRawType())) {
Class<?> returnType = (Class<?>) t.getActualTypeArguments()[0];
// 获取该类型后,第一次对该类型进行初始化
EntityHelper.initEntityNameMap(returnType, mapperHelper.getConfig());
// 放置到缓存中
entityClassMap.put(msId, returnType);
return returnType;
}
}
}
}
throw new RuntimeException("无法获取Mapper<T>泛型类型:" + msId);
}