Mybatis多表关联查询
Gitee地址:https://gitee.com/zhang-zhixi/mybatis-tables-query.git
数据表:oracle
CREATE TABLE "T_ORDER" (
"ID" NUMBER NOT NULL,
"F_ORDER_TIME" DATE,
"F_TOTAL" VARCHAR2(255 BYTE),
"F_USER_ID" NUMBER
);
COMMENT ON COLUMN "ZHANGZHIXI"."T_ORDER"."F_ORDER_TIME" IS '下单时间';
COMMENT ON COLUMN "ZHANGZHIXI"."T_ORDER"."F_TOTAL" IS '下单金额';
COMMENT ON COLUMN "ZHANGZHIXI"."T_ORDER"."F_USER_ID" IS '下单用户';
COMMENT ON TABLE "ZHANGZHIXI"."T_ORDER" IS '用户订单表';
INSERT INTO "T_ORDER" VALUES ('2', TO_DATE('2023-09-18 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'), '2600', '1');
INSERT INTO "T_ORDER" VALUES ('1', TO_DATE('2023-09-18 09:50:23', 'SYYYY-MM-DD HH24:MI:SS'), '3500', '1');
INSERT INTO "T_ORDER" VALUES ('3', TO_DATE('2023-08-31 12:47:43', 'SYYYY-MM-DD HH24:MI:SS'), '2800', '2');
-- ------------------------------------------------------------------------------------------------------------------------
CREATE TABLE "T_ROLE" (
"ID" NUMBER NOT NULL,
"F_ROLE_NAME" VARCHAR2(255 BYTE)
);
COMMENT ON COLUMN "T_ROLE"."F_ROLE_NAME" IS '角色名称';
COMMENT ON TABLE "T_ROLE" IS '用户角色表';
INSERT INTO "ZHANGZHIXI"."T_ROLE" VALUES ('1', '董事长');
INSERT INTO "ZHANGZHIXI"."T_ROLE" VALUES ('2', '业务经理');
INSERT INTO "ZHANGZHIXI"."T_ROLE" VALUES ('3', '部门经理');
INSERT INTO "ZHANGZHIXI"."T_ROLE" VALUES ('4', '公司员工');
-- ------------------------------------------------------------------------------------------------------------------------
CREATE TABLE "T_USER" (
"ID" NUMBER NOT NULL,
"F_NAME" VARCHAR2(255 BYTE),
"F_AGE" VARCHAR2(255 BYTE),
"F_SEX" VARCHAR2(255 BYTE),
"F_ADDRESS" VARCHAR2(255 BYTE)
);
COMMENT ON COLUMN "T_USER"."F_NAME" IS '用户名';
COMMENT ON COLUMN "T_USER"."F_AGE" IS '年龄';
COMMENT ON COLUMN "T_USER"."F_SEX" IS '性别';
COMMENT ON COLUMN "T_USER"."F_ADDRESS" IS '地址';
COMMENT ON TABLE "T_USER" IS '用户表';
INSERT INTO "ZHANGZHIXI"."T_USER" VALUES ('2', '李四', '24', '男', '北京丰台');
INSERT INTO "ZHANGZHIXI"."T_USER" VALUES ('1', '张三', '23', '男', '北京朝阳');
-- ------------------------------------------------------------------------------------------------------------------------
CREATE TABLE "T_USER_ROLE" (
"ID" NUMBER NOT NULL,
"F_USER_ID" NUMBER,
"F_ROLE_ID" NUMBER
);
COMMENT ON COLUMN "T_USER_ROLE"."F_USER_ID" IS '用户ID';
COMMENT ON COLUMN "T_USER_ROLE"."F_ROLE_ID" IS '角色ID';
COMMENT ON TABLE "T_USER_ROLE" IS '用户与角色关联表';
INSERT INTO "T_USER_ROLE" VALUES ('1', '1', '1');
INSERT INTO "T_USER_ROLE" VALUES ('2', '2', '2');
INSERT INTO "T_USER_ROLE" VALUES ('3', '2', '3');
INSERT INTO "T_USER_ROLE" VALUES ('4', '1', '4');
INSERT INTO "T_USER_ROLE" VALUES ('5', '2', '4');
数据表
一对一 1:1(一个订单对应一个用户)
用户表和订单表的关系为:一个用户有多个订单,一个订单只从属于一个用户
一对一查询的需求:查询一个订单,与此同时查询出该订单所属的用户
1、SQL语句
SELECT o.*,u.*FROM T_ORDER o,T_USER u WHERE o.F_USER_ID=u.ID
2、实体
Order
@TableName(value ="T_ORDER")
@Data
public class Order implements Serializable {
/**
*
*/
@TableId(value = "ID")
private Long id;
/**
* 下单时间
*/
@JsonDeserialize(using = LocalDateTimeDeserializer.class)// 反序列化
@JsonSerialize(using = LocalDateTimeSerializer.class)// 序列化
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")// 对入参进行格式化
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")// 对出参进行格式化
@TableField(value = "F_ORDER_TIME")
private LocalDateTime orderTime;
/**
* 下单金额
*/
@TableField(value = "F_TOTAL")
private String total;
/**
* 下单用户
*/
@TableField(value = "F_USER_ID")
private Long userId;
@TableField(exist = false)
private static final long serialVersionUID = 1L;
}
User
@TableName(value = "T_USER")
@Data
public class User implements Serializable {
/**
* 主键
*/
@TableId(value = "ID")
private Long id;
/**
* 用户名
*/
@TableField(value = "F_NAME")
private String name;
/**
* 年龄
*/
@TableField(value = "F_AGE")
private String age;
/**
* 性别
*/
@TableField(value = "F_SEX")
private String sex;
/**
* 地址
*/
@TableField(value = "F_ADDRESS")
private String address;
@TableField(exist = false)
private static final long serialVersionUID = 1L;
}
OrderDTO
/**
* @author zhangzhixi
* @version 1.0
* @description 订单DTO
* @date 2023-09-18 9:52
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class OrderDTO extends Order {
/**
* 下单用户
*/
User user;
}
3、创建Mapper接口
/**
* 查询所有订单,一个订单对应一个用户
* @return 订单列表
*/
List<OrderDTO> findAll();
4、配置OrderMapper.xml
<select id="findAll" resultMap="orderMapTo1">
SELECT *
FROM T_ORDER o,
T_USER u
WHERE o.F_USER_ID = u.ID
</select>
<resultMap id="orderMapTo1" type="orderDTO">
<!--手动指定字段与实体属性的映射关系
column: 数据表的字段名称
property:实体的属性名称
-->
<id column="ID" property="id"/>
<result column="F_ORDER_TIME" property="orderTime"/>
<result column="F_TOTAL" property="total"/>
<result column="F_USER_ID" property="userId"/>
<result column="ID" property="user.id"/>
<result column="F_NAME" property="user.name"/>
<result column="F_AGE" property="user.age"/>
<result column="F_SEX" property="user.sex"/>
<result column="F_ADDRESS" property="user.address"/>
</resultMap>
<resultMap id="orderMapTo2" type="orderDTO">
<!--手动指定字段与实体属性的映射关系
column: 数据表的字段名称
property:实体的属性名称
-->
<id column="ID" property="id"/>
<result column="F_ORDER_TIME" property="orderTime"/>
<result column="F_TOTAL" property="total"/>
<result column="F_USER_ID" property="userId"/>
<!--
property: 当前实体(order)中的属性名称(private User user)
javaType: 当前实体(order)中的属性的类型(User)
-->
<association property="user" javaType="user">
<id column="ID" property="id"/>
<result column="F_NAME" property="name"/>
<result column="F_AGE" property="age"/>
<result column="F_SEX" property="sex"/>
<result column="F_ADDRESS" property="address"/>
</association>
</resultMap>
5、测试接口
[
{
"id": 2,
"orderTime": "2023-09-18 00:00:00",
"total": "2600",
"userId": 1,
"user": {
"id": 2,
"name": "张三",
"age": "23",
"sex": "男",
"address": "北京朝阳"
}
},
{
"id": 1,
"orderTime": "2023-09-18 09:50:23",
"total": "3500",
"userId": 1,
"user": {
"id": 1,
"name": "张三",
"age": "23",
"sex": "男",
"address": "北京朝阳"
}
},
{
"id": 3,
"orderTime": "2023-08-31 12:47:43",
"total": "2800",
"userId": 2,
"user": {
"id": 3,
"name": "李四",
"age": "24",
"sex": "男",
"address": "北京丰台"
}
}
]
一对多 1:N(一个用户拥有多个订单)
用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户
一对多查询的需求:查询一个用户,与此同时查询出该用户具有的订单
1、SQL语句
SELECT u.ID as ID,
u.F_NAME as F_NAME,
u.F_AGE as F_AGE,
u.F_SEX as F_SEX,
u.F_ADDRESS as F_ADDRESS,
o.ID as O_ID,
o.F_ORDER_TIME as F_ORDER_TIME,
o.F_TOTAL as F_TOTAL,
o.F_USER_ID as F_USER_ID
FROM T_USER u
LEFT JOIN T_ORDER o
ON o.F_USER_ID = u.id
2、实体
UserOrderDTO
/**
* @author zhangzhixi
* @version 1.0
* @description 一个用户对应多个订单
* @date 2023-09-18 12:42
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class UserOrderDTO extends User {
/**
* 1:N,一个用户对应多个订单
*/
List<Order> orders;
}
3、UserMapper
/**
* 查询用户订单列表,一个用户对应多个订单
* @return 用户订单列表
*/
List<UserOrderDTO> findUserOrderList();
4、UserMapper.xml
<select id="findUserOrderList" resultMap="userOrderList">
SELECT u.ID as ID,
u.F_NAME as F_NAME,
u.F_AGE as F_AGE,
u.F_SEX as F_SEX,
u.F_ADDRESS as F_ADDRESS,
o.ID as O_ID,
o.F_ORDER_TIME as F_ORDER_TIME,
o.F_TOTAL as F_TOTAL,
o.F_USER_ID as F_USER_ID
FROM T_USER u
LEFT JOIN T_ORDER o
ON o.F_USER_ID = u.id
</select>
<!--查询用户表,一个用户有多个订单信息-->
<resultMap id="userOrderList" type="UserOrderDTO">
<id property="id" column="ID"/>
<result property="name" column="F_NAME"/>
<result property="age" column="F_AGE"/>
<result property="sex" column="F_SEX"/>
<result property="address" column="F_ADDRESS"/>
<collection property="orders" ofType="order">
<id property="id" column="O_ID"/>
<result property="orderTime" column="F_ORDER_TIME"/>
<result property="total" column="F_TOTAL"/>
<result property="userId" column="F_USER_ID"/>
</collection>
</resultMap>
5、测试接口:一个用户可以有多个订单
[
{
"id": 1,
"name": "张三",
"age": "23",
"sex": "男",
"address": "北京朝阳",
"orders": [
{
"id": 2,
"orderTime": "2023-09-18 00:00:00",
"total": "2600",
"userId": 1
},
{
"id": 1,
"orderTime": "2023-09-18 09:50:23",
"total": "3500",
"userId": 1
}
]
},
{
"id": 2,
"name": "李四",
"age": "24",
"sex": "男",
"address": "北京丰台",
"orders": [
{
"id": 3,
"orderTime": "2023-08-31 12:47:43",
"total": "2800",
"userId": 2
}
]
}
]
多对多 N:N(一个用户拥有多个角色,一个角色可以被多个用户使用)
用户表和角色表的关系为,一个用户有多个角色,一个角色被多个用户使用
多对多查询的需求:一个用户可以有多个角色,一个角色可以被多个用户拥有
1、SQL
SELECT
u.ID "U_ID",
u.F_NAME "U_NAME",
u.F_AGE "U_AGE",
u.F_SEX "U_SEX",
u.F_ADDRESS "U_ADDRESS",
r.F_ROLE_NAME "ROLE_NAME"
FROM
T_USER u
LEFT JOIN T_USER_ROLE ur ON u.ID = ur.F_USER_ID
LEFT JOIN T_ROLE r ON ur.F_ROLE_ID = r.ID
ORDER BY
U_NAME DESC
2、实体
Role
@TableName("T_ROLE")
@Data
public class Role {
/**
* 主键
*/
@TableId(value = "ID")
private Long id;
/**
* 角色名称
*/
@TableField(value = "F_ROLE_NAME")
private String roleName;
}
UserRole
@TableName("T_USER_ROLE")
@Data
public class UserRole {
/**
* 主键
*/
@TableId(value = "ID")
private Long id;
@TableField(value = "F_USER_ID")
private Long userId;
@TableField(value = "F_ROLE_ID")
private Long roleId;
}
RoleDTO
@TableName("T_ROLE")
@Data
public class RoleDTO {
/**
* 角色名称
*/
@TableField(value = "F_ROLE_NAME")
private String roleName;
}
UserRoleDTO
@EqualsAndHashCode(callSuper = true)
@Data
public class UserRoleDTO extends User {
/**
* 用户角色列表,一个用户可以对应多个角色
*/
List<RoleDTO> roleList;
}
3、UserMapper接口
/**
* 查询用户角色列表,一个用户对应多个角色,一个角色对应多个用户
* @return 用户角色列表
*/
List<UserRoleDTO> findUserRoleList();
4、UserMapper.xml
<select id="findUserRoleList" resultMap="userRoleList">
SELECT u.ID "U_ID",
u.F_NAME "U_NAME",
u.F_AGE "U_AGE",
u.F_SEX "U_SEX",
u.F_ADDRESS "U_ADDRESS",
r.F_ROLE_NAME "ROLE_NAME"
FROM T_USER u
LEFT JOIN T_USER_ROLE ur ON u.ID = ur.F_USER_ID
LEFT JOIN T_ROLE r ON ur.F_ROLE_ID = r.ID
ORDER BY U_NAME DESC
</select>
<resultMap id="userRoleList" type="UserRoleDTO">
<id property="id" column="U_ID"/>
<result property="name" column="U_NAME"/>
<result property="age" column="U_AGE"/>
<result property="sex" column="U_SEX"/>
<result property="address" column="U_ADDRESS"/>
<collection property="roleList" ofType="RoleDTO">
<result property="roleName" column="ROLE_NAME"/>
</collection>
</resultMap>
5、测试接口
[
{
"id": 1,
"name": "张三",
"age": "23",
"sex": "男",
"address": "北京朝阳",
"roleList": [
{
"roleName": "董事长"
},
{
"roleName": "公司员工"
}
]
},
{
"id": 2,
"name": "李四",
"age": "24",
"sex": "男",
"address": "北京丰台",
"roleList": [
{
"roleName": "公司员工"
},
{
"roleName": "业务经理"
},
{
"roleName": "部门经理"
}
]
}
]
MybatisPlus多表关联查询
案例一:单表查询(带出自定义SQL的使用)
需求:按照订单ID扣减订单余额
update T_ORDER set F_TOTAL = F_TOTAL - 200.0 WHERE (id IN (1,2))
通常写法:
/**
*
* @param modifyTheAmount 修改的金额
* @param ids 订单id列表
* @return >0,更新成功
*/
@Override
public int updateOrderTotalPrice(Double modifyTheAmount, List<Long> ids) {
if (modifyTheAmount == null || ids.isEmpty()) {
throw new RuntimeException("用户编号和需要扣减的余额不能为空");
}
LambdaUpdateWrapper<Order> orderLambdaUpdateWrapper = Wrappers.lambdaUpdate(Order.class);
orderLambdaUpdateWrapper
.setSql("F_TOTAL = F_TOTAL -" + modifyTheAmount)
.in(Order::getId,ids);
return orderMapper.update(null,orderLambdaUpdateWrapper);
}
使用自定义SQL
MybatisPlus提供了自定义SQL功能,可以让我们利用Wrapper生成查询条件,再结合Mapper.xml编写SQL
Service层拼接条件,将set语句要做的东操作,放在xml中:
/**
* @param modifyTheAmount 修改的金额
* @param ids 订单id列表
* @return >0,更新成功
*/
@Override
public int updateOrderTotalPrice(Double modifyTheAmount, List<Long> ids) {
if (modifyTheAmount == null || ids.isEmpty()) {
throw new RuntimeException("用户编号和需要扣减的余额不能为空");
}
// 按照订单ID修改订单余额
QueryWrapper<Order> orderQueryWrapper = new QueryWrapper<Order>().in("id", ids);
int updateResult = orderMapper.updateOrderTotalPrice(modifyTheAmount, orderQueryWrapper);
if (updateResult == 0) {
throw new RuntimeException("更新失败");
}
return updateResult;
}
OrderMapper.java
需要注意的是,编写自定义SQL,@Params中的参数必须是ew,接收参数使用:${ew.customSqlSegment},其实就是在代码中拼接完wher条件后给到xml
/**
* 更新订单总价
*
* @param modifyTheAmount 修改的金额
* @param queryWrapper 查询条件
*/
int updateOrderTotalPrice(@Param("amount") Double modifyTheAmount, @Param("ew") QueryWrapper<Order> queryWrapper);
OrderMapper.xml
<update id="updateOrderTotalPrice">
update T_ORDER
set F_TOTAL = F_TOTAL - #{amount}
${ew.customSqlSegment}
</update>
案例二:多表查询
需求:一个用户拥有多个订单信息,添加查询条件进行筛选指定订单信息
SELECT
u.ID U_ID,
u.F_NAME U_NAME,
u.F_AGE U_AGE,
u.F_SEX U_SEX,
u.F_ADDRESS U_ADDRESS,
o.ID O_ID,
o.F_TOTAL O_TOTAL
FROM
T_USER u
LEFT JOIN T_ORDER o ON u.ID = o.F_USER_ID
WHERE
( o.F_TOTAL >= 1500 AND u.F_NAME = '张三' )
Service层拼接SQL
public List<UserOrderDTO> findUserOrderListByCondition() {
// 拼接查询条件
QueryWrapper<UserOrderDTO> userOrderQueryWrapper = new QueryWrapper<>(UserOrderDTO.class)
.ge("o.F_TOTAL", 1500)
.eq("u.F_NAME", "张三");
return userMapper.findUserOrderListByCondition(userOrderQueryWrapper);
}
UserMapper.java
/**
* 查询用户的所有订单,并添加查询条件
*
* @return 满足条件的用户及订单信息
*/
List<UserOrderDTO> findUserOrderListByCondition(@Param("ew") QueryWrapper<UserOrderDTO> wrapper);
UserMapper.xml
<select id="findUserOrderListByCondition" resultMap="findUserOrderListByConditionReaultMap">
SELECT
u.ID U_ID,
u.F_NAME U_NAME,
u.F_AGE U_AGE,
u.F_SEX U_SEX,
u.F_ADDRESS U_ADDRESS,
o.ID O_ID,
o.F_TOTAL O_TOTAL
FROM
T_USER u
LEFT JOIN T_ORDER o ON u.ID = o.F_USER_ID
${ew.customSqlSegment}
</select>
<resultMap id="findUserOrderListByConditionReaultMap" type="UserOrderDTO">
<id property="id" column="U_ID"/>
<result property="name" column="U_NAME"/>
<result property="age" column="U_AGE"/>
<result property="sex" column="U_SEX"/>
<result property="address" column="U_ADDRESS"/>
<collection property="orders" ofType="order">
<id property="id" column="O_ID"/>
<result property="total" column="O_TOTAL"/>
</collection>
</resultMap>