黑馬點評redis改 part 5

達人探店

發布探店筆記

那第一張表block表它里邊的結構呢是這個
首先呢第一個字段是i d,就是主鍵,第二個呢是shop id,就是商戶你發的這個比例啊,它是跟哪個商戶有關系的。第三個呢用戶id就是誰發的這篇筆記,第四個呢標題,第五個呢是照片,照片呢最多不超過九張,多個呢以道號隔開,所以呢他這個是一個字段,里面包含了多張圖片的
然后呢再往下呢content是探店的文字描述啊,然后再往下呢還有兩個,一個叫點贊的數量啊,還有一個是評論的數量啊,點贊了不一定會評論是吧,所以這兩個是分離去計數,再往下time update time,創建時間更新時間

?實際生產中不會加外鍵,這會給每次操作進行驗證,會大大降低性能,配了負載均衡的,記得啟動兩個服務,否則前端頁面顯示不全

?uploadcontrol實現了功能,而因此呢啊在這個地方,我們會定義一個叫做image upload dr文件上傳的地址,那么這個地址所以我們需要把它改成什么,我們當前的這個目錄啊。你需要找到你自己的index的目錄

package com.hmdp.utils;public class SystemConstants {public static final String IMAGE_UPLOAD_DIR = "D:\\lesson\\nginx-1.18.0\\html\\hmdp\\imgs\\";public static final String USER_NICK_NAME_PREFIX = "user_";public static final int DEFAULT_PAGE_SIZE = 5;public static final int MAX_PAGE_SIZE = 10;
}

?

成功上傳,也可以多 次上傳圖片啊

?

點擊發布,那么會自動跳到主頁里,在app首頁,我們可以看到,同樣的數據庫的tb blog也可以看得到這條消息

?實現查看發布探店筆記的接口

需求:點擊首頁的探店筆記,會進入詳情頁面,實現該頁面的查詢接口

好的,同學們,我們來繼續分析這個接口。首先,接口相關的信息我已經提前分析了一下。請求的方式是GET,這一點我們通過查看就能知道,路徑是/block,后面跟著的是id,這個也沒有問題。然后請求參數自然就是這個id了,也就是當前這篇博客的ID。最后返回值是什么呢?返回值根據id查詢這個博客(Blog),那返回值是不是應該就是Blog呀?理論上講就是如此,但是大家別忘了,其實我們發布的任何一篇探店筆記,它里邊都包含有這個用戶的信息。然后才是這個圖片,還有標題等等。

所以說我們在詳情頁面展示的時候,除了要展示這里的圖片、標題、內容以外,那這個發布這篇筆記的用戶是不是也應該展示出來,這樣其他用戶看到這篇博客以后,如果感興趣,是不是可以直接關注當前的用戶了。所以說呢,我們返回的結果中除了博客信息以外,還應該包含對應的用戶信息。那我們怎么樣才能在結果中包含兩部分內容呢?

那我們回到IDEA看一下,在這里實現起來其實非常的簡單,有兩種選擇。第一種選擇就是在我們的這個Blog類里邊加一個用戶的成員變量就行了。那這樣一來,我們只要把查詢到的跟這個用戶ID有關的這個用戶(User)的對象存進去,是不是就OK了?但在這呢,我采用了一種簡化的方法,怎么簡化呢?就是在我們的Blog類里邊加了兩個字段,你看這個博客類里面的其他字段,店鋪ID(shop id)、用戶ID(user id)、還有我們的標題、圖片、內容等等,這些都是數據庫字段。而唯獨這兩個字段一個叫圖標(icon)、一個叫姓名(name),這兩個就是我們的用戶字段了。

那我們的用戶除了有ID以外,還有就是圖標和姓名,剩下的敏感字段我們就不返回了,只返回這三個足夠頁面顯示就可以了。而這兩個字段呢我們加了@TableField注解,它代表的含義就是當前字段不屬于博客所定義的表,你表不是博客表嗎?而這兩個字段不在表里面,所以說我加了這樣一個注解,那將來呢我們手動的要維護這兩個字段就可以了。這樣大家應該就能理解了吧。

