1、基于mybatis plus进行了扩展,添加了join 功能。
2、并且实现了在内存中join结果集,使用方式同join表一致。
3、支持内嵌视图和子查询,强类型且无sql注入风险。
4、增加了SmartQueryWrapper,构建时基于实体的field名进行构建(无需关注表的列名)
5、通过代码就可以构建出常用查询,减少了对Mapper.xml的依赖,可以将错误暴露在编译环节。
6、查询中可以指定可选表,如果设置为可选表则会判断有where、select、group、having、order等地方使用到相关字段的情况才会去join它,否则会跳过它提升性能。
使用场景: 假设做一个学生管理的页面,可以根据学生名称、班级名称、班主任名称进行搜索,如果按照传统的方式实现,需要关联学生表、老师表、班级表等多个表,即便只输入了学生信息,也需要去关联其它几个表,性能会很受影响(多个大表join查询性能比单个表查询慢很多,尤其是在没有任何输入的情况)。最优的方式应该是没有输入或者只输入了学生相关信息只查学生表,输入了老师信息才需要关联老师表。
1、添加依赖 of.mybatisplus of.mybatisplus 1.0-SNAPSHOT
mybatis和mybatisplus 的依赖会自动引入。其它配置以及依赖可以参考 mybatisplus
2、定义join查询的例子,只需要4步即可。
// (1)、定义执行器,有SmartDBRunner(sql中join表)和SmartMemRunner(内存中join结果集)两种。SmartDBRunner需要指定返回最终结果的类型
SmartDBRunner dbRunner = SmartDBRunner.create(Student.class);
// (2)、创建 SmartQueryWrapper,针对每个表的查询、筛选、order、group等都是在 SmartQueryWrapper 上进行操作的。需要指定类型,表的别名和是否可选。分别构建各个表的查询条件(也可以使用initByProperties方法根据请求里面的参数动态构建)以及排序、分组等信息。
SmartQueryWrapper<Student> studentWrapper = dbRunner.createWrapper(Student.class, "s", false);
// 构建 Student表的查询条件
studentWrapper.eq(StringUtils.isNotEmpty(searchRequest.getStudentName()), Student.Fields.name, searchRequest.getStudentName())
.eq(searchRequest.getAge() != null, Student.Fields.age, searchRequest.getAge())
.select(Student.Fields.id, Student.Fields.name, Student.Fields.age, Student.Fields.classId);
// 构建 Class 表的查询条件
SmartQueryWrapper<Clazz> clazzWrapper = dbRunner.createWrapper(Clazz.class, "c", true);
clazzWrapper.eq(StringUtils.isNotEmpty(searchRequest.getClassName()), Clazz.Fields.name, searchRequest.getClassName());
// 构建 Teacher 表的查询条件
SmartQueryWrapper<Teacher> teacherWrapper = dbRunner.createWrapper(Teacher.class, "t", true);
teacherWrapper.eq(StringUtils.isNotEmpty(searchRequest.getMasterTeacherName()), Teacher.Fields.name, searchRequest.getMasterTeacherName());
// (3)、指定连接条件,注意需要使用 wrapper 的 dbJoinEntry 方法
dbRunner.join(studentWrapper.dbJoinEntry(Student.Fields.classId), clazzWrapper.dbJoinEntry(Clazz.Fields.id))
.join(clazzWrapper.dbJoinEntry(Clazz.Fields.masterTeacherId), teacherWrapper.dbJoinEntry(Teacher.Fields.id));
// (4)、执行,需要传入wrapper数组,会根据这个数组的顺序来生成最终的 sql。
return dbRunner.query(studentMapper, Arrays.asList(studentWrapper, clazzWrapper, teacherWrapper));
3、定义内存中join结果集合的例子,只需要五步。
//(1)、定义执行器,SmartMemRunner需要指定是否反向查找
SmartMemRunner smartMemRunner = SmartMemRunner.create(true);
SmartQueryWrapper<Student> studentWrapper = smartMemRunner.createWrapper(Student.class, "s", false);
studentWrapper.eq(StringUtils.isNotEmpty(searchRequest.getStudentName()), Student.Fields.name, searchRequest.getStudentName())
.eq(searchRequest.getAge() != null, Student.Fields.age, searchRequest.getAge())
.select(Student.Fields.id, Student.Fields.name, Student.Fields.age, Student.Fields.classId);
// (2)、创建 SmartQueryWrapper,同join查询没有区别
SmartQueryWrapper<Clazz> clazzWrapper = smartMemRunner.createWrapper(Clazz.class, "c", true);
clazzWrapper.eq(StringUtils.isNotEmpty(searchRequest.getClassName()), Clazz.Fields.name, searchRequest.getClassName());
SmartQueryWrapper<Teacher> teacherWrapper = smartMemRunner.createWrapper(Teacher.class, "t", true);
teacherWrapper.eq(StringUtils.isNotEmpty(searchRequest.getMasterTeacherName()), Teacher.Fields.name, searchRequest.getMasterTeacherName());
// (3)、指定连接条件,注意需要使用 wrapper 的 memJoinEntry 方法
smartMemRunner.join(studentWrapper.memJoinEntry(Student::getClassId, Student.Fields.classId), clazzWrapper.memJoinEntry(Clazz::getId, Clazz.Fields.id), Integer.class)
.join(clazzWrapper.memJoinEntry(Clazz::getMasterTeacherId, Clazz.Fields.masterTeacherId), teacherWrapper.memJoinEntry(Teacher::getId, Teacher.Fields.id), Integer.class);
// (4)、执行,需要传入wrapper数组,并且需要传入 queryer 的consumer,在此consumer 中依次调用 queryer的query方法传入 mapper 和对应的 wrapper来执行相关表的查询。(执行顺序要和需要传入wrapper数组一致,否则框架会提示出错)
smartMemRunner.run(Arrays.asList(studentWrapper, clazzWrapper, teacherWrapper), queryer -> {
queryer.query(studentMapper, studentWrapper);
queryer.query(clazzMapper, clazzWrapper);
queryer.query(teacherMapper, teacherWrapper);
});
// (5)、每个wrapper相关的执行的结果都会放在runner内部的字典中。所以可以传入 wrapper 来获取每个wrapper对应的所有记录。并且可以通过 findTarget 来查找到某个wrapper的数据关联到其它wrapper的数据(框架会根据wrapper之间的关系进行推断,此过程会搜索所有可能的路径)
List<Student> studentList = smartMemRunner.getResult(studentWrapper);
List<StudentQueryResult> resultList = new ArrayList<>();
for (Student student : studentList) {
// 演示根据 studentWrapper 和 clazzWrapper 关系查找,找到学生对应的班级信息。
List<Clazz> clazzList = smartMemRunner.findTarget(studentWrapper, clazzWrapper, Collections.singletonList(student));
// 演示根据 studentWrapper 和 clazzWrapper 关系查找,找到学生对应的班级信息,再通过 clazzWrapper 和 teacherWrapper的关联关系找到对应的班主任。
List<Teacher> teacherList = smartMemRunner.findTarget(studentWrapper, teacherWrapper, Collections.singletonList(student));
// 演示根据 clazzWrapper 和 teacherWrapper的关联关系直接取班级的班主任信息
List<Teacher> teacher2List = smartMemRunner.findTarget(clazzWrapper, teacherWrapper, clazzList);
log.info("student:{}, clazz: {}, teacher:{}, teacher2: {}", student, clazzList, teacherList, teacher2List);
}
4、定义内存中join结果集合的例子(中间都步骤可以是其它数据源、其它方法或者其它join表) 同3基本一致,区别是调用 Queryer 的 重载方法 query(Function<SmartQueryWrapper, List> mapper, SmartQueryWrapper wrapper) ,具体实现可以参考 https://gitee.com/fastdev/of.mybatistest 中的 effectiveMixQuery 示例。
详细示例和演示代码在另外一个仓库下,地址是: https://gitee.com/fastdev/of.mybatistest
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。