背景:
如果对一些评论、点赞、收藏等互动数据,使用了按照 type 分类存储,num 也是对应的。
这样如果创建一个帖子,那么就会出现 3 行数据(type 不同,num 不同,对应评论点赞和收藏),那如果我返回给前端,只想返回一行。
即 3 行数据转化为 3 列属性(评论、点赞、收藏)。
本文就是一个项目中的一个举例,不再单独举例子。
这里只关注了 sql编写,到 mapper 中怎么写,关于中间接口的定义,就不再赘述。
前端需要的字段都在,AlbumListVo
中,不代表所有的都在一个数据表中,所以需要联查才可以。
java
@Data
@Schema(description = "专辑信息")
public class AlbumInfoVo {
@NotEmpty(message = "专辑标题不能为空")
@Length(min = 2, message = "专辑标题的长度必须大于2")
@Schema(description = "标题", required=true)
private String albumTitle;
@Positive(message = "三级分类不能为空")
@Schema(description = "三级分类id", required=true)
private Long category3Id;
@NotEmpty(message = "专辑简介不能为空")
@Schema(description = "专辑简介", required=true)
private String albumIntro;
@NotEmpty(message = "专辑封面不能为空")
@Schema(description = "专辑封面图", required=true)
private String coverUrl;
@Schema(description = "预计更新多少集")
private Integer estimatedTrackCount;
@Schema(description = "专辑简介,富文本")
private String albumRichIntro;
@NotEmpty(message = "付费类型不能为空")
@Schema(description = "付费类型: 0101-免费、0102-vip免费、0103-付费", required=true)
private String payType;
@Schema(description = "价格类型: 0201-单集 0202-整专辑")
private String priceType;
@Schema(description = "原价")
@JsonSerialize(using = Decimal2Serializer.class)
private BigDecimal price;
@Schema(description = "0.1-9.9 不打折 -1")
@JsonSerialize(using = Decimal2Serializer.class)
private BigDecimal discount = new BigDecimal(-1);
@Schema(description = "0.1-9.9 不打折 -1")
@JsonSerialize(using = Decimal2Serializer.class)
private BigDecimal vipDiscount = new BigDecimal(-1);
@Schema(description = "免费试听集数")
private Integer tracksForFree;
@Schema(description = "每集免费试听秒数")
private Integer secondsForFree;
@Schema(description = "购买须知,富文本")
private String buyNotes;
@Schema(description = "专辑卖点,富文本")
private String sellingPoint;
@Schema(description = "是否公开:0-否 1-是")
private String isOpen;
//递归校验
//@Valid
//@NotEmpty(message = "属性值集合不能为空")
@Schema(description = "属性值集合")
private List<AlbumAttributeValueVo> albumAttributeValueVoList;
@NotEmptyPaid(message = "价格类型不能为空")
public String getPayTypeAndPriceType() {
return this.getPayType() + "_" + this.getPriceType();
}
@NotEmptyPaid(message = "价格不能为空")
public String getPayTypeAndPrice() {
return this.getPayType() + "_" + this.getPrice();
}
}
在 sql 里面查关联表。
不同维度,你是4 行,但是我想要 1593 的4列属性,最后封装在一个Vo
实体类中
sql
select
album_info.id as albumId,
album_info.album_title,
album_info.cover_url,
album_info.include_track_count,
album_info.is_finished,
album_info.status,
album_stat.stat_num,
album_stat.stat_type
from album_info
inner join album_stat
on album_stat.album_id=album_info.id
where album_info.user_id = 19;

对于同一个albumId
应该所有的属性在一行中,所以我们对他们进行分组一下
但是分组是有要求的:
分组划分的这一列必须是表的主键,不是就爆炸
这里首先以单个表来演示:
sql
select
album_info.id as albumId,
album_info.album_title
from album_info
where album_info.user_id = 19
group by album_info.id;

如果不是主键列进行分组
sql
select
album_info.id as albumId,
album_info.album_title
from album_info
where album_info.user_id = 19
group by album_info.user_id;

按照分组的列查询,但是如果有的了后面任意
sql
select
album_info.user_id
这加其他列也一样报错
from album_info
where album_info.user_id = 19
group by album_info.user_id;
分组的规则:
mysql5.7 后有一种模式,sql规范的模式:sql_mode,其中涉及到分组
单表
分组是主键,select 后面可以跟这个表中的任意列
不是主键 select 只能跟上分组的列,以及其他列的聚合函数处理后的结果
多表
即使是主键分组,依然需要聚合函数处理
这个跟了主键,但是加了聚合函数是成功了
sql
select
album_info.user_id,
count(album_info.id)
from album_info
where album_info.user_id = 19
group by album_info.user_id;
那如果是多表呢?做聚合函数
这里是个多表,哪怕是主键,后面有其他表,依然得用聚合函数!
那怎么去映射呢 4 行变 4列
我们这里用到了一个 if 函数,最后还是套聚合函数!
sql
select
album_info.id as albumId,
album_info.album_title,
album_info.cover_url,
album_info.include_track_count,
album_info.is_finished,
album_info.status,
count(if(album_stat.stat_type='0401', album_stat.stat_num, 0)) as playStatNum
from album_info
inner join album_stat
on album_stat.album_id=album_info.id
where album_info.user_id = 19
group by album_info.id;
这里只是显现了一行,但是如果 4 行转化也是同样的

因为要做一个分页,我们再进行一个排序。
sql
order by album_info.update_time desc
编码实现
转到 mapper.xml 中写 sql,对写的sql 进行一个简单的更改
这里没去校验 userId 是因为这个前面使用了 TingshuLogin,不登录肯定不行
AlbumInfoMapper
XML
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.anran.tingshu.album.mapper.AlbumInfoMapper">
<select id="findUserAlbumPage" resultType="com.anran.tingshu.vo.album.AlbumInfoVo">
select
album_info.id as albumId,
album_info.album_title,
album_info.cover_url,
album_info.include_track_count,
album_info.is_finished,
album_info.status,
max(if(album_stat.stat_type='0401', album_stat.stat_num, 0)) as playStatNum,
max(if(album_stat.stat_type='0402', album_stat.stat_num, 0)) as subscribeStatNum,
max(if(album_stat.stat_type='0403', album_stat.stat_num, 0)) as buyStatNum,
max(if(album_stat.stat_type='0404', album_stat.stat_num, 0)) as commentsStatNum
from album_info
inner join album_stat
on album_stat.album_id=album_info.id
<where>
<if test="vo.albumTitle != null and vo.albumTitle != ''">
and album_info.album_title=#{vo.albumTitle}
</if>
<if test="vo.status != null and vo.status != ''">
and album_info.status=#{vo.status}
</if>
and album_info.user_id=#{vo.userId} and album_info.is_deleted=0
</where>
group by album_info.id
order by album_info.update_time desc
</select>
</mapper>
- 因为不需要自定义数据,在 sql 方面完成了封装,所以不需要 mybatis 封装 ,所以直接返回值就是
resultType
而不是resultMap