那下面呢我們就可以去實現一下了,我們找到博客控制器(BlogController),我們在這兒去寫這個接口。實現的時候,我們首先要根據id查詢到對應的博客(Blog)信息,然后根據博客信息中的用戶ID(user id),再去查詢用戶(User)信息。查詢到用戶信息后,我們將用戶信息添加到博客信息中,最后將包含用戶信息的博客信息返回給前端。剩下的敏感字段我們就不返回,只返回這三個足夠頁面顯示就可以了,而這兩個字段呢我們加了table field,exit等于false這樣一個注解,它代表的含義就是說當前字段不屬于blog它所定義的表,而我這倆字段不在表里面,寫control

package com.hmdp.controller;import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hmdp.dto.Result;
import com.hmdp.dto.UserDTO;
import com.hmdp.entity.Blog;
import com.hmdp.entity.User;
import com.hmdp.service.IBlogService;
import com.hmdp.service.IUserService;
import com.hmdp.utils.SystemConstants;
import com.hmdp.utils.UserHolder;
import org.springframework.web.bind.annotation.*;import javax.annotation.Resource;
import java.util.List;/*** <p>* 前端控制器* </p>** @author 虎哥* @since 2021-12-22*/
@RestController
@RequestMapping("/blog")
public class BlogController {@Resourceprivate IBlogService blogService;@PostMappingpublic Result saveBlog(@RequestBody Blog blog) {// 獲取登錄用戶UserDTO user = UserHolder.getUser();blog.setUserId(user.getId());// 保存探店博文blogService.save(blog);// 返回idreturn Result.ok(blog.getId());}@PutMapping("/like/{id}")public Result likeBlog(@PathVariable("id") Long id) {// 修改點贊數量blogService.update().setSql("liked = liked + 1").eq("id", id).update();return Result.ok();}@GetMapping("/of/me")public Result queryMyBlog(@RequestParam(value = "current", defaultValue = "1") Integer current) {// 獲取登錄用戶UserDTO user = UserHolder.getUser();// 根據用戶查詢Page<Blog> page = blogService.query().eq("user_id", user.getId()).page(new Page<>(current, SystemConstants.MAX_PAGE_SIZE));// 獲取當前頁數據List<Blog> records = page.getRecords();return Result.ok(records);}@GetMapping("/hot")public Result queryHotBlog(@RequestParam(value = "current", defaultValue = "1") Integer current) {return blogService.queryHotBlog(current);}@GetMapping("/{id}")public Result queryBlogById(@PathVariable("id") Long id) {return blogService.queryBlogById(id);}}

service加一下就行,impl寫一下如下

package com.hmdp.service.impl;import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hmdp.dto.Result;
import com.hmdp.entity.Blog;
import com.hmdp.entity.User;
import com.hmdp.mapper.BlogMapper;
import com.hmdp.service.IBlogService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.service.IUserService;
import com.hmdp.utils.SystemConstants;
import org.springframework.stereotype.Service;import javax.annotation.Resource;
import java.util.List;/*** <p>* 服務實現類* </p>** @author 虎哥* @since 2021-12-22*/
@Service
public class BlogServiceImpl extends ServiceImpl<BlogMapper, Blog> implements IBlogService {@Resourceprivate IUserService userService;@Overridepublic Result queryBlogById(Long id) {//1.查詢blogBlog blog = getById(id);if (blog == null) {return Result.fail("筆記不存在!");}//2.查詢bLog有關的用戶queryBlogUser(blog);return Result.ok(blog);}private void queryBlogUser(Blog blog) {Long userId = blog.getUserId();User user = userService.getById(userId);blog.setName(user.getNickName());blog.setIcon(user.getIcon());}@Overridepublic Result queryHotBlog(Integer current) {// 根據用戶查詢Page<Blog> page = query().orderByDesc("liked").page(new Page<>(current, SystemConstants.MAX_PAGE_SIZE));// 獲取當前頁數據List<Blog> records = page.getRecords();// 查詢用戶//lamrecords.forEach(this::queryBlogUser);return Result.ok(records);}
}

?

點贊

你發現一個人可以無限次的點贊,這是因為代碼直接數據庫中++,不做任何判斷限定

需求:

