背景:
如果對一些評論、點贊、收藏等互動數據,使用了按照 type 分類存儲,num 也是對應的。
這樣如果創建一個帖子,那么就會出現 3 行數據(type 不同,num 不同,對應評論點贊和收藏),那如果我返回給前端,只想返回一行。
即 3 行數據轉化為 3 列屬性(評論、點贊、收藏)。
本文就是一個項目中的一個舉例,不再單獨舉例子。
這里只關注了 sql編寫,到 mapper 中怎么寫,關于中間接口的定義,就不再贅述。
前端需要的字段都在,AlbumListVo
中,不代表所有的都在一個數據表中,所以需要聯查才可以。
@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
實體類中
selectalbum_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_typefrom album_infoinner join album_stat
on album_stat.album_id=album_info.id
where album_info.user_id = 19;
對于同一個albumId
應該所有的屬性在一行中,所以我們對他們進行分組一下
但是分組是有要求的:
分組劃分的這一列必須是表的主鍵,不是就爆炸
這里首先以單個表來演示:
selectalbum_info.id as albumId,album_info.album_titlefrom album_info
where album_info.user_id = 19
group by album_info.id;
如果不是主鍵列進行分組
selectalbum_info.id as albumId,album_info.album_titlefrom album_info
where album_info.user_id = 19
group by album_info.user_id;
按照分組的列查詢,但是如果有的了后面任意
selectalbum_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 只能跟上分組的列,以及其他列的聚合函數處理后的結果
多表
????????即使是主鍵分組,依然需要聚合函數處理
????????這個跟了主鍵,但是加了聚合函數是成功了
selectalbum_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 函數,最后還是套聚合函數!
selectalbum_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 playStatNumfrom album_infoinner join album_stat
on album_stat.album_id=album_info.id
where album_info.user_id = 19
group by album_info.id;
這里只是顯現了一行,但是如果 4 行轉化也是同樣的
因為要做一個分頁,我們再進行一個排序。
order by album_info.update_time desc
編碼實現
轉到 mapper.xml 中寫 sql,對寫的sql 進行一個簡單的更改
這里沒去校驗 userId 是因為這個前面使用了 TingshuLogin,不登錄肯定不行
AlbumInfoMapper
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//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">selectalbum_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 commentsStatNumfrom album_infoinner join album_staton 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.idorder by album_info.update_time desc</select>
</mapper>
- 因為不需要自定義數據,在 sql 方面完成了封裝,所以不需要 mybatis 封裝 ,所以直接返回值就是
resultType
而不是resultMap