🎡Mybatis多表查询(1:1、1:N、N:N),MP多表查询(自定义SQL)

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},其实就是在代码中拼接完where条件后给到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>

 

  

 

 

posted @ 2023-09-20 20:17  Java小白的搬砖路  阅读(452)  评论(0编辑  收藏  举报