栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

基于tkmybatis的无xml联表通用mapper

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

基于tkmybatis的无xml联表通用mapper

写在最开始,一直是tkmybatis的拥趸,不想写sql经常被人鄙视,但是我恬不知耻...


目录

构造通用sql模板

构造select段sql模板

构造from段sql模板

构造适配example的通用条件sql模板

sql查询结果java对象映射

初始化个说事的数据库

xml的联表查询

xml到ResultMap

java类到resultMap

coding

sql字段模板

联表sql模板

java字段名与sql列名

java类生成resultMap对象

写在最后


为了表达对tkmaybatis的敬意,研习了一下他的源码,总结一下就是做了下边两件事

  1. 对于查询的通用mapper,构造出能够适配他的example的sql模板.
  2. sql查询结果的java对象映射,也就是xml里面对应的resultmap

构造通用sql模板

我们先看一下tkmybatis自己的查询sql模板

tk.mybatis.mapper.provider.ExampleProvider.java    

    public String selectByExample(MappedStatement ms) {
        Class entityClass = this.getEntityClass(ms);
        this.setResultType(ms, entityClass);
        StringBuilder sql = new StringBuilder("SELECT ");
        if (this.isCheckExampleEntityClass()) {
            sql.append(SqlHelper.exampleCheck(entityClass));
        }

        sql.append("distinct");
        sql.append(SqlHelper.exampleSelectColumns(entityClass));
        sql.append(SqlHelper.fromTable(entityClass, this.tableName(entityClass)));
        sql.append(SqlHelper.exampleWhereClause());
        sql.append(SqlHelper.exampleOrderBy(entityClass));
        sql.append(SqlHelper.exampleForUpdate());
        return sql.toString();
    }

可以看出主要做了3件事

  1. 构造select段sql模板
  2. 构造from段sql模板
  3. 构造适配example的通用条件sql模板

构造select段sql模板

由于tkmybatis的通用mapper目前只对单表进行操作,制作通用sql查询模板是需要解析单表entity类

tk.mybatis.mapper.mapperhelper.MapperTemplate.java

    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 MapperException("无法获取 " + msId + " 方法的泛型信息!");
    }

其中EntityHelper.initEntityNameMap(returnType, mapperHelper.getConfig());是核心代码.他负责解析对应entity类的字段名,对应数据相关属性等.这个里面我们发现他解析的entity类只能是简单pojo,代码如下

tk.mybatis.mapper.mapperhelper.resolveDefaultEntityResolve.java


public EntityTable resolveEntity(Class entityClass, Config config) {
...

for (EntityField field : fields) {
            //如果启用了简单类型,就做简单类型校验,如果不是简单类型,直接跳过
            //3.5.0 如果启用了枚举作为简单类型,就不会自动忽略枚举类型
            //4.0 如果标记了 Column 或 ColumnType 注解,也不忽略
            if (config.isUseSimpleType()
                    && !field.isAnnotationPresent(Column.class)
                    && !field.isAnnotationPresent(ColumnType.class)
                    && !(SimpleTypeUtil.isSimpleType(field.getJavaType())
                    ||
                    (config.isEnumAsSimpleType() && Enum.class.isAssignableFrom(field.getJavaType())))) {
                continue;
            }
            processField(entityTable, field, config, style);
        }
...
}

对于多表联查,entity肯定不能是简单pojo,select字段也需要带上表名前缀,这个地方markA

构造from段sql模板

这部分基于单表的很简单,就是entity类上边table注解.如果没有注解用一个内部定义方式把class名转换成为数据库表名.

对于多表联查,from字段也需要配置多表之间的连接关系,这个地方markB

构造适配example的通用条件sql模板

这部分基本可以复用已有的example解析模板,单表查询entity字段名和数据库字段名在example中进行了对应

对于多表联查,example中的不同表的字段和"表.字段"的sql转换是重要修改部分,这个地方markC

sql查询结果java对象映射

初始化个说事的数据库
DROp DATABASE IF EXISTS `demo`;
CREATE DATABASE IF NOT EXISTS `demo` ;
USE `demo`;

DROP TABLE IF EXISTS `test_class`;
CREATE TABLE IF NOT EXISTS `test_class` (
  `id` varchar(20) NOT NULL,
  `name` varchar(255) DEFAULT NULL,
  `school_id` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

DELETE FROM `test_class`;
INSERT INTO `test_class` (`id`, `name`, `school_id`) VALUES
	('1', '大一班', '1'),
	('2', '大二班', '1'),
	('3', '大三班', '1');

DROp TABLE IF EXISTS `test_school`;
CREATE TABLE IF NOT EXISTS `test_school` (
  `id` varchar(20) NOT NULL,
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

DELETE FROM `test_school`;
INSERT INTO `test_school` (`id`, `name`) VALUES
	('1', '幼儿园');

DROp TABLE IF EXISTS `test_teacher`;
CREATE TABLE IF NOT EXISTS `test_teacher` (
  `id` varchar(20) NOT NULL,
  `name` varchar(255) DEFAULT NULL,
  `class_id` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

DELETE FROM `test_teacher`;
INSERT INTO `test_teacher` (`id`, `name`, `class_id`) VALUES
	('1', '王老师', '1'),
	('2', '李老师', '1'),
	('3', '王二老师', '2'),
	('4', '李二老师', '2'),
	('5', '王三老师', '3'),
	('6', '李三老师', '3');

数据库关系如下

每个entity生成之后是这样的

@ApiModel(value = "test_school")
@Data
@Table(name = "test_school")
public class TestSchool {
    @Id
    @Column(name = "id")
    @ApiModelProperty(value = "")
    private String id;

    @Column(name = "`name`")
    @ApiModelProperty(value = "")
    private String name;
}
@ApiModel(value = "test_class")
@Data
@Table(name = "test_class")
public class TestClass {
    @Id
    @Column(name = "id")
    @ApiModelProperty(value = "")
    private String id;

    @Column(name = "`name`")
    @ApiModelProperty(value = "")
    private String name;

    @Column(name = "school_id")
    @ApiModelProperty(value = "")
    private String schoolId;
}
@ApiModel(value = "test_teacher")
@Data
@Table(name = "test_teacher")
public class TestTeacher {
    @Id
    @Column(name = "id")
    @ApiModelProperty(value = "")
    private String id;

    @Column(name = "`name`")
    @ApiModelProperty(value = "")
    private String name;

    @Column(name = "class_id")
    @ApiModelProperty(value = "")
    private String classId;
}

xml的联表查询

联表需要的select段sql

  
    
    test_school.id as test_school_id,
    test_school.name as test_school_name
  
  
    
    test_class.id as test_class_id,
    test_class.name as test_class_name,
    test_class.school_id as test_class_school_id
  
  
    
    test_teacher.id as test_teacher_id,
    test_teacher.name as test_teacher_name,
    test_teacher.class_id as test_teacher_class_id
  

 联表返回的resultMap

  
    
    
    
  
  
    
    
    
    
  
  
    
    
    
    
  

  
    
    
  
  
    
  

可以看出来我们想返回的是一个这样的java

@Data
@EqualsAndHashCode(callSuper = true)
public class SchoolTeacherJoin extends TestSchool {
    private List classTeacherJoinList;
}

@Data
@EqualsAndHashCode(callSuper = true)
public class ClassTeacherJoin extends TestClass {
    private List teacherList;
}

最终我们可以写出一个dao接口

public interface SchoolTeacherJoinDao {
    List select(@Param("schoolId") String schoolId,
                                   @Param("classId") String classId,
                                   @Param("teacherId") String teacherId);
}