一、mybatis-plus-join
mybatis-plus
原生的能力不支持多表关联,对于这种场景只能通过写SQL
进行实现,而mybatis-plus-join
则是建立在 mybatis-plus
基础之上的扩展框架,可以在不影响原有能力之上通过简单的API
即可实现多表关联能力而无需编写SQL
。
官方仓库地址:https://gitee.com/best_handsome/mybatis-plus-join
官方文档:https://mybatisplusjoin.com/pages/quickstart/js.html
二、mybatis-plus-join 实践
新建测试表结构:
CREATE TABLE `user` (`id` int NOT NULL AUTO_INCREMENT,`name` varchar(255) DEFAULT NULL,`age` int DEFAULT NULL,`email` varchar(255) DEFAULT NULL,`username` varchar(255) DEFAULT NULL,`password` varchar(255) DEFAULT NULL,`status` int DEFAULT NULL,`delete_flag` varchar(255) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;CREATE TABLE `role` (`id` int NOT NULL AUTO_INCREMENT,`role_name` varchar(255) DEFAULT NULL,`desc` varchar(255) DEFAULT NULL,`delete_flag` varchar(255) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;CREATE TABLE `user_role_mapping` (`id` int NOT NULL AUTO_INCREMENT,`user_id` int DEFAULT NULL,`role_id` int DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
写入测试数据:
INSERT INTO `test1`.`user` (`id`, `name`, `age`, `email`, `username`, `password`, `status`, `delete_flag`) VALUES (1, '张三', 15, 'zhangsan@test.com', 'zhangsan', '123', 1, '0');
INSERT INTO `test1`.`user` (`id`, `name`, `age`, `email`, `username`, `password`, `status`, `delete_flag`) VALUES (2, '李四', 16, 'lisi@test.com', 'lisi', '123', 1, '0');
INSERT INTO `test1`.`user` (`id`, `name`, `age`, `email`, `username`, `password`, `status`, `delete_flag`) VALUES (3, '王五', 15, 'wangwu@test.com', 'wangwu', '123', 1, '0');
INSERT INTO `test1`.`user` (`id`, `name`, `age`, `email`, `username`, `password`, `status`, `delete_flag`) VALUES (4, '李六', 18, 'liliu@test.com', 'liliu', '123', 1, '0');
INSERT INTO `test1`.`user` (`id`, `name`, `age`, `email`, `username`, `password`, `status`, `delete_flag`) VALUES (5, '小红', 15, 'xiaohong@test.com', 'xiaohong', '123', 1, '0');
INSERT INTO `test1`.`user` (`id`, `name`, `age`, `email`, `username`, `password`, `status`, `delete_flag`) VALUES (6, '小明', 19, 'xiaoming@test.com', 'xiaoming', '123', 1, '0');
INSERT INTO `test1`.`user` (`id`, `name`, `age`, `email`, `username`, `password`, `status`, `delete_flag`) VALUES (7, '小张', 15, 'xiaozhang@test.com', 'xiaozhang', '123', 1, '0');INSERT INTO `test1`.`role` (`id`, `role_name`, `desc`, `delete_flag`) VALUES (1, 'admin', '管理员', '0');
INSERT INTO `test1`.`role` (`id`, `role_name`, `desc`, `delete_flag`) VALUES (2, 'root', '超级管理员', '0');
INSERT INTO `test1`.`role` (`id`, `role_name`, `desc`, `delete_flag`) VALUES (3, 'common', '普通人', '0');
INSERT INTO `test1`.`role` (`id`, `role_name`, `desc`, `delete_flag`) VALUES (4, 'leader', '组长', '0');INSERT INTO `test1`.`user_role_mapping` (`id`, `user_id`, `role_id`) VALUES (1, 1, 1);
INSERT INTO `test1`.`user_role_mapping` (`id`, `user_id`, `role_id`) VALUES (2, 2, 1);
INSERT INTO `test1`.`user_role_mapping` (`id`, `user_id`, `role_id`) VALUES (3, 3, 3);
INSERT INTO `test1`.`user_role_mapping` (`id`, `user_id`, `role_id`) VALUES (4, 4, 3);
INSERT INTO `test1`.`user_role_mapping` (`id`, `user_id`, `role_id`) VALUES (5, 5, 4);
INSERT INTO `test1`.`user_role_mapping` (`id`, `user_id`, `role_id`) VALUES (6, 6, 4);
INSERT INTO `test1`.`user_role_mapping` (`id`, `user_id`, `role_id`) VALUES (7, 7, 3);
INSERT INTO `test1`.`user_role_mapping` (`id`, `user_id`, `role_id`) VALUES (8, 1, 2);
INSERT INTO `test1`.`user_role_mapping` (`id`, `user_id`, `role_id`) VALUES (9, 1, 4);
INSERT INTO `test1`.`user_role_mapping` (`id`, `user_id`, `role_id`) VALUES (10, 2, 4);
下面新建 SpringBoot
项目,在 pom
中引入以下依赖:
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.1</version>
</dependency><dependency><groupId>com.github.yulichang</groupId><artifactId>mybatis-plus-join-boot-starter</artifactId><version>1.4.7</version>
</dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId>
</dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.6</version>
</dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId>
</dependency>
application.yml
配置信息如下:
server:port: 8010spring:datasource:url: jdbc:mysql://127.0.0.1:3306/test1?useUnicode=true&characterEncoding=utf8&serverTimezone=GMTusername: rootpassword: roottype: com.alibaba.druid.pool.DruidDataSource
mybatis-plus:mapper-locations: classpath:mapper/*.xmltype-aliases-package: com.bxc.mybatisplusjoin.domain.entityconfiguration:log-impl: org.apache.ibatis.logging.stdout.StdOutImplmap-underscore-to-camel-case: trueglobal-config:db-config:logic-delete-field: deleteFlag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)logic-delete-value: 1 # 逻辑已删除值(默认为 1)logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)mybatis-plus-join:#是否打印 mybatis plus join banner 默认truebanner: true#全局启用副表逻辑删除(默认true) 关闭后关联查询不会加副表逻辑删除sub-table-logic: true#拦截器MappedStatement缓存(默认true)ms-cache: true#表别名(默认 t)table-alias: t#副表逻辑删除条件的位置,支持where、on默认 where (1.4.4+)logic-del-type: where
启动类上增加 mapper
扫描注解:
@MapperScan("com.bxc.mybatisplusjoin.mapper")
下面通过 MyBatisX
生成 entity、mapper、service
,不了解 MyBatisX
可以参考下面链接:
https://baomidou.com/pages/ba5b24/
生成的代码是基于原生的 mybatis-plus
的,需要简单修改下:
首先对于 mapper
将继承 BaseMapper
替换成 MPJBaseMapper
:
使用此方式修改其他 mapper
,然后对于 service
,将继承 IService
替换成 MPJBaseService
:
同样对于 Impl
实现也需要将 ServiceImpl
需改为 MPJBaseServiceImpl
:
下面就可以进行关联查询了:
例如:查询用户和角色信息
先创建 vo 类:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserRoleVO {private Long userId;private String name;private Long roleId;private String roleName;}
关联查询
@Slf4j
@SpringBootTest
class MybatisPlusJoinApplicationTests {@ResourceUserService userService;// 查询用户和角色信息@Testvoid contextLoads() {List<UserRoleVO> userRoleVOS = userService.selectJoinList(UserRoleVO.class,new MPJLambdaWrapper<User>().select(User::getId, User::getName).selectAs(User::getId, UserRoleVO::getUserId).selectAs(User::getName, UserRoleVO::getName).innerJoin(UserRoleMapping.class, UserRoleMapping::getUserId, User::getId).innerJoin(Role.class, Role::getId, UserRoleMapping::getRoleId).select(Role::getId, Role::getRoleName).selectAs(Role::getId, UserRoleVO::getRoleId).selectAs(Role::getRoleName, UserRoleVO::getRoleName));userRoleVOS.forEach(vo-> log.info(vo.toString()));}}
运行结果:
生成的 SQL:
SELECT t.id,t.name,t.id AS userId,t.name AS name,t2.id,t2.role_name,t2.id AS roleId,t2.role_name AS roleName FROM user t INNER JOIN user_role_mapping t1 ON (t1.user_id = t.id) INNER JOIN role t2 ON (t2.id = t1.role_id) WHERE t.delete_flag='0' AND t2.delete_flag='0'
或者通过写简单SQL
的方式调用,如:
@Slf4j
@SpringBootTest
class MybatisPlusJoinApplicationTests {@ResourceUserService userService;// 查询用户和角色信息@Testvoid contextLoads() {List<UserRoleVO> userRoleVOS = userService.selectJoinList(UserRoleVO.class,new MPJQueryWrapper<User>().setAlias("u").select("u.id AS userId","u.name").innerJoin("user_role_mapping m ON u.id = m.user_id").innerJoin("role r ON m.role_id = r.id").select("r.id AS roleId","r.role_name"));userRoleVOS.forEach(vo-> log.info(vo.toString()));}}
运行结果:
生成的SQL
:
SELECT u.id AS userId,u.name,r.id AS roleId,r.role_name FROM user u INNER JOIN user_role_mapping m ON u.id = m.user_id INNER JOIN role r ON m.role_id = r.id WHERE u.delete_flag='0'
例如:查看 admin
角色下的用户信息:
@Slf4j
@SpringBootTest
class MybatisPlusJoinApplicationTests {@ResourceUserService userService;// 查看 `admin` 角色下的用户信息@Testvoid findAdamUsers() {List<User> users = userService.selectJoinList(User.class,new MPJLambdaWrapper<User>().selectAll(User.class).innerJoin(UserRoleMapping.class, UserRoleMapping::getUserId, User::getId).innerJoin(Role.class, Role::getId, UserRoleMapping::getRoleId).eq(Role::getRoleName, "admin"));users.forEach(u -> log.info(u.toString()));}}
运行结果:
生成的SQL
:
SELECT t.id,t.name,t.age,t.email,t.username,t.password,t.status,t.delete_flag FROM user t INNER JOIN user_role_mapping t1 ON (t1.user_id = t.id) INNER JOIN role t2 ON (t2.id = t1.role_id) WHERE t.delete_flag='0' AND t2.delete_flag='0' AND (t2.role_name = ?)
例如:查看和小明
同角色的用户信息,需要两次关联 user
表:
@Slf4j
@SpringBootTest
class MybatisPlusJoinApplicationTests {@ResourceUserService userService;// 查看和`小明`同角色的用户信息@Testvoid findUsers() {List<User> users = userService.selectJoinList(User.class,new MPJLambdaWrapper<User>().innerJoin(UserRoleMapping.class, "m1", UserRoleMapping::getUserId, User::getId).innerJoin(UserRoleMapping.class, "m2", UserRoleMapping::getRoleId, UserRoleMapping::getRoleId).innerJoin(User.class, "u2", User::getId, UserRoleMapping::getUserId).eq("t.name", "小明").ne("u2.name","小明").selectAll(User.class,"u2"));users.forEach(u -> log.info(u.toString()));}}
运行结果:
生成的SQL
:
SELECT u2.id,u2.name,u2.age,u2.email,u2.username,u2.password,u2.status,u2.delete_flag FROM user t INNER JOIN user_role_mapping m1 ON (m1.user_id = t.id) INNER JOIN user_role_mapping m2 ON (m2.role_id = m1.role_id) INNER JOIN user u2 ON (u2.id = m2.user_id) WHERE t.delete_flag='0' AND u2.delete_flag='0' AND (t.name = ? AND u2.name <> ?)
或者使用 SQL
的写法:
@Slf4j
@SpringBootTest
class MybatisPlusJoinApplicationTests {@ResourceUserService userService;// 查看和`小明`同角色的用户信息@Testvoid findUsers() {List<User> users = userService.selectJoinList(User.class,new MPJQueryWrapper<User>().setAlias("u1").innerJoin("user_role_mapping m1 ON u1.id = m1.user_id").innerJoin("user_role_mapping m2 ON m1.role_id = m2.role_id").innerJoin("user u2 ON m2.user_id = u2.id").eq("u1.name", "小明").ne("u2.name","小明").selectAll(User.class, "u2"));users.forEach(u -> log.info(u.toString()));}}
运行后可以得到相同的结果。