  • 同一個用戶只能點贊一次,再次點擊則取消點贊
  • 如果當前用戶已經點贊,則點贊按鈕高亮顯示(前端已實現,判斷字段Blog類的isLike屬性)

?由于需要記錄點贊人和被點贊人,還有點贊狀態(點贊、取消點贊),還要固定時間間隔取出 Redis 中所有點贊數據,分析了下 Redis 數據格式中 Hash 最合適。blog.java里面添加有這個(實際用的set)

/*** 是否點贊過了* boolean類型字段不能is打頭,阿里明文規定*/
@TableField(exist = false)
private Boolean isLike;

?如果業務量比較多可以做個定時同步,隔一段時間同步一次點贊信息到數據庫

    @PutMapping("/like/{id}")public Result likeBlog(@PathVariable("id") Long id) {return blogService.likeBlog(id);}

?修改或者添加這幾個函數

    @Overridepublic Result queryBlogById(Long id) {//1.查詢blogBlog blog = getById(id);if (blog == null) {return Result.fail("筆記不存在!");}//2.查詢bLog有關的用戶queryBlogUser(blog);//3.查詢blog是否被點贊isBlogLiked(blog);return Result.ok(blog);}private void isBlogLiked(Blog blog) {// 1. 獲取登錄用戶Long userId = UserHolder.getUser().getId();// 2. 判斷當前登錄用戶是否已經點贊String key = "blog:liked:" + blog.getId();Boolean isMember = stringRedisTemplate.opsForSet().isMember(key, userId.toString());blog.setIsLike(BooleanUtil.isTrue(isMember));}@Overridepublic Result queryHotBlog(Integer current) {// 根據用戶查詢Page<Blog> page = query().orderByDesc("liked").page(new Page<>(current, SystemConstants.MAX_PAGE_SIZE));// 獲取當前頁數據List<Blog> records = page.getRecords();// 查詢用戶records.forEach(blog  ->{this.queryBlogUser(blog);this.isBlogLiked(blog);});return Result.ok(records);}@Overridepublic Result likeBlog(Long id) {// 1. 獲取登錄用戶Long userId = UserHolder.getUser().getId();// 2. 判斷當前登錄用戶是否已經點贊String key = "blog:liked:" + id;Boolean isMember = stringRedisTemplate.opsForSet().isMember(key, userId.toString());if(BooleanUtil.isFalse(isMember)) {// 3. 如果未點贊,可以點贊// 3.1. 數據庫點贊數 + 1boolean isSuccess = update().setSql("liked = liked + 1").eq("id", id).update();// 3.2. 保存用戶到Redis的set集合if(isSuccess) {stringRedisTemplate.opsForSet().add(key,  userId.toString());//此處應該拷貝if(isSuccess)判斷數據更新成功才更新redis}}else {// 4. 如果已點贊,取消點贊// 4.1. 數據庫點贊數 - 1boolean isSuccess = update().setSql("liked = liked - 1").eq("id", id).update();//這里有線程安全問題,判斷和修改不是原子.也有事務的問題,update了兩條數據// 4.2. 把用戶從Redis的set集合移除stringRedisTemplate.opsForSet().remove(key, userId.toString());}return Result.ok();}/*如果A線程查到當前用戶沒點贊,沒等進行redis更新操作,B線程進來查詢redis當前用戶也沒進行點贊,就會贊兩次一般賬號都會設置限制在1-5臺設備左右登錄,本來并發度就不高,并且這類數據也不要求強一致性,安全問題還是很低很低的。*/

實現點贊和高亮

點贊排行榜

?

好的同學們,剛才我已經為大家總結了接口的信息,并且進行了梳理。可以看到,請求方式是GET請求,路徑是`/blog/likes`或者`/blog/likes/d`,這里的`d`可能是指“點贊”的意思,后面跟著的是當前這篇筆記的ID。我們要查的是給這篇筆記點贊的人,所以參數就是`blogId`了。返回的自然是點贊的前五名了,所以是一個集合,集合里裝的是用戶信息。這里我們用了`UserDTO`,因為`UserDTO`里把敏感信息都已經給去掉了,所以不用擔心數據泄漏的風險。在這個地方,我們要返回的這個集合就是所謂的前五名,也就是排行榜里面的前五名。但在這就有一個問題了,我上哪去查這前五名的用戶去,而且是點贊的用戶。

那同學們,我們點贊的信息都放在哪兒啊?而在上一節課當中,我們點贊是基于一個Redis的Set集合來實現的,也就是說我現在要想查詢這些用戶信息,我是不是得去Set集合來查?但是我要的是前五名,Set集合是所有的,那我就必須把Set集合的元素給它排個序啊。但是同學們,Set集合是有虛的嗎?顯然不是,我們當初為什么選Set集合呀,是因為當初我們的業務需求是:第一要存多個元素,也就是集合;第二點為一。那這兩點他都滿足呀。但現在我們又多了個需求,我們要做排行榜,所以我們的需求變成了三個需求了:要能夠存多個元素,要能為一,完了還能排序。那Set集合就不行了,我們該選誰?

哎,在這兒呢我們去做一個對比啊,目前為止我們學習的Redis集合總共就三種,那這三種都是集合,所以第一條是不是就滿足了,那么還剩兩條嗎,那一個就是排序,一個就是唯一。在這呢我給大家對比的就是這三點,哪個呢,第一排序,第二唯一性,第三個多了一個,我們要對比一下他們的查找元素時的方式。為什么呢?因為將來我們除了去做排序以外,我們還有一個需求就是點贊,點贊我們要判斷它存不存在,對不對,而你要判斷存不存在,不就是查找嗎?所以說這個也要去做對比。那來一個看啊,首先Set集合,它首先是可以做排序的,為什么呢?他是個鏈表嗎,那他按什么排,按照添加順序,還有`lpush`和`rpush`兩種,對不對?如果說我們所有的元素都是按`lpush`就插入的,那元素呢先插入進去的,是不是在最后,后插入的,在最前變成了一個按照插入順序的倒序排序,這個跟我們點贊排行榜不太相符,但是如果我們全部采用`rpush`呢,先點贊在最前邊,后點在最后邊,這樣是不是就剛好符合?所以說它是支持這種排序的啊,而Set集合就不好意思了,不支持啊,無法排序,而我們的Sorted Set,這個咱們之前是不是也學過呢?它可以排序的,按什么排按分數,也就是說我們存入`zset`的元素啊,除了元素本身以外,還要帶一個score,那就分數啊,那么這個score分數呢,可以是用戶自定義的任意的東西,那如果我把它按照時間戳啊作為score值存進去,那這樣一來是不是也能實現按添加順序排序了?那想添加越早,時間說是不是越小,那天下越晚,時間戳越大,這樣天然是不是就帶有一個順序了?因此Sorted Set能不能實現按照時間排序沒問題,這也就是說符合要求的是不是就有兩個啊,從排序上來講啊,那再從唯一性上來講啊,直接把list排除了啊,list是鏈表,無法保證數據的唯一啊,他只管一共要往里面加,但是呢Set和Sorted Set都滿足,因為他們底層都有一個哈希啊哈表,接下來它可以判斷元素是否存在,從而把一些重復元素給剔除或者是覆蓋,那從查找方式上來講呢,我們的list查找,我們剛才講過了,它底層是什么呢,是鏈表,所以說呢它只能按角標查找元素,或者是首尾查找啊,那意思我想知道一個元素存不存在,它的做法只能是便利一邊啊,但是Set和Sorted Set就不一樣了,那么這兩個呢因為他們底層采用的是哈希表,所以說他們可以根據元素做這種哈希運算,快速定位到對應的那個數組位置,然后呢去判斷是否存在,所以他們的查找更加高效,對不對,所以從這三點來看,哪個更適合啊,其實把前兩點看完就已經找到答案了,是不是Sorted Set更符合我們的業務需求?同學們,只要你掌握了啊這種Redis數據結構的特點,然后你再結合你的業務需求,是不是一目了然,就能快速定位到合適的數據類型?那這里呢我們就需要用Sorted Set來代替我們的Set集合,改造我們之前的點贊業務了,但是在這就有一個問題了,Sorted Set雖然跟Set類似,但是還是存在差異的,很多的命令上是不一樣的,那我們來看一下啊,在這呢我們之前使用這個Set的時候,我們去添加是`sadd`,對不對啊,`sadd`去添加一個元素,我們就判斷是否存在叫`sismember`,它可以直接判斷一個元素是否存在,但是現在我們用的是Sorted Set,Sorted Set里面就不存在啊,這個`sismember`這樣一個命令了,那它添加元素是一樣的,都是去`zadd`,但是呢他判斷元素是否存在,它沒有一個叫`sismember`的沒有,那怎么辦啊,那這里呢我們只能用一種別的方式啊,在這兒呢我們會使用一個叫做`zscore`的,意思是獲取指定一個元素對應的分數,那為什么用它可以判斷元素是否存在呢?我去獲取元素的分數,元素如果存在,返回的自然就是分數,元素不存在,返回的是不是就是空了啊,你看我們可以試一下,現在我們通過這個`zadd`啊去添加這個元素啊,比如說k叫`z1`啊,然后呢我一啊,這是分數嗎,`m1`,`m2`,`m3`,這樣我是不是一下添加了三個元素,好全部添加進去了,那接下來呢我們就用`zscore`啊去查看一下,這個`z1`里邊的`m1`這個元素,它的分數是不是查到了`m2`,查到了`m3`,查到了`m4`,不存在,看到沒有,返回的是不是`null`,也就是空,所以說我們完全可以通過查分數的形式查到了就存在查不到是不是不存在,來判斷元素是否存在,其他的就沒什么太大差別了啊,那當然最后我們要去查排行榜,我們怎么查,這個之前咱們講過,其實就用`zrange`,`range`是什么啊,`range`查詢的是按范圍查嗎,就是你要查找哪個范圍內的,它天然的會幫你做排序嘛,對不對,假如說我們按時間啊,時間戳插入,那么它會天然的按照時間戳從小到大排序,那顯然啊最早插入的是不是就在最前,那這時我們要查前五名,其實就是查什么呢,它里邊從0~4的這些啊,01234不剛好五個嘛,那就前五名啊,就這么來查的啊,所以呢這個將來我們要查前五名啊,也就知道該怎么做了。

127.0.0.1:6379> ZADD z1 1 m1 2 m2 3 m3
(integer) 3
127.0.0.1:6379> ZSCORE z1 m1
"1"
127.0.0.1:6379> ZSCORE z1 m3
"3"
127.0.0.1:6379> ZSCORE z1 m9
(nil)
127.0.0.1:6379> ZRANGE z1 0 4
1) "m1"
2) "m2"
3) "m3"
    @Overridepublic Result likeBlog(Long id) {// 1. 獲取登錄用戶Long userId = UserHolder.getUser().getId();// 2. 判斷當前登錄用戶是否已經點贊String key = "blog:liked:" + id;//Boolean isMember = stringRedisTemplate.opsForSet().isMember(key, userId.toString());Double score = stringRedisTemplate.opsForZSet().score(key, userId.toString());if(score == null) {// 3. 如果未點贊,可以點贊// 3.1. 數據庫點贊數 + 1boolean isSuccess = update().setSql("liked = liked + 1").eq("id", id).update();// 3.2. 保存用戶到Redis的set集合 zadd key value scoreif(isSuccess) {stringRedisTemplate.opsForZSet().add(key,  userId.toString(),System.currentTimeMillis());//此處應該拷貝if(isSuccess)判斷數據更新成功才更新redis}}else {// 4. 如果已點贊,取消點贊// 4.1. 數據庫點贊數 - 1boolean isSuccess = update().setSql("liked = liked - 1").eq("id", id).update();//這里有線程安全問題,判斷和修改不是原子.也有事務的問題,update了兩條數據// 4.2. 把用戶從Redis的set集合移除stringRedisTemplate.opsForSet().remove(key, userId.toString());}return Result.ok();}private void isBlogLiked(Blog blog) {// 1. 獲取登錄用戶Long userId = UserHolder.getUser().getId();// 2. 判斷當前登錄用戶是否已經點贊String key = "blog:liked:" + blog.getId();Double score = stringRedisTemplate.opsForZSet().score(key, userId.toString());blog.setIsLike(score!=null);}

這里記得改完調試的時候把之前Redis的緩存刪除了

神了,有的帖子可以取消贊,有的就不能,哦牛批

    @GetMapping("/likes/{id}")public Result queryBlogLikes(@PathVariable("id") Long id) {return blogService.queryBlogLikes(id);}
    //使用map方法將每個元素轉換為對應的Long類型,并使用collect方法將結果收集到一個List中@Overridepublic Result queryBlogLikes(Long id) {String key = BLOG_LIKED_KEY + id;// 1. 查詢top5的點贊用戶 zrange key 0 4Set<String> top5 = stringRedisTemplate.opsForZSet().range(key,0,4);if (top5 == null || top5.isEmpty()) {return Result.ok(Collections.emptyList());}// 2. 解析出其中的用戶idList<Long> ids = top5.stream().map(Long::valueOf).collect(Collectors.toList());// 3. 根據用戶id查詢用戶List<UserDTO> userDTOS = userService.listByIds(ids).stream().map(user -> BeanUtil.copyProperties(user, UserDTO.class)).collect(Collectors.toList());// 4. 返回return Result.ok(userDTOS);}private void isBlogLiked(Blog blog) {// 1. 獲取登錄用戶//return前可以把isLike設為null或者false,不然賬號退出登錄再打開首頁會殘留上一個賬號的點贊狀態。UserDTO user = UserHolder.getUser();if (user == null) {return;}Long userId = UserHolder.getUser().getId();// 2. 判斷當前登錄用戶是否已經點贊String key = "blog:liked:" + blog.getId();Double score = stringRedisTemplate.opsForZSet().score(key, userId.toString());blog.setIsLike(score!=null);}

return前可以把isLike設為null或者false,不然賬號退出登錄再打開首頁會殘留上一個賬號的點贊狀態。

    //使用map方法將每個元素轉換為對應的Long類型,并使用collect方法將結果收集到一個List中@Overridepublic Result queryBlogLikes(Long id) {String key = BLOG_LIKED_KEY + id;// 1. 查詢top5的點贊用戶 zrange key 0 4Set<String> top5 = stringRedisTemplate.opsForZSet().range(key,0,4);if (top5 == null || top5.isEmpty()) {return Result.ok(Collections.emptyList());}// 2. 解析出其中的用戶idList<Long> ids = top5.stream().map(Long::valueOf).collect(Collectors.toList());// 3. 根據用戶id查詢用戶String idStr = StrUtil.join(",",ids);List<UserDTO> userDTOS = userService.query().in("id",ids).last("ORDER BY FIELD(id,"+idStr+")").list().stream().map(user -> BeanUtil.copyProperties(user, UserDTO.class)).collect(Collectors.toList());// 4. 返回return Result.ok(userDTOS);}

?foottxt有字幕

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/80190.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/80190.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/80190.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【PCB工藝】運放電路中的負反饋機制

通過運算方法器電路設計詳細解釋負反饋機制&#xff08;Negative Feedback&#xff09; 負反饋 是控制系統、電子電路、神經系統等多個領域中非常核心的概念。特別在運算放大器&#xff08;Op-Amp&#xff09;電路中&#xff0c;負反饋是實現精確控制和高穩定性的關鍵機制。 …

聲紋振動傳感器在電力監測領域的應用

聲紋振動傳感器在電力監測領域有多種應用&#xff0c;主要包括以下幾個方面&#xff1a; 變壓器監測 故障診斷&#xff1a;變壓器在運行過程中會產生特定的聲紋和振動信號&#xff0c;當變壓器內部出現故障&#xff0c;如繞組短路、鐵芯松動、局部放電等&#xff0c;其聲紋和振…

7、sentinel

控制臺訪問地址&#xff1a;http://localhost:8080/ 依賴 <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency>配置文件 spring:cloud:sentinel:transpo…

線程封裝

目錄 makefile Thread.hpp main.cc 以面向對象的方式造輪子 #ifndef _THREAD_HPP__ // 如果沒有定義過 _THREAD_HPP__ #define _THREAD_HPP__ // 則定義 _THREAD_HPP__// 這里是頭文件的實際內容&#xff08;類、函數聲明等&#xff09;#endif // 結束條件…

【maven-7.1】POM文件中的屬性管理:提升構建靈活性與可維護性

在Maven項目中&#xff0c;POM (Project Object Model) 文件是核心配置文件&#xff0c;而屬性管理則是POM中一個強大但常被低估的特性。良好的屬性管理可以顯著提升項目的可維護性、減少重復配置&#xff0c;并使構建過程更加靈活。本文將深入探討Maven中的屬性管理機制。 1.…

極狐GitLab 的合并請求部件能干什么?

極狐GitLab 是 GitLab 在中國的發行版&#xff0c;關于中文參考文檔和資料有&#xff1a; 極狐GitLab 中文文檔極狐GitLab 中文論壇極狐GitLab 官網 合并請求部件 (BASIC ALL) 合并請求的 概述 頁面顯示了來自服務的狀態更新&#xff0c;這些服務會對您的合并請求執行操作。…

26、C# 中是否可以繼承String類?為什么?

在 C# 中&#xff0c;不能直接繼承 String 類&#xff08;System.String&#xff09;。這是由于以下幾個原因&#xff1a; 1、String 類是 sealed 的 String 類在 .NET 中被標記為 sealed&#xff0c;這意味著它是一個密封類&#xff0c;不能被繼承。 sealed 關鍵字的作用是防…

deeplab語義分割訓練自定數據集

鏈接&#xff1a;https://pan.baidu.com/s/1KkkM1rLfyiMPtYLycpnxmg?pwdj2rd 提取碼&#xff1a;j2rd --來自百度網盤超級會員V2的分享 采用數據集&#xff1a; https://aistudio.baidu.com/datasetdetail/130647 采用代碼&#xff1a; https://github.com/jfzhang95/pyt…

【Pandas】pandas DataFrame mod

Pandas2.2 DataFrame Binary operator functions 方法描述DataFrame.add(other)用于執行 DataFrame 與另一個對象&#xff08;如 DataFrame、Series 或標量&#xff09;的逐元素加法操作DataFrame.add(other[, axis, level, fill_value])用于執行 DataFrame 與另一個對象&…

12、高階組件:魔法增幅器——React 19 HOC模式

一、魔法增幅器的本質 "高階組件是魔法師用咒語疊加的煉金術&#xff0c;"霍格沃茨魔咒研究院院長凝視著發光的增幅器&#xff0c;"通過函數式能量場的嵌套&#xff0c;讓基礎組件獲得預言家日報式的邏輯繼承&#xff01;" ——以神秘事務司的「維度疊加理…

Qt creator 16.0.1 語言家失效解決方法

一、在菜單“工具-->外部”里面沒有語言家、更新翻譯、發布翻譯工具。 二、解決方法 手工添加 1、添加目錄 2、添加工具 更新翻譯 (lupdate) %{CurrentDocument:Project:QT_INSTALL_BINS}\lupdate %{CurrentDocument:Project:FilePath} %{CurrentDocument:Project:Path}…

Apple AirTag定位原理

AirTag 是蘋果公司推出的一款用于追蹤物品的設備&#xff0c;觸及到我的知識盲區。所以特地記錄一下技術原理。其工作所用的技術原理主要涉及以下幾個方面&#xff1a; 藍牙技術&#xff1a;AirTag 使用藍牙低功耗技術&#xff08;BLE&#xff09;與用戶的 iPhone 或其他蘋果設…

計算機網絡 實驗五 RIP的配置與應用

摘要 本實驗基于華為eNSP平臺構建多路由器網絡拓撲&#xff0c;旨在通過實戰掌握路由器配置、RIP協議部署及網絡故障排查等核心技能。實驗分為拓撲設計、設備初始化、協議配置、連通性測試四個階段&#xff0c;重點研究RIPv2版本特性及自動匯總抑制機制。 在配置過程中&#…

MQTTX + MCP:MQTT 客戶端秒變物聯網 Agent

引言&#xff1a;MQTTX 與 MCP 的融合 作為最受歡迎的 MQTT 客戶端工具&#xff0c;MQTTX 在 1.12.0 beta 版本中集成了模型上下文協議&#xff08;MCP&#xff09;到 Copilot AI 功能中&#xff0c;顯著提升了服務能力。這一融合讓 MQTTX 轉變為 MCP Host&#xff08;也就是發…

UML統一建模

UML UML&#xff08;統一建模語言&#xff09;介紹 UML&#xff08;統一建模語言&#xff09;介紹 面向對象軟件開發需要經過OOA面向對象分析、OOD面向對象設計和OOP面向對象編程三個階段。OOA對目標系統進行分析并寄哪里分析模型&#xff0c;并將之文檔化&#xff0c;OOD用面向…

CPP_類和對象

面向對象&#xff1a; 更接近真實世界&#xff08;關注各對象之間的關系&#xff0c;而非各步驟的進行&#xff09; 將結構體升級成立類 類里面可以有&#xff1a;成員函數&#xff0c;成員變量 class Stack { public:void Init(int defaultCapacity4 ) {_a (int*)malloc(s…

極狐GitLab 如何撤銷變更?

極狐GitLab 是 GitLab 在中國的發行版&#xff0c;關于中文參考文檔和資料有&#xff1a; 極狐GitLab 中文文檔極狐GitLab 中文論壇極狐GitLab 官網 還原更改 (BASIC ALL) 在極狐GitLab 中&#xff0c;您可以還原單個提交或整個合并請求。 當您在 Git 中還原一個提交時&…

PNG透明免摳設計素材大全26000+

在當今的數字設計領域&#xff0c;尋找高質量且易于使用的素材是每個設計師的日常需求。今天&#xff0c;我們將為大家介紹一個超全面的PNG透明免摳設計素材大全&#xff0c;涵蓋多種風格、主題和應用場景&#xff0c;無論是平面設計、網頁設計還是多媒體制作&#xff0c;都能輕…

uniapp小程序使用echarts

1、引入插件 在Dcloud插件市場下載echarts插件&#xff1a;插件地址 2、頁面使用簡單示例&#xff1a; <template><view class"pie-view flex-center"><view style"width: 100%; height: 600rpx"><l-echart ref"chartRef&quo…

7-1 三種語言的單詞轉換

編寫程序實現&#xff1a;首先從鍵盤輸入若干個中文與英文單詞的偶對&#xff0c;以空行作結束標記&#xff1b;再輸入若干個英文與丹麥文單詞的偶對&#xff0c;以空行作結束標記。然后輸入一個中文單詞&#xff0c;輸出對應的丹麥文單詞&#xff1b;若不存在該單詞&#xff0…