一、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: 8010
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/test1?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT
username: root
password: root
type: com.alibaba.druid.pool.DruidDataSource
mybatis-plus:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.bxc.mybatisplusjoin.domain.entity
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
map-underscore-to-camel-case: true
global-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 默认true
banner: 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 {
@Resource
UserService userService;
// 查询用户和角色信息
@Test
void 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 {
@Resource
UserService userService;
// 查询用户和角色信息
@Test
void 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 {
@Resource
UserService userService;
// 查看 `admin` 角色下的用户信息
@Test
void 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 {
@Resource
UserService userService;
// 查看和`小明`同角色的用户信息
@Test
void 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 {
@Resource
UserService userService;
// 查看和`小明`同角色的用户信息
@Test
void 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()));
}
}
运行后可以得到相同的结果。