-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearch.xml
732 lines (358 loc) · 712 KB
/
search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>seata</title>
<link href="/2025/01/11/seata/"/>
<url>/2025/01/11/seata/</url>
<content type="html"><![CDATA[<h1 id="Seata"><a href="#Seata" class="headerlink" title="Seata"></a>Seata</h1><h2 id="1-简介"><a href="#1-简介" class="headerlink" title="1.简介"></a>1.简介</h2><p><a href="https://seata.apache.org/">Seata</a>是什么?</p><blockquote><p>Seata 是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。在 Seata 开源之前,其内部版本在阿里系内部一直扮演着应用架构层数据一致性的中间件角色,帮助经济体平稳的度过历年的双11,对上层业务进行了有力的技术支撑。经过多年沉淀与积累,其商业化产品先后在阿里云、金融云上售卖。2019.1 为了打造更加完善的技术生态和普惠技术成果,Seata 正式宣布对外开源,未来 Seata 将以社区共建的形式帮助用户快速落地分布式事务解决方案。</p></blockquote><h2 id="2-快速开始"><a href="#2-快速开始" class="headerlink" title="2.快速开始"></a>2.快速开始</h2><h3 id="1-示例"><a href="#1-示例" class="headerlink" title="1.示例"></a>1.示例</h3><p>在这个架构图中,<code>Business</code>模块调用了<code>Storage</code>模块和<code>Order</code>模块,而<code>Order</code>模块又调用了<code>Account</code>模块,不同于单体架构,由于各个模块以微服务的方式相互独立,我们不能保证<code>Business</code>执行的方法具有事务性,也不能简单的通过<code>@Transactional</code>注解实现事务,因为无法确定远程调用是否成功</p><p><img src="https://seata.apache.org/zh-cn/assets/images/architecture-6bdb120b83710010167e8b75448505ec.png"></p><h3 id="2-解决方案"><a href="#2-解决方案" class="headerlink" title="2.解决方案"></a>2.解决方案</h3><blockquote><p>我们只需要使用一个 <code>@GlobalTransactional</code> 注解在业务方法上:</p></blockquote><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@GlobalTransactional</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">purchase</span><span class="hljs-params">(String userId, String commodityCode, <span class="hljs-type">int</span> orderCount)</span> {<br> ......<br>}<br></code></pre></td></tr></table></figure><p><img src="https://seata.apache.org/zh-cn/assets/images/solution-1bdadb80e54074aa3088372c17f0244b.png"></p>]]></content>
<categories>
<category> springcloud </category>
</categories>
<tags>
<tag> seata </tag>
<tag> 分布式事务 </tag>
</tags>
</entry>
<entry>
<title>sentinel</title>
<link href="/2025/01/09/sentinel/"/>
<url>/2025/01/09/sentinel/</url>
<content type="html"><![CDATA[<h1 id="sentinel"><a href="#sentinel" class="headerlink" title="sentinel"></a><a href="https://sentinelguard.io/zh-cn">sentinel</a></h1><h2 id="1-简介"><a href="#1-简介" class="headerlink" title="1.简介"></a>1.简介</h2><blockquote><p><strong>Sentinel</strong> 是面向分布式、多语言异构化服务架构的<strong>流量治理组件</strong>,主要以流量为切入点,从<strong>流量控制</strong>、流量路由、<strong>熔断降级</strong>、系统自适应<strong>保护</strong>等多个维度来帮助用户保障微服务的稳定性。</p></blockquote><h2 id="2-快速使用"><a href="#2-快速使用" class="headerlink" title="2.快速使用"></a>2.快速使用</h2><h3 id="1-下载"><a href="#1-下载" class="headerlink" title="1.下载"></a>1.下载</h3><p>前往<a href="https://github.com/alibaba/Sentinel/releases">sentinel</a>的发布页面下载sentinel的jar包,最好放到一个<strong>没有中文目录的文件夹</strong></p><h3 id="2-启动"><a href="#2-启动" class="headerlink" title="2.启动"></a>2.启动</h3><figure class="highlight shell"><table><tr><td class="code"><pre><code class="hljs shell">java -jar .\sentinel-dashboard-1.8.8.jar<br></code></pre></td></tr></table></figure><h3 id="3-额外配置"><a href="#3-额外配置" class="headerlink" title="3.额外配置"></a>3.额外配置</h3><blockquote><p>可以通过命令行传递参数对sentinel进行一些额外的配置,但是每次都需要使用一长串命令很不方便,还有一个更好的办法,这个办法对springboot应用的jar包都有用</p></blockquote><p>在jar包的同一个文件夹下新建一个 <code>application.yml</code> 文件,然后在文件中配置想要配置的参数,然后使用java命令启动时<strong>会自动读取这个文件的配置,而且优先级更高</strong>,下面是一个示例</p><figure class="highlight yml"><table><tr><td class="code"><pre><code class="hljs yml"><span class="hljs-comment"># 端口</span><br><span class="hljs-attr">server:</span><br> <span class="hljs-attr">port:</span> <span class="hljs-number">9999</span> <br><span class="hljs-comment"># sentinel控制台的地址</span><br><span class="hljs-attr">csp:</span><br> <span class="hljs-attr">sentinel:</span><br> <span class="hljs-attr">dashboard:</span><br> <span class="hljs-attr">server:</span> <span class="hljs-string">localhost:9999</span><br><span class="hljs-comment"># 项目名称</span><br><span class="hljs-attr">project:</span><br> <span class="hljs-attr">name:</span> <span class="hljs-string">sentinel</span><br><span class="hljs-comment"># 允许ANSI颜色输出,打开这项后控制台输出的日志带有颜色,但是如果控制台不支持显示颜色的话会乱码,按需打开</span><br><span class="hljs-attr">spring:</span><br> <span class="hljs-attr">output:</span><br> <span class="hljs-attr">ansi:</span><br> <span class="hljs-attr">enabled:</span> <span class="hljs-string">always</span><br><br></code></pre></td></tr></table></figure><p>另外可以在命令中指定<strong>日志文件输出位置</strong>,因为日志默认是输出到C盘的,<code>-Dcsp.sentinel.log.dir=X:/env/sentinel/logs</code>指定了日志输出的文件夹,可以自行修改</p><figure class="highlight shell"><table><tr><td class="code"><pre><code class="hljs shell">java -Dcsp.sentinel.log.dir=X:/env/sentinel/logs -jar sentinel-dashboard-1.8.8.jar<br></code></pre></td></tr></table></figure><p>这样的话命令会显得很长,可以将这条命令保存为 <code>sentinel.cmd</code> 文件,双击执行即可</p><blockquote><p>如果你使用 <code>pwsh</code> 的话还有更方便的办法</p><ol><li><p>找到powershell的配置文件,在控制台使用<code>echo $PROFILE</code>打印配置文件路径</p></li><li><p>在配置文件中添加配置</p><figure class="highlight sh"><table><tr><td class="code"><pre><code class="hljs sh"><span class="hljs-keyword">function</span> sentinel {<br> <span class="hljs-built_in">cd</span> X:\<span class="hljs-built_in">env</span>\sentinel<br> ./sentinel.cmd<br>}<br></code></pre></td></tr></table></figure></li><li><p>打开一个终端,输入<code>sentinel</code>即可启动,既方便又优雅</p><p><img src="https://s2.loli.net/2025/01/09/L7yEr2NhgbqMnml.png" alt="image-20250109182558016"></p></li></ol></blockquote><h3 id="4-控制面板"><a href="#4-控制面板" class="headerlink" title="4.控制面板"></a>4.控制面板</h3><p>sentinel启动后可以打开<a href="http://localhost:9999/">localhost:9999</a>查看,用户名和密码默认都是<code>sentinel</code> </p><p><img src="https://s2.loli.net/2025/01/09/e4q8EuMsPBzbioK.png" alt="image-20250109183123528"></p><h3 id="5-引入项目"><a href="#5-引入项目" class="headerlink" title="5.引入项目"></a>5.引入项目</h3><h4 id="1-添加依赖"><a href="#1-添加依赖" class="headerlink" title="1.添加依赖"></a>1.添加依赖</h4><figure class="highlight xml"><table><tr><td class="code"><pre><code class="hljs xml"><span class="hljs-comment"><!--sentinel--></span><br><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>com.alibaba.cloud<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>spring-cloud-starter-alibaba-sentinel<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br></code></pre></td></tr></table></figure><h4 id="2-添加配置"><a href="#2-添加配置" class="headerlink" title="2.添加配置"></a>2.添加配置</h4><figure class="highlight yml"><table><tr><td class="code"><pre><code class="hljs yml"><span class="hljs-attr">spring:</span><br> <span class="hljs-attr">cloud:</span><br> <span class="hljs-attr">sentinel:</span><br> <span class="hljs-attr">transport:</span><br> <span class="hljs-comment"># 控制台地址</span><br> <span class="hljs-attr">dashboard:</span> <span class="hljs-string">localhost:9999</span><br> <span class="hljs-comment"># 通过请求方法区分</span><br> <span class="hljs-attr">http-method-specify:</span> <span class="hljs-literal">true</span><br></code></pre></td></tr></table></figure><h4 id="3-访问接口"><a href="#3-访问接口" class="headerlink" title="3.访问接口"></a>3.访问接口</h4><p>访问项目的接口,然后回到sentinel控制台查看,可以看到对应接口的访问信息等</p><p><img src="https://s2.loli.net/2025/01/09/PTQEkGODmz7rsHM.png" alt="image-20250109192951159"></p><h2 id="3-流量控制"><a href="#3-流量控制" class="headerlink" title="3.流量控制"></a>3.流量控制</h2><h4 id="1-QPS"><a href="#1-QPS" class="headerlink" title="1.QPS"></a>1.QPS</h4><blockquote><p>QPS是Queries Per Second的缩写,意思是每秒查询率或每秒请求数。</p></blockquote><p>sentinel中的簇点链路对应controller层中的每个接口,配置流控规则可以对这些接口的请求进行限流,下图对用户登录接口进行限流,如果每秒请求数超过10个就会返回错误状态码<code>429</code>,<code>flow limiting</code>即限流</p><p><img src="https://s2.loli.net/2025/01/10/URzt2jTC3ZGNhdo.png" alt="image-20250110114059436"></p><h4 id="2-并发线程数"><a href="#2-并发线程数" class="headerlink" title="2.并发线程数"></a>2.并发线程数</h4><blockquote><p>并发线程数是指在某一时刻同时访问同一资源的线程数量。换句话说,就是同时处理该资源逻辑的线程数。</p><p>在 Sentinel 中,通过并发线程数限流可以有效避免某些资源因为过多线程同时访问而导致性能问题(例如:锁竞争、CPU 或内存消耗过高)。</p></blockquote><p>试想这样一个场景:</p><p>假设一个服务器并发线程数上限为50,有10个接口,平均每个接口可以连接5个线程。如果不对每个接口进行限制的话,如果此时有一个接口出现问题,处理请求的速度大大降低,源源不断的请求打到这个接口,那么这个接口所需要的并发线程数就会快速增加,影响其他接口的处理速度,严重的话甚至会耗尽整个服务器资源。</p><p><strong>在sentinel中对接口设置并发请求数上限,防止出现问题的接口耗尽服务器资源,实现线程隔离</strong></p><p><img src="https://s2.loli.net/2025/01/10/hHDMKjp6iUFRedr.png" alt="image-20250110122040749"></p><h2 id="4-熔断"><a href="#4-熔断" class="headerlink" title="4.熔断"></a>4.熔断</h2><blockquote><p>熔断实际上就是断开与异常微服务的连接,我们可以配置一定的规则来判断微服务是否出现异常,如果出现异常就将其熔断,等待一段时间后放行一些流量测试,如果还是异常则继续熔断,直到正常才继续连接,熔断可以防止服务出现异常后还一直消耗资源</p></blockquote><p>熔断状态:</p><ul><li>Closed(关闭):默认状态,请求正常调用。</li><li>Open(打开):触发熔断,短时间内拒绝请求。</li><li>Half-Open(半开):经过一段时间后,断路器尝试恢复,允许部分请求通过测试是否恢复。</li></ul><p>触发熔断的条件:</p><ul><li>失败率阈值:请求失败的比例超过设定值。</li><li>请求次数:总请求量达到最小阈值后,才开始统计失败率。</li><li>响应时间阈值:服务响应时间过长。</li></ul><p>恢复机制: 熔断后,系统会经过一段时间尝试恢复,允许部分流量进入测试服务是否恢复正常。</p><p><img src="https://s2.loli.net/2025/01/11/UtqKufCalwLTRov.png" alt="image-20250111153826954"></p><h2 id="5-自定义返回响应"><a href="#5-自定义返回响应" class="headerlink" title="5.自定义返回响应"></a>5.自定义返回响应</h2><p>添加一个自定义配置,发生<code>BlockException</code>异常后设置状态码为429,并写入自定义的返回数据</p><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * sentinel配置</span><br><span class="hljs-comment"> */</span><br><span class="hljs-meta">@Slf4j</span><br><span class="hljs-meta">@Configuration</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">SentinelConfig</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">BlockExceptionHandler</span> {<br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 统一处理限流返回响应</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-meta">@Override</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">handle</span><span class="hljs-params">(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e)</span> <span class="hljs-keyword">throws</span> Exception {<br> log.error(E.FLOW_LIMIT, e);<br> httpServletResponse.setStatus(<span class="hljs-number">429</span>);<br> R<Object> error = R.error(<span class="hljs-number">429</span>, E.FLOW_LIMIT);<br> httpServletResponse.setContentType(<span class="hljs-string">"application/json;charset=UTF-8"</span>);<br> httpServletResponse.getWriter().write(JSON.toJSONString(error));<br> }<br>}<br></code></pre></td></tr></table></figure><h2 id="6-sentinel规则持久化"><a href="#6-sentinel规则持久化" class="headerlink" title="6.sentinel规则持久化"></a>6.sentinel规则持久化</h2><p>to be continued…</p><ul><li><input disabled="" type="checkbox"> sentinel规则如何持久化</li></ul>]]></content>
<categories>
<category> springcloud </category>
<category> 服务保护 </category>
</categories>
<tags>
<tag> sentinel </tag>
</tags>
</entry>
<entry>
<title>Gateway</title>
<link href="/2025/01/01/Gateway/"/>
<url>/2025/01/01/Gateway/</url>
<content type="html"><![CDATA[<h1 id="Gateway"><a href="#Gateway" class="headerlink" title="Gateway"></a>Gateway</h1><h2 id="1-概述"><a href="#1-概述" class="headerlink" title="1.概述"></a>1.概述</h2><h3 id="是什么?"><a href="#是什么?" class="headerlink" title="是什么?"></a>是什么?</h3><blockquote><p>Spring Cloud Gateway 是一个基于 Spring Framework 和 Spring Boot 构建的 <span alt='highlight'>API 网关框架</span>,用于<strong>路由</strong>和<strong>负载均衡</strong>。</p></blockquote><h3 id="有什么用?"><a href="#有什么用?" class="headerlink" title="有什么用?"></a>有什么用?</h3><p><img src="https://docs.spring.io/spring-cloud-gateway/reference/_images/spring_cloud_gateway_diagram.png"></p><ol><li><strong>路由功能</strong>:<code>Spring Cloud Gateway</code> 通过配置的路由规则,将外部请求转发到后端服务。这些路由可以基于请求的 URL、HTTP 方法、请求头等进行匹配。</li><li><strong>负载均衡</strong>:<code>Spring Cloud Gateway</code> 可以与 <code>Spring Cloud LoadBalancer</code> 配合,进行请求的负载均衡,确保请求的流量均衡地分配到各个后端服务。</li><li><strong>过滤器</strong>:可以使用过滤器进行请求和响应的<strong>预处理或后处理</strong>,例如认证、日志记录、请求转发等。</li><li><strong>动态路由</strong>:支持根据条件(如用户身份、请求参数等)动态修改路由规则。</li><li><strong>集成其他 Spring Cloud 组件</strong>:Spring Cloud Gateway 与其他 Spring Cloud 项目(如 Spring Cloud Config、Spring Cloud Discovery、Spring Security 等)无缝集成,能够帮助实现微服务架构中的服务发现、配置管理、安全认证等功能。</li></ol><p><strong>应用场景:</strong></p><ul><li><strong>微服务架构</strong>:作为微服务架构中的 API 网关,Spring Cloud Gateway 可以对外提供统一的入口,简化客户端的访问,同时还能够处理一些常见的网关功能,如身份验证、请求限流等。</li><li><strong>统一路由管理</strong>:在系统中统一管理所有服务的路由规则,简化了客户端的访问和服务间的通信。</li><li><strong>负载均衡和流量控制</strong>:配合负载均衡和限流功能,提高服务的可用性和稳定性。</li></ul><h3 id="相关术语"><a href="#相关术语" class="headerlink" title="相关术语"></a>相关术语</h3><ul><li><p><strong>Route</strong>: 路由,网关的基本构建块。它由一个 <strong>ID</strong>、一个目标 <strong>URI</strong>、一组 <strong>谓词</strong>(predicates)和一组 <strong>过滤器</strong>(filters)定义。如果聚合后的谓词为真,则匹配该路由。</p></li><li><p><strong>Predicate</strong>: 断言,这是一个 Java 8 的 <strong>Function Predicate</strong>。其输入类型是 Spring Framework 的 <strong>ServerWebExchange</strong>。这使得你可以基于 HTTP 请求中的任何内容进行匹配,例如请求头或请求参数。</p></li><li><p><strong>Filter</strong>: 过滤器,这些是由特定工厂构造的 <strong>GatewayFilter</strong> 实例。你可以在发送下游请求之前或之后,修改请求和响应。</p></li></ul><h2 id="2-快速开始"><a href="#2-快速开始" class="headerlink" title="2.快速开始"></a>2.快速开始</h2><h3 id="1-导入依赖"><a href="#1-导入依赖" class="headerlink" title="1.导入依赖"></a>1.导入依赖</h3><p>要<strong>通过网关将请求路由到其他微服务</strong>,同样需要<strong>将网关注册到注册中心</strong>,以nacos为例,还需要导入nacos注册发现依赖</p><figure class="highlight xml"><table><tr><td class="code"><pre><code class="hljs xml"><span class="hljs-comment"><!-- gateway网关 --></span><br><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.springframework.cloud<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>spring-cloud-starter-gateway<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br><span class="hljs-comment"><!-- nacos注册发现 --></span><br><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>com.alibaba.cloud<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>spring-cloud-starter-alibaba-nacos-discovery<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br><span class="hljs-comment"><!-- nacos配置 --></span><br><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>com.alibaba.cloud<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>spring-cloud-starter-alibaba-nacos-config<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br></code></pre></td></tr></table></figure><blockquote><p>[!WARNING]</p><p><strong>常见问题</strong>:Gateway微服务启动时出现:<strong>Spring MVC found on classpath, which is incompatible with Spring Cloud Gateway.</strong></p><p>原因:Spring Cloud Gateway 基于 <strong>Reactive Web</strong>(响应式编程)架构,而 <strong>Spring MVC</strong> 是基于传统的 <strong>Servlet</strong>(阻塞式编程)架构。这两者在同一个应用中<strong>无法共存</strong>,因此会出现冲突</p></blockquote><p>你需要将gateway项目中的<code>spring-boot-starter-web</code>依赖删除,或者通过下面的办法排除依赖,因为可能<strong>你没有显式引入MVC依赖,而是因为引入的其他依赖中包含了mvc依赖</strong></p><ul><li>在maven工具窗口找到gateway项目,右键<strong>依赖项</strong>,点击<strong>分析依赖关系</strong></li></ul><p><img src="https://s2.loli.net/2025/01/01/jXYHuesf3F1T9tl.png" alt="image-20250101184836544"></p><ul><li><p>在搜索框中搜索<strong>mvc</strong>,找到 <code>spring-boot-starter-web</code> 依赖,发现在引入的common模块中</p><p><img src="https://s2.loli.net/2025/01/01/HVWfdLe6kcZy5qp.png" alt="image-20250101185714684"></p></li><li><p>找到common依赖,排除整个web依赖或者排除spring-webmvc也可以,这里直接排除整个web依赖,刷新maven后重启项目就可以了</p><figure class="highlight xml"><table><tr><td class="code"><pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>io.github.caolib<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>common<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">version</span>></span>0.0.1-SNAPSHOT<span class="hljs-tag"></<span class="hljs-name">version</span>></span><br> <span class="hljs-comment"><!-- 排除spring-web --></span><br> <span class="hljs-tag"><<span class="hljs-name">exclusion</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.springframework.boot<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>spring-boot-starter-web<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br> <span class="hljs-tag"></<span class="hljs-name">exclusion</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br></code></pre></td></tr></table></figure></li></ul><h3 id="2-配置路由"><a href="#2-配置路由" class="headerlink" title="2.配置路由"></a>2.配置路由</h3><figure class="highlight yml"><table><tr><td class="code"><pre><code class="hljs yml"><span class="hljs-attr">spring:</span><br> <span class="hljs-attr">application:</span><br> <span class="hljs-attr">name:</span> <span class="hljs-string">gateway</span><br> <span class="hljs-attr">cloud:</span><br> <span class="hljs-attr">gateway:</span><br> <span class="hljs-comment"># 路由规则</span><br> <span class="hljs-attr">routes:</span><br> <span class="hljs-bullet">-</span> <span class="hljs-attr">id:</span> <span class="hljs-string">admin-route</span><br> <span class="hljs-attr">uri:</span> <span class="hljs-string">lb://admin</span><br> <span class="hljs-attr">predicates:</span><br> <span class="hljs-bullet">-</span> <span class="hljs-string">Path=/admins/**</span><br> <span class="hljs-bullet">-</span> <span class="hljs-attr">id:</span> <span class="hljs-string">user-route</span><br> <span class="hljs-attr">uri:</span> <span class="hljs-string">lb://user</span><br> <span class="hljs-attr">predicates:</span><br> <span class="hljs-bullet">-</span> <span class="hljs-string">Path=/users/**,/books/**</span><br> <span class="hljs-attr">filters:</span><br> <span class="hljs-bullet">-</span> <span class="hljs-string">AddRequestHeader=X-Request-red,</span> <span class="hljs-string">blue</span><br> <span class="hljs-comment"># 默认过滤器</span><br> <span class="hljs-attr">default-filters:</span><br> <span class="hljs-bullet">-</span> <span class="hljs-string">AddRequestHeader=X-Request-red,</span> <span class="hljs-string">blue</span><br></code></pre></td></tr></table></figure><p>routes下可以定义多个路由规则,<strong>以第二条路由规则<code>user-route</code>为例</strong></p><ul><li><p>id:<strong>路由规则的唯一标识</strong>,可以随意自定义但要保证唯一,此处的<code>user-route</code>就是id</p></li><li><p>uri:<strong>转发的目的地址</strong>,<code>lb</code>表示负载均衡,后接转发的微服务名称,此处的 <code>lb://user</code> 意思就是从nacos中找到名为user的微服务并将请求转发给它,如果有多个user微服务,则进行负载均衡,uri也可以填固定的地址,比如 <code>https://example.org</code></p></li><li><p>predicates:<strong>断言,其实说是转发条件更为合适</strong>,断言中包含各种条件,只有满足这些条件时才会进行转发,此处的 <code>- Path=/users/**,/books/**</code> 表示请求路径以 <code>/users/</code>或者<code>/books</code> 开头的所有请求都会进行转发,像这样的断言规则当然是不止一个,这是<a href="https://docs.spring.io/spring-cloud-gateway/reference/spring-cloud-gateway/request-predicates-factories.html">官网所有断言规则</a></p></li><li><p>filters:<strong>过滤器</strong>,可以在路由转发请求之前完成一些统一的动作,此处的 <code>- AddRequestHeader=X-Request-red, blue</code> 指添加一个请求头。使用 <code>default-filters</code> 属性可以配置一个默认的过滤器,所有请求都会添加上这个请求头。像这样的过滤器当然是不止一个,还有很多可以完成其他动作的过滤器,这是<a href="https://docs.spring.io/spring-cloud-gateway/reference/spring-cloud-gateway/gatewayfilter-factories.html">官网所有过滤器</a>,</p></li></ul><h2 id="3-自定义过滤器"><a href="#3-自定义过滤器" class="headerlink" title="3.自定义过滤器"></a>3.自定义过滤器</h2><h3 id="1-全局过滤器"><a href="#1-全局过滤器" class="headerlink" title="1.全局过滤器"></a>1.全局过滤器</h3><h4 id="添加过滤器"><a href="#添加过滤器" class="headerlink" title="添加过滤器"></a>添加过滤器</h4><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Slf4j</span><br><span class="hljs-meta">@Component</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">DefaultGlobalFilter</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">GlobalFilter</span>, Ordered {<br> <span class="hljs-meta">@Override</span><br> <span class="hljs-keyword">public</span> Mono<Void> <span class="hljs-title function_">filter</span><span class="hljs-params">(ServerWebExchange exchange, GatewayFilterChain chain)</span> {<br> <span class="hljs-type">ServerHttpRequest</span> <span class="hljs-variable">request</span> <span class="hljs-operator">=</span> exchange.getRequest();<br>log.debug(<span class="hljs-string">"request headers: {}"</span>, request.getHeaders()); <span class="hljs-comment">// 打印所有请求头</span><br> log.debug(<span class="hljs-string">"request path: {}"</span>, request.getPath()); <span class="hljs-comment">// 打印请求路径</span><br> <span class="hljs-comment">// 放行</span><br> <span class="hljs-keyword">return</span> chain.filter(exchange);<br> }<br><br> <span class="hljs-comment">// 过滤器的优先级,数字越小,优先级越高</span><br> <span class="hljs-meta">@Override</span><br> <span class="hljs-keyword">public</span> <span class="hljs-type">int</span> <span class="hljs-title function_">getOrder</span><span class="hljs-params">()</span> {<br> <span class="hljs-keyword">return</span> <span class="hljs-number">100</span>;<br> }<br>}<br></code></pre></td></tr></table></figure><p>自定义一个拦截器实现登录校验,请求发送过来后,网关完成登录校验后将用户信息保存到请求头中,然后转发请求到其他微服务</p><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Component</span><br><span class="hljs-meta">@RequiredArgsConstructor</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">LoginFilter</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">GlobalFilter</span>, Ordered {<br> <span class="hljs-meta">@Override</span><br> <span class="hljs-keyword">public</span> Mono<Void> <span class="hljs-title function_">filter</span><span class="hljs-params">(ServerWebExchange exchange, GatewayFilterChain chain)</span> {<br> <span class="hljs-comment">// 获取请求路径</span><br> <span class="hljs-type">ServerHttpRequest</span> <span class="hljs-variable">request</span> <span class="hljs-operator">=</span> exchange.getRequest();<br> <span class="hljs-type">RequestPath</span> <span class="hljs-variable">path</span> <span class="hljs-operator">=</span> request.getPath();<br><br> <span class="hljs-comment">// 如果请求路径不需要校验直接放行</span><br> <span class="hljs-comment">// code here ......</span><br><br> <span class="hljs-comment">// 取出请求头中的token</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">token</span> <span class="hljs-operator">=</span> request.getHeaders().getFirst(<span class="hljs-string">"Authorization"</span>);<br> <span class="hljs-comment">// 校验jwt令牌</span><br> <span class="hljs-comment">// code here ......</span><br><br> <span class="hljs-comment">// 将用户信息放入请求头</span><br> <span class="hljs-type">ServerWebExchange</span> <span class="hljs-variable">newRequest</span> <span class="hljs-operator">=</span> exchange.mutate()<br> .request(builder -> builder.header(<span class="hljs-string">"info"</span>,<span class="hljs-string">"用户信息的字符串"</span>)) <span class="hljs-comment">//这里是怎么存的,到时候就怎么取</span><br> .build();<br><br> <span class="hljs-keyword">return</span> chain.filter(newRequest); <span class="hljs-comment">// 放行</span><br> }<br><br> <span class="hljs-meta">@Override</span><br> <span class="hljs-keyword">public</span> <span class="hljs-type">int</span> <span class="hljs-title function_">getOrder</span><span class="hljs-params">()</span> {<br> <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;<br> }<br>}<br></code></pre></td></tr></table></figure><p>问题:网关路由转发的请求中的请求头中的信息<strong>怎么在下游的微服务中获取</strong>呢?</p><p>我们可以在其他微服务中<strong>使用拦截器获取到请求头中的用户信息然后保存到ThreadLocal中</strong>,但是每个微服务都需要这个拦截器,所以可以将这个配置放在通用模块中</p><h4 id="添加拦截器"><a href="#添加拦截器" class="headerlink" title="添加拦截器"></a>添加拦截器</h4><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">UserInfoInterceptor</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">HandlerInterceptor</span> {<br> <span class="hljs-meta">@Override</span><br> <span class="hljs-keyword">public</span> <span class="hljs-type">boolean</span> <span class="hljs-title function_">preHandle</span><span class="hljs-params">(HttpServletRequest request, HttpServletResponse response, Object handler)</span> {<br> <span class="hljs-comment">// 从请求头中获取用户id</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">userId</span> <span class="hljs-operator">=</span> request.getHeader(<span class="hljs-string">"info"</span>);<br> <span class="hljs-keyword">if</span> (StrUtil.isNotBlank(userId))<br> <span class="hljs-comment">// 不为空则保存到ThreadLocal中</span><br> <span class="hljs-comment">// code here ......</span><br> <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;<br> }<br><br> <span class="hljs-meta">@Override</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">postHandle</span><span class="hljs-params">(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)</span> {<br> <span class="hljs-comment">// 从ThreadLocal中删除信息</span><br> <span class="hljs-comment">// code here ......</span><br> }<br>}<br></code></pre></td></tr></table></figure><h4 id="添加配置"><a href="#添加配置" class="headerlink" title="添加配置"></a>添加配置</h4><blockquote><p>[!warning]</p><p>[!warning] 在mvc配置类中添加这个拦截器,此处的 <code>@ConditionalOnClass(DispatcherServlet.class)</code> 注解可以防止gateway项目导入这个配置,因为gateway中一般不允许mvc存在,所以自然就没有mvc的依赖,如果导入这个配置就会出错</p></blockquote><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Configuration</span><br><span class="hljs-meta">@ConditionalOnClass(DispatcherServlet.class)</span> <span class="hljs-comment">// 项目中有mvc的情况下才生效,避免webflux项目报错</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">MvcConfig</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">WebMvcConfigurer</span> {<br> <span class="hljs-meta">@Override</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">addInterceptors</span><span class="hljs-params">(InterceptorRegistry registry)</span> {<br> <span class="hljs-comment">// 添加用户拦截器</span><br> registry.addInterceptor(<span class="hljs-keyword">new</span> <span class="hljs-title class_">UserInfoInterceptor</span>());<br> }<br>}<br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category> springcloud </category>
</categories>
<tags>
<tag> gateway </tag>
</tags>
</entry>
<entry>
<title>公网访问本地服务</title>
<link href="/2024/12/31/%E5%85%AC%E7%BD%91%E8%AE%BF%E9%97%AE%E6%9C%AC%E5%9C%B0%E6%9C%8D%E5%8A%A1/"/>
<url>/2024/12/31/%E5%85%AC%E7%BD%91%E8%AE%BF%E9%97%AE%E6%9C%AC%E5%9C%B0%E6%9C%8D%E5%8A%A1/</url>
<content type="html"><![CDATA[<blockquote><p>在公网部署前端项目是很方便的,Vercel、Netlify、Cloudflare等等都可以很快地部署一个前端页面(<del>前端部署是这样的,你只要上传文件就可以,而后端部署要考虑的就很多了,没有云服务器</del>)</p><p>现在我部署了一个前端页面,我想让这个页面可以访问我本地电脑上运行的后端服务,不用云服务器怎么做?</p></blockquote><h2 id="查看IPv6地址"><a href="#查看IPv6地址" class="headerlink" title="查看IPv6地址"></a>查看IPv6地址</h2><p>家里的路由器有IPv6地址,可以使用<code>ipconfig</code>命令查看</p><img src="https://s2.loli.net/2025/01/01/lB1oVpEv8muQhDM.png" alt="image-20250101000149006" style="zoom:67%;" /><h2 id="创建后端项目"><a href="#创建后端项目" class="headerlink" title="创建后端项目"></a>创建后端项目</h2><p>以springboot为例,创建一个后端项目,需要导入<code>spring-boot-starter-web</code>依赖</p><blockquote><p>创建一个简单的测试接口,启动项目访问<code>localhost:8080</code>,会显示Hello World!</p></blockquote><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@RestController</span><br><span class="hljs-meta">@CrossOrigin</span> <span class="hljs-comment">// 允许跨域请求</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">TestController</span> {<br> <span class="hljs-meta">@GetMapping</span><br> <span class="hljs-keyword">public</span> String <span class="hljs-title function_">test</span><span class="hljs-params">()</span> {<br> <span class="hljs-keyword">return</span> <span class="hljs-string">"Hello World!"</span>;<br> }<br>}<br></code></pre></td></tr></table></figure><blockquote><p>在<code>application.yml</code>文件中添加一个配置,启动IPv6功能,重启项目,访问 <code>http://[240e:370:9114**********]:8080/</code>,中间的一长串就是刚才获取的IPv6地址,同样会显示Hello World!</p></blockquote><figure class="highlight yml"><table><tr><td class="code"><pre><code class="hljs yml"><span class="hljs-attr">server:</span><br> <span class="hljs-attr">address:</span> <span class="hljs-string">"::"</span><br></code></pre></td></tr></table></figure><h2 id="部署前端页面"><a href="#部署前端页面" class="headerlink" title="部署前端页面"></a>部署前端页面</h2><p>创建一个文件夹,在文件夹下添加一个 <code>index.html</code> 文件,作用很简单,就是向我们刚刚创建的接口发送请求,<strong>你需要将下面的ip地址替换</strong></p><figure class="highlight html"><table><tr><td class="code"><pre><code class="hljs html"><span class="hljs-meta"><!DOCTYPE <span class="hljs-keyword">html</span>></span><br><span class="hljs-tag"><<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>></span><br><span class="hljs-tag"><<span class="hljs-name">head</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">title</span>></span>Test<span class="hljs-tag"></<span class="hljs-name">title</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"</span>></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">style</span>></span><span class="language-css"></span><br><span class="language-css"> <span class="hljs-selector-tag">button</span> {</span><br><span class="language-css"> <span class="hljs-attribute">font-size</span>: <span class="hljs-number">50px</span>;</span><br><span class="language-css"> <span class="hljs-attribute">margin-left</span>: <span class="hljs-number">30vw</span>;</span><br><span class="language-css"> <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">20vh</span>;</span><br><span class="language-css"> <span class="hljs-attribute">width</span>: <span class="hljs-number">300px</span>;</span><br><span class="language-css"> }</span><br><span class="language-css"> </span><span class="hljs-tag"></<span class="hljs-name">style</span>></span><br><span class="hljs-tag"></<span class="hljs-name">head</span>></span><br><span class="hljs-tag"><<span class="hljs-name">body</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">button</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"fetchDataBtn"</span>></span>获取数据<span class="hljs-tag"></<span class="hljs-name">button</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">script</span>></span><span class="language-javascript"></span><br><span class="language-javascript"> <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">getElementById</span>(<span class="hljs-string">'fetchDataBtn'</span>).<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">'click'</span>, <span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) {</span><br><span class="language-javascript"> axios.<span class="hljs-title function_">get</span>(<span class="hljs-string">'http://[240e:370:9114**********]:8080/'</span>) <span class="hljs-comment">//替换这个ip</span></span><br><span class="language-javascript"> .<span class="hljs-title function_">then</span>(<span class="hljs-keyword">function</span> (<span class="hljs-params">response</span>) {</span><br><span class="language-javascript"> <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-title class_">JSON</span>.<span class="hljs-title function_">stringify</span>(response.<span class="hljs-property">data</span>, <span class="hljs-literal">null</span>, <span class="hljs-number">2</span>));</span><br><span class="language-javascript"> })</span><br><span class="language-javascript"> .<span class="hljs-title function_">catch</span>(<span class="hljs-keyword">function</span> (<span class="hljs-params">error</span>) {</span><br><span class="language-javascript"> <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">error</span>(<span class="hljs-string">'Error fetching data:'</span>, error);</span><br><span class="language-javascript"> });</span><br><span class="language-javascript"> });</span><br><span class="language-javascript"> </span><span class="hljs-tag"></<span class="hljs-name">script</span>></span><br><span class="hljs-tag"></<span class="hljs-name">body</span>></span><br><span class="hljs-tag"></<span class="hljs-name">html</span>></span><br></code></pre></td></tr></table></figure><blockquote><p>这里使用<strong>surge</strong>进行部署这个html页面</p><p>为什么使用它?因为<strong>它可以使用http协议</strong>,本地的springboot服务器使用http,<strong>如果前端部署使用https的话,发送请求会被浏览器拦截</strong>(当然如果你有有效SSL证书也可以把本地服务器改为https,然后前端部署也使用https)</p></blockquote><ul><li><p>安装surge</p><figure class="highlight sh"><table><tr><td class="code"><pre><code class="hljs sh">npm install -g surge<br></code></pre></td></tr></table></figure></li><li><p>执行<code>surge</code>,部署网页,第一次使用需要注册,<strong>输入邮箱</strong>然后<strong>设置一个密码</strong>,随后<strong>验证邮箱</strong></p><figure class="highlight sh"><table><tr><td class="code"><pre><code class="hljs sh">surge<br></code></pre></td></tr></table></figure></li><li><p>之后会询问使用哪个文件夹,默认当前文件夹,<strong>直接回车</strong></p></li><li><p>然后<strong>设置一个域名</strong>,回车就完成了</p></li></ul><h2 id="测试"><a href="#测试" class="headerlink" title="测试"></a>测试</h2><p>打开刚才设置的域名,默认会使用https,<strong>手动修改为http</strong>,点击页面上的按钮,打开控制台就能看到Hello World!了,这样在公网部署的前端项目就能访问本地的后端服务器了,而且不需要云服务器!</p>]]></content>
<categories>
<category> IPv6 </category>
</categories>
<tags>
<tag> surge </tag>
</tags>
</entry>
<entry>
<title>springboot发送邮件</title>
<link href="/2024/12/28/springboot%E5%8F%91%E9%80%81%E9%82%AE%E4%BB%B6/"/>
<url>/2024/12/28/springboot%E5%8F%91%E9%80%81%E9%82%AE%E4%BB%B6/</url>
<content type="html"><![CDATA[<h1 id="springboot发送邮件"><a href="#springboot发送邮件" class="headerlink" title="springboot发送邮件"></a>springboot发送邮件</h1><h2 id="导入依赖"><a href="#导入依赖" class="headerlink" title="导入依赖"></a>导入依赖</h2><figure class="highlight xml"><table><tr><td class="code"><pre><code class="hljs xml"><span class="hljs-comment"><!-- 邮件依赖 --></span><br><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.springframework.boot<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>spring-boot-starter-mail<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br></code></pre></td></tr></table></figure><h2 id="添加配置"><a href="#添加配置" class="headerlink" title="添加配置"></a>添加配置</h2><figure class="highlight yml"><table><tr><td class="code"><pre><code class="hljs yml"><span class="hljs-attr">spring:</span><br> <span class="hljs-attr">mail:</span><br> <span class="hljs-comment"># qq邮箱的host</span><br> <span class="hljs-attr">host:</span> <span class="hljs-string">smtp.qq.com</span><br> <span class="hljs-comment"># 端口,固定的</span><br> <span class="hljs-attr">port:</span> <span class="hljs-number">465</span><br> <span class="hljs-comment"># 发件人的邮箱</span><br> <span class="hljs-attr">username:</span> <span class="hljs-number">1265</span><span class="hljs-string">****[email protected]</span><br> <span class="hljs-comment"># qq邮箱服务的授权码</span><br> <span class="hljs-attr">password:</span> <span class="hljs-string">etj*******afh</span><br> <span class="hljs-attr">test-connection:</span> <span class="hljs-literal">true</span><br> <span class="hljs-attr">properties:</span><br> <span class="hljs-attr">mail:</span><br> <span class="hljs-attr">smtp:</span><br> <span class="hljs-attr">ssl:</span><br> <span class="hljs-attr">enable:</span> <span class="hljs-literal">true</span><br></code></pre></td></tr></table></figure><h2 id="测试发送"><a href="#测试发送" class="headerlink" title="测试发送"></a>测试发送</h2><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@SpringBootTest</span><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">MailApplicationTests</span> {<br> <span class="hljs-meta">@Autowired</span><br> <span class="hljs-keyword">private</span> JavaMailSender javaMailSender;<br><br> <span class="hljs-meta">@Test</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">senderMail</span><span class="hljs-params">()</span> {<br> <span class="hljs-type">SimpleMailMessage</span> <span class="hljs-variable">message</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">SimpleMailMessage</span>();<br> <span class="hljs-comment">// 发件人 你的邮箱</span><br> message.setFrom(<span class="hljs-string">"12******[email protected]"</span>);<br> <span class="hljs-comment">// 接收人 接收者邮箱</span><br> message.setTo(<span class="hljs-keyword">new</span> <span class="hljs-title class_">String</span>[]{<span class="hljs-string">"tan*****@gmail.com"</span>});<br> <span class="hljs-comment">//邮件主题</span><br> message.setSubject(<span class="hljs-string">"hello"</span>);<br> <span class="hljs-comment">//邮件内容</span><br> message.setText(<span class="hljs-string">"test"</span>);<br><br> javaMailSender.send(message);<br> }<br>}<br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category> 后端 </category>
</categories>
<tags>
<tag> 邮件 </tag>
<tag> springboot </tag>
</tags>
</entry>
<entry>
<title>邮箱开启SMTP服务</title>
<link href="/2024/12/27/%E5%BC%80%E5%90%AFSMTP%E6%9C%8D%E5%8A%A1/"/>
<url>/2024/12/27/%E5%BC%80%E5%90%AFSMTP%E6%9C%8D%E5%8A%A1/</url>
<content type="html"><![CDATA[<h1 id="开启邮件SMTP服务"><a href="#开启邮件SMTP服务" class="headerlink" title="开启邮件SMTP服务"></a>开启邮件SMTP服务</h1><h2 id="1-SMTP服务是什么?"><a href="#1-SMTP服务是什么?" class="headerlink" title="1. SMTP服务是什么?"></a>1. SMTP服务是什么?</h2><blockquote><p><strong>SMTP</strong>(Simple Mail Transfer Protocol,简单邮件传输协议)是用于发送和接收电子邮件的通信协议,是电子邮件系统中最基础的协议之一。它负责在邮件服务器之间传输邮件,但在客户端中,通常只用于<strong>发送邮件</strong>。</p><p>简单来说,<strong>SMTP服务的作用是发送电子邮件</strong>,而开启SMTP服务可以让我们使用编程语言发送邮件</p></blockquote><h2 id="2-开启SMTP服务"><a href="#2-开启SMTP服务" class="headerlink" title="2. 开启SMTP服务"></a>2. 开启SMTP服务</h2><blockquote><p>以QQ邮箱为例,登陆<a href="https://mail.qq.com/">QQ邮箱</a>,点击界面右上角<strong>账号与安全</strong>,也可能需要先点击右上角头像然后点击<strong>账号与安全</strong>(qq邮箱如果绑定了微信则界面会不一样)</p><p>随后点击 <strong>安全设置</strong> -> <strong>开启服务</strong></p></blockquote><p> <img src="https://s2.loli.net/2024/12/27/xIj8fl9Qeqnd5tr.png" alt="image-20241227210629985"></p><blockquote><p>微信扫码发送短信后点击<strong>我已发送</strong></p></blockquote><p><img src="https://s2.loli.net/2024/12/27/dkt9EBzFcDMAOKh.png" alt="image-20241227210750815"></p><blockquote><p>复制授权码(保存好这个授权码),然后点击返回就完成了</p></blockquote><p><img src="https://s2.loli.net/2024/12/27/xiWaF7dANyJb8kU.png" alt="image-20241227211012501"></p><h2 id="3-发送邮件"><a href="#3-发送邮件" class="headerlink" title="3. 发送邮件"></a>3. 发送邮件</h2><h3 id="普通文本"><a href="#普通文本" class="headerlink" title="普通文本"></a>普通文本</h3><blockquote><p>以python为例发送一个电子邮件,注意修改发、收件人和授权码</p></blockquote><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">import</span> smtplib<br><span class="hljs-keyword">from</span> email.mime.text <span class="hljs-keyword">import</span> MIMEText<br><span class="hljs-keyword">from</span> email.mime.multipart <span class="hljs-keyword">import</span> MIMEMultipart<br><br>smtp_server = <span class="hljs-string">'smtp.qq.com'</span><br>smtp_port = <span class="hljs-number">465</span><br>sender_email = <span class="hljs-string">'2*****[email protected]'</span> <span class="hljs-comment"># 发件人</span><br>receiver_email = <span class="hljs-string">'2*****[email protected]'</span> <span class="hljs-comment"># 收件人</span><br>password = <span class="hljs-string">'修改为你的授权码'</span> <span class="hljs-comment"># 授权码</span><br><br>subject = <span class="hljs-string">'Test Email'</span> <span class="hljs-comment"># 主题</span><br>body = <span class="hljs-string">'This is a test email sent using Python.'</span> <span class="hljs-comment"># 正文</span><br><br>msg = MIMEMultipart()<br>msg[<span class="hljs-string">'From'</span>] = sender_email<br>msg[<span class="hljs-string">'To'</span>] = receiver_email<br>msg[<span class="hljs-string">'Subject'</span>] = subject<br><br>msg.attach(MIMEText(body, <span class="hljs-string">'plain'</span>))<br><br><span class="hljs-comment"># Send the email</span><br><span class="hljs-keyword">try</span>:<br> server = smtplib.SMTP_SSL(smtp_server, smtp_port)<br> server.login(sender_email, password)<br> server.sendmail(sender_email, receiver_email, msg.as_string())<br> server.quit()<br> <span class="hljs-built_in">print</span>(<span class="hljs-string">'Email sent successfully!'</span>)<br><span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> e:<br> <span class="hljs-built_in">print</span>(<span class="hljs-string">f'Failed to send email: <span class="hljs-subst">{e}</span>'</span>)<br></code></pre></td></tr></table></figure><p><img src="https://s2.loli.net/2024/12/27/xvLgBjfYducsmoE.png" alt="image-20241227212000262"></p><h3 id="HTML内容"><a href="#HTML内容" class="headerlink" title="HTML内容"></a>HTML内容</h3><blockquote><p>也可以发送html格式的文本内容,以表格为例,将正文改为表格,然后将MIMEText格式改为html就可以了</p></blockquote><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">import</span> smtplib<br><span class="hljs-keyword">from</span> email.mime.text <span class="hljs-keyword">import</span> MIMEText<br><span class="hljs-keyword">from</span> email.mime.multipart <span class="hljs-keyword">import</span> MIMEMultipart<br><br>smtp_server = <span class="hljs-string">'smtp.qq.com'</span><br>smtp_port = <span class="hljs-number">465</span><br>sender_email = <span class="hljs-string">'[email protected]'</span> <br>receiver_email = <span class="hljs-string">'[email protected]'</span> <br>password = <span class="hljs-string">'jlegvgancaltbijc'</span> <br><br><br>subject = <span class="hljs-string">'Test Email'</span><br>body = <span class="hljs-string">'''</span><br><span class="hljs-string"><html></span><br><span class="hljs-string"> <body></span><br><span class="hljs-string"> <h2>芝士表格</h2></span><br><span class="hljs-string"> <table border="1" cellpadding="5" cellspacing="0"></span><br><span class="hljs-string"> <tr></span><br><span class="hljs-string"> <th>姓名</th></span><br><span class="hljs-string"> <th>年龄</th></span><br><span class="hljs-string"> </tr></span><br><span class="hljs-string"> <tr></span><br><span class="hljs-string"> <td>clb</td></span><br><span class="hljs-string"> <td>18</td></span><br><span class="hljs-string"> </tr></span><br><span class="hljs-string"> <tr></span><br><span class="hljs-string"> <td>zs</td></span><br><span class="hljs-string"> <td>20</td></span><br><span class="hljs-string"> </tr></span><br><span class="hljs-string"> </table></span><br><span class="hljs-string"> </body></span><br><span class="hljs-string"></html></span><br><span class="hljs-string">'''</span> <span class="hljs-comment"># HTML 正文</span><br><br>msg = MIMEMultipart()<br>msg[<span class="hljs-string">'From'</span>] = sender_email<br>msg[<span class="hljs-string">'To'</span>] = receiver_email<br>msg[<span class="hljs-string">'Subject'</span>] = subject<br><br>msg.attach(MIMEText(body, <span class="hljs-string">'html'</span>)) <span class="hljs-comment"># 格式改为html</span><br><br><span class="hljs-comment"># Send the email</span><br><span class="hljs-keyword">try</span>:<br> server = smtplib.SMTP_SSL(smtp_server, smtp_port)<br> server.login(sender_email, password)<br> server.sendmail(sender_email, receiver_email, msg.as_string())<br> server.quit()<br> <span class="hljs-built_in">print</span>(<span class="hljs-string">'Email sent successfully!'</span>)<br><span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> e:<br> <span class="hljs-built_in">print</span>(<span class="hljs-string">f'Failed to send email: <span class="hljs-subst">{e}</span>'</span>)<br></code></pre></td></tr></table></figure><p><img src="https://s2.loli.net/2024/12/27/LVN37hY4DyFa2dq.png" alt="image-20241227212648499"></p>]]></content>
<categories>
<category> tools </category>
</categories>
<tags>
<tag> SMTP </tag>
<tag> email </tag>
</tags>
</entry>
<entry>
<title>openfeign远程调用</title>
<link href="/2024/12/26/openfeign/"/>
<url>/2024/12/26/openfeign/</url>
<content type="html"><![CDATA[<h1 id="openfeign"><a href="#openfeign" class="headerlink" title="openfeign"></a><a href="https://docs.spring.io/spring-cloud-openfeign/docs/current/reference/html/">openfeign</a></h1><h2 id="1-feign是什么?"><a href="#1-feign是什么?" class="headerlink" title="1.feign是什么?"></a>1.feign是什么?</h2><blockquote><p>Feign 是一个声明式的 Web 服务客户端。它使得编写 Web 服务客户端变得更加容易。要使用 Feign,你需要创建一个接口并对其进行注解。它支持可插拔的注解,包括 Feign 注解和 JAX-RS 注解。Feign 还支持可插拔的编码器和解码器。Spring Cloud 增加了对 Spring MVC 注解的支持,并允许使用 Spring Web 默认使用的相同 HttpMessageConverters。Spring Cloud 集成了 Eureka、Spring Cloud CircuitBreaker,以及 Spring Cloud LoadBalancer,以在使用 Feign 时提供负载均衡的 HTTP 客户端。</p></blockquote><h2 id="2-快速使用"><a href="#2-快速使用" class="headerlink" title="2.快速使用"></a>2.快速使用</h2><ol><li><p>导入openfeign和负载均衡器依赖</p><figure class="highlight xml"><table><tr><td class="code"><pre><code class="hljs xml"><span class="hljs-comment"><!--openfeign--></span><br><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.springframework.cloud<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>spring-cloud-starter-openfeign<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br><span class="hljs-comment"><!--负载均衡器--></span><br><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.springframework.cloud<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>spring-cloud-starter-loadbalancer<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br></code></pre></td></tr></table></figure></li><li><p>开启openfeign</p><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@SpringBootApplication</span><br><span class="hljs-meta">@EnableFeignClients</span> <span class="hljs-comment">// 添加注解开启openfeign</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Application</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> SpringApplication.run(Application.class, args);<br> }<br>}<br></code></pre></td></tr></table></figure></li><li><p>新增接口<code>/feign/StoreClient.java</code>,添加调用方法声明(可直接从被调用服务的controller层复制)</p><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@FeignClient("stores")</span> <span class="hljs-comment">// 调用 stores微服务的方法</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">StoreClient</span> {<br> <span class="hljs-comment">// 可以直接将stores中的controller方法的声明直接复制到这里</span><br> <span class="hljs-meta">@RequestMapping(method = RequestMethod.GET, value = "/stores")</span><br> List<Store> <span class="hljs-title function_">getStores</span><span class="hljs-params">()</span>;<br><br> <span class="hljs-meta">@RequestMapping(method = RequestMethod.GET, value = "/stores")</span><br> Page<Store> <span class="hljs-title function_">getStores</span><span class="hljs-params">(Pageable pageable)</span>;<br><br> <span class="hljs-meta">@RequestMapping(method = RequestMethod.DELETE, value = "/stores/{storeId:\\d+}")</span><br> <span class="hljs-keyword">void</span> <span class="hljs-title function_">delete</span><span class="hljs-params">(<span class="hljs-meta">@PathVariable</span> Long storeId)</span>;<br>}<br></code></pre></td></tr></table></figure></li><li><p>如果调用方和被调用方都已经在注册中心(Nacos/Eureka)注册了,接下来就可以直接调用接口进行测试了</p></li></ol><h2 id="3-连接池"><a href="#3-连接池" class="headerlink" title="3.连接池"></a>3.连接池</h2><blockquote><p>连接池有什么用?在服务访问比较频繁的情况下,<strong>每次访问都要先建立和销毁连接</strong>,开销比较大,连接池可以帮我们管理连接,建立成功的连接<strong>使用完后不会立即释放</strong>(keep-alive),如果有相同的访问请求就可以<strong>基于这个已经建立好的连接</strong>,减少了资源开销和访问时间。</p><p>openfeign的默认httpclient不支持连接池,支持连接池的httpclient有<code>Apache HttpClient</code>和<code>OkHttp</code>两种</p></blockquote><h3 id="1-Apache-HttpClient"><a href="#1-Apache-HttpClient" class="headerlink" title="1.Apache HttpClient"></a>1.Apache HttpClient</h3><p>引入依赖</p><figure class="highlight xml"><table><tr><td class="code"><pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.apache.httpcomponents<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>httpclient<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br></code></pre></td></tr></table></figure><p>添加配置</p><figure class="highlight yml"><table><tr><td class="code"><pre><code class="hljs yml"><span class="hljs-attr">feign:</span><br> <span class="hljs-attr">httpclient:</span><br> <span class="hljs-attr">enabled:</span> <span class="hljs-literal">true</span> <span class="hljs-comment"># 启用 Apache HttpClient</span><br></code></pre></td></tr></table></figure><h3 id="2-OkHttp"><a href="#2-OkHttp" class="headerlink" title="2.OkHttp"></a>2.OkHttp</h3><p>引入依赖</p><figure class="highlight xml"><table><tr><td class="code"><pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>io.github.openfeign<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>feign-okhttp<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br></code></pre></td></tr></table></figure><p>添加配置</p><figure class="highlight yml"><table><tr><td class="code"><pre><code class="hljs yml"><span class="hljs-attr">feign:</span><br> <span class="hljs-attr">okhttp:</span><br> <span class="hljs-attr">enabled:</span> <span class="hljs-literal">true</span> <span class="hljs-comment"># 启用 OkHttp</span><br></code></pre></td></tr></table></figure><h2 id="4-日志配置"><a href="#4-日志配置" class="headerlink" title="4.日志配置"></a>4.日志配置</h2><p>创建一个feign的配置类,创建一个bean,返回值类型是日志级别,有 <code>NONE</code>,<code>BASIC</code>,<code>HEADERS</code>,<code>FULL</code>这几个级别</p><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">FeignConfig</span> {<br> <span class="hljs-meta">@Bean</span><br> <span class="hljs-keyword">public</span> Logger.Level <span class="hljs-title function_">feignLoggerLevel</span><span class="hljs-params">()</span> {<br> <span class="hljs-keyword">return</span> Logger.Level.HEADERS; <span class="hljs-comment">// openfeign 日志级别</span><br> }<br>}<br></code></pre></td></tr></table></figure><p>在启动类上开启feign功能的注解上加载这个配置 <code>@EnableFeignClients(defaultConfiguration = FeignConfig.class) </code></p><p>分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰分为抚慰抚慰抚慰</p>]]></content>
<categories>
<category> 后端 </category>
</categories>
<tags>
<tag> springcloud </tag>
<tag> openfeign </tag>
</tags>
</entry>
<entry>
<title>git版本控制系统</title>
<link href="/2024/03/25/git/"/>
<url>/2024/03/25/git/</url>
<content type="html"><![CDATA[<h1 id="Git"><a href="#Git" class="headerlink" title="Git"></a><a href="https://nulab.com/zh-cn/learn/software-development/git-tutorial/">Git</a></h1><p><a href="https://git-scm.com/">Git官网</a></p><h2 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h2><blockquote><p>Git是一个开源的<strong>分布式版本控制系统</strong>,用于高效地处理从小到大的项目。Git由Linus Torvalds创建,用于管理Linux内核开发。与集中式版本控制系统不同,如CVS或Subversion,Git采用分布式版本库的方式,不需要服务器端软件支持。这使得源代码的发布和交流变得非常方便。Git的速度很快,特别适合大型项目的版本管理。</p></blockquote><h2 id="常用命令"><a href="#常用命令" class="headerlink" title="常用命令"></a>常用命令</h2><p>Git的常用命令包括但不限于以下几个:</p><ul><li><code>git init</code>:初始化一个Git仓库。</li><li><code>git clone [url]</code>:克隆一个仓库到本地。</li><li><code>git add [file]</code>:添加文件到暂存区。</li><li><code>git commit -m "[message]"</code>:提交更新,并附加一条提交信息。</li><li><code>git status</code>:查看仓库当前的状态,显示有变更的文件。</li><li><code>git push [alias] [branch]</code>:将本地分支的更新推送到远程仓库。</li><li><code>git pull [alias] [branch]</code>:从远程仓库拉取更新并合并到本地。</li><li><code>git branch</code>:列出所有本地分支。</li><li><code>git checkout [branch-name]</code>:切换到指定分支。</li><li><code>git merge [branch]</code>:合并指定分支到当前分支。</li></ul><p>工作流程图</p><p><img src="https://s2.loli.net/2024/09/27/boYzTetVQDfp61R.png" alt="image-20240325220023918"></p><h2 id="撤消提交"><a href="#撤消提交" class="headerlink" title="撤消提交"></a>撤消提交</h2><p>使用<a href="https://nulab.com/zh-cn/learn/software-development/git-tutorial/git-commands-settings/basic-git-commands/#undo-changes-from-previous-commit">git revert 命令</a>撤消以前的提交。这是撤消更改的最常用方法。</p><p>Revert 命令可创建一个新的提交,用于恢复先前提交所做的更改。它允许您撤消不需要的更改,而无需完全移除提交或修改存储库的历史记录。它是一个有用的工具,用于管理对 Git 存储库的更改,同时保留其历史记录。</p><p>虽然你可以用<a href="https://nulab.com/zh-cn/learn/software-development/git-tutorial/git-commands-settings/git-commit-history-commands/#remove-previous-commit">git reset</a>或<a href="https://nulab.com/zh-cn/learn/software-development/git-tutorial/git-commands-settings/git-commit-history-commands/#modify--move-past-commit-and-messages">git rebase -i</a> 命令,从历史记录中删除先前的提交,但一般不建议这样做,因为这会导致远程存储库与其他成员的本地存储库不同</p><h2 id="推送"><a href="#推送" class="headerlink" title="推送"></a>推送</h2><p>要与他人共享更改,您必须使用 <a href="https://nulab.com/zh-cn/learn/software-development/git-tutorial/git-commands-settings/remote-git-commands/#create--push-branch-changes-to-remote-repository">git push</a>命令,这将更新远程存储库并将其与本地存储库同步。</p><h2 id="解决合并冲突"><a href="#解决合并冲突" class="headerlink" title="解决合并冲突"></a>解决合并冲突</h2><p>在正确完成合并之前,您可能会遇到需要解决的冲突。例如,如果两个或多个成员在两个不同的分支 (即远程和本地分支) 中对文件的同一部分进行更改,Git 无法自动合并它们。</p><p>发生这种情况时,Git 会在冲突文件中添加<strong>冲突解决标记</strong>。这些标记可帮助您确定文件的哪些部分<strong>需要手动处理</strong>。</p><p>发生冲突的示例。</p><p>在我们上面的例子中,<code>=====</code>上面的所有内容都是您的本地内容,下面的所有内容都来自远程分支。</p><p>在继续创建合并提交之前,您必须依照下列所示方式解决冲突部分。</p><p>修改变更集以解决冲突。</p><h2 id="修正提交"><a href="#修正提交" class="headerlink" title="修正提交"></a>修正提交</h2><p>您可以通过运行 <a href="https://nulab.com/zh-cn/learn/software-development/git-tutorial/git-commands-settings/git-commit-history-commands/#modify-previous-commit-and-messages">git commit –amend 命令</a>修改同一分支中的最新提交。这个命令可以方便地将新的或更新的文件添加到上一次提交中。这也是一种编辑提交消息或将提交消息添加到上一次提交的简便方法。</p><p>用 git commit –amend 修改最新的提交</p><h2 id="分支"><a href="#分支" class="headerlink" title="分支"></a>分支</h2><p> Git分支是一个非常强大的功能,它允许你在不同的开发线路上独立工作,而不会影响主线(通常是master分支)。这样,你可以在一个分支上开发新功能,而在另一个分支上修复bug,然后再将它们合并回主线。分支的用途包括:</p><ul><li><strong>功能开发和并行开发</strong>:你可以在不同的分支上同时开发多个功能。</li><li><strong>Bug修复和紧急修复</strong>:为了不干扰主要开发,可以在单独的分支上进行bug修复。</li><li><strong>版本发布和稳定的主分支</strong>:保持主分支稳定,只有完全测试通过的代码才能合并。</li><li><strong>实验性开发和试验性分支</strong>:尝试新想法而不影响主分支。</li><li><strong>多人协作和团队开发</strong>:团队成员可以在各自的分支上工作,然后合并他们的更改。</li></ul><p>假设你正在开发一个新功能,我们将其称为“feature-x”。你不希望这些更改影响主分支,因此你可以创建一个新分支并在其中工作。以下是步骤:</p><ol><li><p><strong>创建新分支</strong>:</p><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash">git branch feature-x<br></code></pre></td></tr></table></figure><p>这将创建一个名为<code>feature-x</code>的新分支。</p></li><li><p><strong>切换到新分支</strong>:</p><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash">git checkout feature-x<br></code></pre></td></tr></table></figure><p>现在你在<code>feature-x</code>分支上工作。</p></li><li><p><strong>添加更改</strong>: 在<code>feature-x</code>分支上进行更改后,使用以下命令将它们添加到暂存区:</p><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash">git add .<br></code></pre></td></tr></table></figure></li><li><p><strong>提交更改</strong>: 提交你的更改,并附加一条提交信息:</p><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash">git commit -m <span class="hljs-string">"Add new feature x"</span><br></code></pre></td></tr></table></figure></li><li><p><strong>切换回主分支</strong>: 完成开发后,切换回主分支:</p><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash">git checkout master<br></code></pre></td></tr></table></figure></li><li><p><strong>合并分支</strong>: 将<code>feature-x</code>分支的更改合并到主分支:</p><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash">git merge feature-x<br></code></pre></td></tr></table></figure></li><li><p><strong>推送更改</strong>: 如果你想将更改推送到远程仓库,使用:</p><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash">git push origin master<br></code></pre></td></tr></table></figure></li></ol><h2 id="gitignore"><a href="#gitignore" class="headerlink" title="gitignore"></a>gitignore</h2><h3 id="名称"><a href="#名称" class="headerlink" title="名称"></a>名称</h3><p>gitignore - 指定有意不跟踪的文件</p><h3 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h3><p>$XDG_CONFIG_HOME/git/ignore, $GIT_DIR/info/exclude, .gitignore</p><h3 id="描述"><a href="#描述" class="headerlink" title="描述"></a>描述</h3><p><code>gitignore</code> 文件指定了 Git 追踪时应忽略的文件。 已被 Git 追踪的文件不受影响,详见下面的注释。</p><p><code>gitignore</code> 文件中的每一行都指定了一个模式。 在决定是否忽略路径时,Git 通常会检查多个来源的 <code>gitignore</code> 模式,优先级从高到低(在一个优先级内,由最后匹配的模式决定结果):</p><ul><li>从支持这些模式的命令行中读取的模式。</li><li>模式从与路径相同目录下的 <code>.gitignore</code> 文件或任何父目录(直到工作树的顶层)中读取,上层文件中的模式会被下层文件中的模式覆盖,直到包含该文件的目录。这些模式相对于 <code>.gitignore</code> 文件的位置进行匹配。 项目通常会在其资源库中包含此类 <code>.gitignore</code> 文件,其中包含项目构建过程中生成的文件的模式。</li><li>从 <code>$GIT_DIR/info/exclude</code> 中读取的模式。</li><li>从配置变量 <code>core.excludesFile</code> 指定的文件中读取的模式。</li></ul><p>将模式放入哪个文件取决于模式的使用方式。</p><ul><li>应受版本控制并通过克隆分发到其他仓库的模式(即所有开发人员都想忽略的文件)应放入 <code>.gitignore</code> 文件。</li><li>特定于某个仓库但无需与其他相关仓库共享的模式(例如,存在于仓库内部但特定于某个用户工作流程的辅助文件)应放入 <code>$GIT_DIR/info/exclude</code> 文件。</li><li>用户希望 Git 在任何情况下都忽略的模式(例如,由用户选择的编辑器生成的备份或临时文件),一般会放入用户的 <code>~/.gitconfig</code> 中由 <code>core.excludesFile</code> 指定的文件。它的默认值是 $XDG_CONFIG_HOME/git/ignore。如果 $XDG_CONFIG_HOME 未设置或为空,则使用 $HOME/.config/git/ignore 代替。</li></ul><p>底层的 Git 工具,如 <em>git ls-files</em> 和 <em>git read-tree</em>,会读取命令行选项指定的 <em>gitignore</em> 模式,或从命 令行选项指定的文件中读取。 更高层次的 Git 工具,如 <em>git status</em> 和 <em>git add</em>,会使用上述指定来源的模式。</p><h3 id="日期格式"><a href="#日期格式" class="headerlink" title="日期格式"></a>日期格式</h3><ul><li>空行不匹配任何文件,因此可以作为分隔符,以提高可读性。</li><li>以 # 开头的一行为注释。 对于以散列开头的模式,在第一个散列前面加上反斜杠( “<code>\</code>“ )。</li><li>除非使用反斜线(”<code>\</code>“)引号,否则尾部空格将被忽略。</li><li>一个可选的前缀 “<code>!``",用于否定模式;任何被先前模式排除的匹配文件都将被重新包含。如果文件的父目录已被排除,则无法重新包含该文件。出于性能考虑,Git 不会列出排除的目录,因此无论在哪里定义,任何包含文件的模式都不会产生影响。 对于以 "</code>!<code>" 开头的模式,在第一个 "</code>!<code>" 前面加反斜杠("</code>`“),例如 “<code>\!important!.txt</code>“。</li><li>斜线 “<code>/</code>“ 用作目录分隔符。分隔符可以出现在 <code>.gitignore</code> 搜索模式的开头、中间或结尾。</li><li>如果在模式的开头或中间(或两者都有)有分隔符,则该模式是相对于特定 <code>.gitignore</code> 文件本身的目录层级而言的。否则,该模式也可能匹配 <code>.gitignore</code> 层级以下的任何层级。</li><li>如果模式末尾有分隔符,则模式只能匹配目录,否则模式既可以匹配文件,也可以匹配目录。</li><li>例如,模式 <code>doc/frotz/</code> 匹配 <code>doc/frotz</code> 目录,但不匹配 <code>a/doc/frotz</code> 目录;而 <code>frotz/</code> 则匹配 <code>frotz</code> 和 <code>a/frotz</code> 这两个目录(所有路径都是从 <code>.gitignore</code> 文件开始的相对路径)。</li><li>星号 “<code>*</code>“ 匹配斜线以外的任何字符。 字符 “<code>?</code>“ 匹配除 “<code>/</code>“ 以外的任何一个字符。 范围符号,如 “<code>[a-zA-Z]</code>“,可用于匹配范围中的一个字符。更详细的说明请参见 fnmatch(3) 和 FNM_PATHNAME 标志。</li></ul><p>在与全路径名匹配的模式中,两个连续的星号(”<code>**</code>“)可能有特殊含义:</p><ul><li>“<code>**</code>“在带斜杠目录之前,表示在所有目录中匹配。例如,”<code>**/foo</code>“匹配任何文件或目录的”<code>foo</code>“,与模式”<code>foo</code>“相同。”<code>**/foo/bar</code>“匹配任何文件或目录中直接位于目录”<code>foo</code>“之下的”<code>bar</code>“。</li><li>路径后跟有 “<code>/**</code>“ 表示匹配这个目录里面的所有文件。例如,”<code>abc/**</code>“ 匹配相对于 <code>.gitignore</code> 文件的位置中目录 “<code>abc</code>“ 内的所有文件,深度无限。</li><li>一个斜杠后面是两个连续的星号再接上一个斜杠,匹配零个或多个目录。例如,”<code>a/**/b</code>“ 匹配 “<code>a/b</code>“、”<code>a/x/b</code>“、”<code>a/x/y/b</code>“,等等,依此类推。</li><li>其他连续星号被视为普通星号,将根据前面的规则进行匹配。</li></ul><h3 id="配置"><a href="#配置" class="headerlink" title="配置"></a>配置</h3><p>可选的配置变量 <code>core.excludesFile</code> 表示包含要排除的文件名模式的文件路径,类似于 <code>$GIT_DIR/info/exclude</code>。 除 <code>$GIT_DIR/info/exclude</code> 中的模式外,还将使用排除文件中的模式。</p><h3 id="注释"><a href="#注释" class="headerlink" title="注释"></a>注释</h3><p>使用 gitignore 文件的目的是确保某些不被 Git 追踪的文件不被追踪。</p><p>要停止跟踪当前已被跟踪的文件,可使用 <em>git rm –cached</em> 从索引中移除该文件。文件名随后会被添加到 <code>.gitignore</code> 文件中,以防止该文件在以后的提交中被重新引入。</p><p>访问工作树中的 <code>.gitignore</code> 文件时,Git 不会跟踪符号链接。这样,当从索引或工作树访问文件时,与从文件系统访问文件时的行为保持一致。</p><h3 id="实例"><a href="#实例" class="headerlink" title="实例"></a>实例</h3><ul><li>模式 <code>hello.*</code> 匹配名称以 <code>hello.</code> 开头的任何文件或目录。如果只想将其限制在目录中,而不限制在其子目录中,则可以在模式前加上斜线,即 <code>/hello.*</code>;现在该模式可匹配 <code>hello.txt</code> 和 <code>hello.c</code> 但不匹配 <code>a/hello.java</code>。</li><li>模式 <code>foo/</code> 将匹配目录 <code>foo</code> 及其下的路径,但不会匹配常规文件或符号链接 <code>foo</code>(这与 Git 中 pathspec 的一般工作方式一致)</li><li><code>doc/frotz</code> 和 <code>/doc/frotz</code> 模式在任何 <code>.gitignore</code> 文件中都有同样的效果。换句话说,如果模式中已经有中间斜线,那么前导斜线就无关紧要了。</li><li>模式 <code>foo/*</code> 匹配 <code>foo/test.json</code>(一个正则文件)和 <code>foo/bar</code>(一个目录),但不匹配 <code>foo/bar/hello.c</code>(一个正则文件),因为模式中的星号不匹配 <code>bar/hello.c</code>,因为 <code>bar/hello.c</code> 中含有斜线。</li></ul><figure class="highlight plaintext"><table><tr><td class="code"><pre><code class="hljs plaintext">$ git status<br>[...]<br># 未追踪的文件:<br>[...]<br># Documentation/foo.html<br># Documentation/gitignore.html<br># file.o<br># lib.a<br># src/internal.o<br>[...]<br>$ cat .git/info/exclude<br># 忽略在目录树中的所有对象和存档文件<br>*.[oa]<br>$ cat Documentation/.gitignore<br># 忽略 html 文件,<br>*.html<br># 但追踪自己写的 foo.html<br>!foo.html<br>$ git status<br>[...]<br># 未追踪的文件:<br>[...]<br># Documentation/foo.html<br>[...]<br></code></pre></td></tr></table></figure><p>再举一个例子:</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><code class="hljs plaintext">$ cat .gitignore<br>vmlinux*<br>$ ls arch/foo/kernel/vm*<br>arch/foo/kernel/vmlinux.lds.S<br>$ echo '!/vmlinux*' >arch/foo/kernel/.gitignore<br></code></pre></td></tr></table></figure><p>第二个 .gitignore 阻止 Git 忽略 <code>arch/foo/kernel/vmlinux.lds.S</code>。</p><p>示例排除除特定目录 <code>foo/bar</code> 以外的所有内容(注意 <code>/*</code> - 如果没有斜线,通配符也会排除 <code>foo/bar</code> 中的所有内容):</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><code class="hljs plaintext">$ cat .gitignore<br># 排除 foo/bar 以外的所有内容<br>/*<br>!/foo<br>/foo/*<br>!/foo/bar<br></code></pre></td></tr></table></figure><h2 id="简化使用"><a href="#简化使用" class="headerlink" title="简化使用"></a>简化使用</h2><p>将下面代码保存为py文件,就能直接运行python执行一些常用操作,<strong>注意<code>git glog</code>是我自定义的一个命令</strong></p><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">import</span> os<br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">show</span>():<br> <span class="hljs-built_in">print</span>(<span class="hljs-string">'---------------------------------------------------------'</span>)<br> <span class="hljs-built_in">print</span>(<span class="hljs-string">'enter ---> 状态'</span>)<br> <span class="hljs-built_in">print</span>(<span class="hljs-string">'1 ---> 添加'</span>)<br> <span class="hljs-built_in">print</span>(<span class="hljs-string">'2 ---> 提交'</span>)<br> <span class="hljs-built_in">print</span>(<span class="hljs-string">'3 ---> 推送'</span>)<br> <span class="hljs-built_in">print</span>(<span class="hljs-string">'4 ---> 拉取'</span>)<br> <span class="hljs-built_in">print</span>(<span class="hljs-string">'5 ---> 添加、提交、推送'</span>)<br> <span class="hljs-built_in">print</span>(<span class="hljs-string">'6 ---> 日志'</span>)<br> <span class="hljs-built_in">print</span>(<span class="hljs-string">'7 ---> 远程仓库'</span>)<br> <span class="hljs-built_in">print</span>(<span class="hljs-string">'else ---> 退出'</span>)<br> <span class="hljs-built_in">print</span>(<span class="hljs-string">'---------------------------------------------------------'</span>)<br><br><span class="hljs-keyword">while</span> <span class="hljs-literal">True</span>:<br> <span class="hljs-keyword">try</span>:<br> show()<br> choice = <span class="hljs-built_in">int</span>(<span class="hljs-built_in">input</span>(<span class="hljs-string">'chioce: '</span>))<br> <span class="hljs-keyword">if</span> choice == <span class="hljs-literal">None</span>:<br> os.system(<span class="hljs-string">'git status'</span>)<br> <span class="hljs-keyword">elif</span> choice == <span class="hljs-number">1</span>:<br> os.system(<span class="hljs-string">'git add .'</span>)<br> <span class="hljs-keyword">elif</span> choice == <span class="hljs-number">2</span>:<br> message = <span class="hljs-built_in">input</span>(<span class="hljs-string">'Enter your message: '</span>)<br> os.system(<span class="hljs-string">f'git commit -m "<span class="hljs-subst">{message}</span>"'</span>)<br> <span class="hljs-built_in">print</span>(<span class="hljs-string">f'message:<span class="hljs-subst">{message}</span>'</span>)<br> <span class="hljs-keyword">elif</span> choice == <span class="hljs-number">3</span>:<br> os.system(<span class="hljs-string">'git push'</span>)<br> <span class="hljs-keyword">elif</span> choice == <span class="hljs-number">4</span>:<br> os.system(<span class="hljs-string">'git pull'</span>)<br> <span class="hljs-keyword">elif</span> choice == <span class="hljs-number">5</span>:<br> message = <span class="hljs-built_in">input</span>(<span class="hljs-string">'Enter your message: '</span>)<br> os.system(<span class="hljs-string">f'git add .'</span>)<br> os.system(<span class="hljs-string">f'git commit -m "<span class="hljs-subst">{message}</span>"'</span>)<br> os.system(<span class="hljs-string">f'git push'</span>)<br> <span class="hljs-built_in">print</span>(<span class="hljs-string">f'message:<span class="hljs-subst">{message}</span>'</span>)<br> <span class="hljs-keyword">elif</span> choice == <span class="hljs-number">6</span>:<br> os.system(<span class="hljs-string">'git glog'</span>)<br> <span class="hljs-keyword">elif</span> choice == <span class="hljs-number">7</span>:<br> os.system(<span class="hljs-string">'git remote -v'</span>)<br> <span class="hljs-keyword">else</span>:<br> exit()<br> <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> e:<br> <span class="hljs-keyword">if</span> <span class="hljs-built_in">isinstance</span>(e, ValueError):<br> os.system(<span class="hljs-string">'git status'</span>)<br> <span class="hljs-built_in">print</span>(<span class="hljs-string">'Invalid choice'</span>)<br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category> tools </category>
</categories>
<tags>
<tag> git </tag>
</tags>
</entry>
<entry>
<title>windows定时关机</title>
<link href="/2024/03/19/windows%E5%AE%9A%E6%97%B6%E5%85%B3%E6%9C%BA/"/>
<url>/2024/03/19/windows%E5%AE%9A%E6%97%B6%E5%85%B3%E6%9C%BA/</url>
<content type="html"><![CDATA[<ol><li>打开控制面板,搜索<strong>计划</strong>,点击<mark>计划任务</mark></li></ol><p><img src="https://s2.loli.net/2024/03/19/8olEi2dyD5xTKcG.png" alt="image-20240319223355706"></p><ol start="2"><li>右键<strong>任务计划程序库</strong>,创建任务</li></ol><p><img src="https://s2.loli.net/2024/03/19/gpT9YVFaxUEWq3C.png" alt="image-20240319223747111"></p><ol start="3"><li>常规</li></ol><p><img src="https://s2.loli.net/2024/03/19/Mr2XbwVfNPxUD8g.png" alt="image-20240319223931837"></p><ol start="4"><li>触发器</li></ol><p><img src="https://s2.loli.net/2024/03/19/oIUu7gqSaji2O83.png" alt="image-20240319224108154"></p><ol start="5"><li>操作,22:55触发,240秒后启动,也就是22:59关机</li></ol><p><img src="https://s2.loli.net/2024/09/25/upVvQNW6Zt2IdC8.png" alt="image-20240319224152722"></p><ol start="6"><li>条件</li></ol><blockquote><p><strong>添加这个条件后,即使电脑在休眠,也会自动唤醒电脑然后关机!</strong></p></blockquote><p><img src="https://s2.loli.net/2024/09/25/Ex9OzeNFTprJBdt.png" alt="image-20240319224409803"></p><ol start="7"><li>设置<img src="https://s2.loli.net/2024/09/25/6YbUoycx5kMfw7h.png" alt="image-20240319224442431"></li></ol><p>最后保存即可,这样就能在断电前一分钟让电脑自动关机了,可以通过查看执行记录看看任务有没有正常执行或者自己写一个脚本添加在关机指令之后的第二条指令</p>]]></content>
<categories>
<category> os </category>
</categories>
<tags>
<tag> windows </tag>
</tags>
</entry>
<entry>
<title>apifox测试工具</title>
<link href="/2024/03/18/apifox%E6%B5%8B%E8%AF%95%E5%B7%A5%E5%85%B7/"/>
<url>/2024/03/18/apifox%E6%B5%8B%E8%AF%95%E5%B7%A5%E5%85%B7/</url>
<content type="html"><![CDATA[<h1 id="Apifox"><a href="#Apifox" class="headerlink" title="Apifox"></a>Apifox</h1><p><a href="https://apifox.com/help/">官方文档</a></p><h2 id="登录自动更新token"><a href="#登录自动更新token" class="headerlink" title="登录自动更新token"></a>登录自动更新token</h2><blockquote><p>问题:使用apifox测试接口时,令牌过期后,需要重新登录,然后复制粘贴替换全局变量的token,有点麻烦,而且不注意可能会复制错误,怎么实现运行登录接口后自动将返回数据中的token值更新,然后其他请求自动携带全新的token呢</p></blockquote><h3 id="1-给登录接口添加后置操作"><a href="#1-给登录接口添加后置操作" class="headerlink" title="1.给登录接口添加后置操作"></a>1.给登录接口添加后置操作</h3><blockquote><p><strong>1.1 打开项目的登录接口,添加该接口的后置操作</strong></p></blockquote><p><img src="https://s2.loli.net/2024/03/31/BVyNZqb1Mfm7UsW.png" alt="1"></p><blockquote><p><strong>1.2 随便设置一个变量名字,然后点击小箭头</strong></p></blockquote><p><img src="https://s2.loli.net/2024/03/31/ZChcWljvr3yNdGx.png" alt="2"></p><blockquote><p><strong>1.3 左边是返回结果的结构,书写jsonpath表达式 ,$代表左边的json对象,然后就像访问对象属性一样写就可以了,最后看看提取结果和左边是不是对上了,最后点击确定保存,我的token是data中的token,所以是 <code>$.data.token</code></strong></p></blockquote><p><img src="https://s2.loli.net/2024/03/31/HAPCavXuh9Ns5lY.png" alt="img"><img src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" alt="点击并拖拽以移动"></p><blockquote><p><strong>1.4 保存接口文档后测试接口</strong></p></blockquote><p><img src="https://s2.loli.net/2024/03/31/2uJr9ocZqThwCQP.png" alt="img"><img src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" alt="点击并拖拽以移动"></p><blockquote><p><strong>1.5 如果上面操作无误,点击右上角按钮可以看到在本地环境多了一个变量值token,并且值就是返回结果的token</strong></p></blockquote><p><img src="https://s2.loli.net/2024/03/31/nW2jkyY3ZHiuz4f.png" alt="img"><img src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" alt="点击并拖拽以移动"></p><h3 id="2-设置项目所有接口的auth认证"><a href="#2-设置项目所有接口的auth认证" class="headerlink" title="2.设置项目所有接口的auth认证"></a>2.设置项目所有接口的auth认证</h3><blockquote><p><strong>2.1 如图依次点击,key的名字根据自己需要设置,value的值从环境变量读取</strong></p></blockquote><p><img src="https://s2.loli.net/2024/03/31/HLtYpbIF3w4eSnk.png" alt="img"><img src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" alt="点击并拖拽以移动"></p><blockquote><p><strong>2.2 读取变量选择之前设置的变量,确定后保存接口的修改</strong></p></blockquote><p><img src="https://s2.loli.net/2024/03/31/6RGFCTetWnHb3Kh.png" alt="img"><img src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" alt="点击并拖拽以移动"></p><blockquote><p><strong>3. 测试执行登录接口是否会自动设置token,可以执行登录接口后直接执行其他接口</strong></p></blockquote><p><img src="https://s2.loli.net/2024/03/31/GWsMZJSzY2Nqw1H.png" alt="img"><img src="data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==" alt="点击并拖拽以移动"></p><p>每次修改完成后记得保存再运行,这样就能简单完成token的自动更换</p><h2 id="Apifox-IDEA-插件快速上手"><a href="#Apifox-IDEA-插件快速上手" class="headerlink" title="Apifox IDEA 插件快速上手"></a><a href="https://apifox.com/help/applications-and-plugins/idea/start">Apifox IDEA 插件快速上手</a></h2><p><code>Apifox Helper</code> 是 Apifox 团队针对 IntelliJ IDEA 环境所推出的插件,可以在 IDEA 环境中识别本地 Java、Kotlin 后端项目的源代码,自动生成 API 文档并一键同步到 Apifox 的项目中。</p><p>对于常见的开发框架,<code>Apifox Helper</code> 插件能够做到开箱即用,实现真正的代码零侵入。如下图所示,仅通过识别最基本的 SpringBoot 代码,即可生成一份详尽的 API 文档:</p><p><img src="https://cdn.apifox.cn/uploads/help/202401101156728.png"></p>]]></content>
<categories>
<category> 测试 </category>
</categories>
<tags>
<tag> apifox </tag>
<tag> 测试 </tag>
</tags>
</entry>
<entry>
<title>nacos从入门到入土</title>
<link href="/2024/03/16/nacos/"/>
<url>/2024/03/16/nacos/</url>
<content type="html"><![CDATA[<h1 id="nacos"><a href="#nacos" class="headerlink" title="nacos"></a>nacos</h1><h2 id="注册中心"><a href="#注册中心" class="headerlink" title="注册中心"></a>注册中心</h2><h3 id="1-下载"><a href="#1-下载" class="headerlink" title="1. 下载"></a>1. 下载</h3><p><a href="https://nacos.io/download/nacos-server/#%E7%A8%B3%E5%AE%9A%E7%89%88%E6%9C%AC">Nacos Server 下载 | Nacos</a></p><h3 id="2-启动"><a href="#2-启动" class="headerlink" title="2. 启动"></a>2. 启动</h3><blockquote><p>解压缩后在<code>bin</code>目录下有几个脚本,<code>startup</code>就是启动脚本,默认都是集群<code>cluster</code>方式启动,也可以使用单机<code>standalone</code>模式启动</p></blockquote><figure class="highlight sh"><table><tr><td class="code"><pre><code class="hljs sh"><span class="hljs-comment"># 单机模式启动 </span><br>./startup.cmd -m standalone<br><br><span class="hljs-comment"># 集群模式启动</span><br>./startup.cmd<br></code></pre></td></tr></table></figure><p><img src="https://s2.loli.net/2024/12/26/9DLP4RrzJdsmkIw.png" alt="image-20241226192616560"></p><blockquote><p>[!note] </p><p>启动后默认端口<code>8848</code>,直接在浏览器打开<a href="http://localhost:8848/nacos">http://localhost:8848/nacos</a> 进行访问,默认用户名和密码都是<code>nacos</code>可以在<code>conf/application.properties</code>文件中修改端口<code>server.port</code></p></blockquote><p><img src="https://s2.loli.net/2024/12/26/Ol378APSIxhCJie.png" alt="image-20241226193352670"></p><h3 id="3-导入依赖"><a href="#3-导入依赖" class="headerlink" title="3. 导入依赖"></a>3. 导入依赖</h3><figure class="highlight xml"><table><tr><td class="code"><pre><code class="hljs xml"><span class="hljs-comment"><!--nacos--></span><br><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>com.alibaba.cloud<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>spring-cloud-starter-alibaba-nacos-discovery<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br></code></pre></td></tr></table></figure><p>版本号可以指定也可以不指定,<strong>如果不指定的话,需要导入下面的依赖管理</strong>,添加了这个配置后,spring-cloud-alibaba的依赖版本都使用这个版本,便于统一版本</p><figure class="highlight xml"><table><tr><td class="code"><pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">dependencyManagement</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">dependencies</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>com.alibaba.cloud<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>spring-cloud-alibaba-dependencies<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">version</span>></span>2.2.5.RELEASE<span class="hljs-tag"></<span class="hljs-name">version</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">type</span>></span>pom<span class="hljs-tag"></<span class="hljs-name">type</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">scope</span>></span>import<span class="hljs-tag"></<span class="hljs-name">scope</span>></span><br> <span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"></<span class="hljs-name">dependencies</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependencyManagement</span>></span><br></code></pre></td></tr></table></figure><h3 id="3-添加配置"><a href="#3-添加配置" class="headerlink" title="3. 添加配置"></a>3. 添加配置</h3><p>添加nacos地址与当前项目的应用名称,默认是单机模式</p><figure class="highlight yml"><table><tr><td class="code"><pre><code class="hljs yml"><span class="hljs-attr">spring:</span><br> <span class="hljs-attr">cloud:</span><br> <span class="hljs-attr">nacos:</span><br> <span class="hljs-attr">discovery:</span><br> <span class="hljs-attr">server-addr:</span> <span class="hljs-string">localhost:8848</span><br> <span class="hljs-attr">application:</span><br> <span class="hljs-attr">name:</span> <span class="hljs-string">ms-user</span><br></code></pre></td></tr></table></figure><h3 id="4-注册服务"><a href="#4-注册服务" class="headerlink" title="4. 注册服务"></a>4. 注册服务</h3><p>启动项目,可以看到当前项目服务已经注册到nacos</p><p><img src="https://s2.loli.net/2024/12/26/KWIJXuB25kav4TV.png" alt="image-20241226200052783"></p><h2 id="配置中心"><a href="#配置中心" class="headerlink" title="配置中心"></a>配置中心</h2><h3 id="1-引入依赖"><a href="#1-引入依赖" class="headerlink" title="1. 引入依赖"></a>1. 引入依赖</h3><p>在<code>pom.xml</code>文件中引入nacos配置管理的依赖</p><figure class="highlight xml"><table><tr><td class="code"><pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>com.alibaba.cloud<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>spring-cloud-starter-alibaba-nacos-config<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br></code></pre></td></tr></table></figure><h3 id="2-添加配置"><a href="#2-添加配置" class="headerlink" title="2. 添加配置"></a>2. 添加配置</h3><ul><li>在<code>resources</code>文件夹下新建配置文件<code>bootstrap.yml</code>文件,添加nacos的<strong>配置地址</strong>,注意与前面的<strong>注册发现地址</strong>区分</li></ul><figure class="highlight yml"><table><tr><td class="code"><pre><code class="hljs yml"><span class="hljs-attr">spring:</span><br> <span class="hljs-attr">application:</span><br> <span class="hljs-attr">name:</span> <span class="hljs-string">ms-coupon</span><br> <span class="hljs-attr">cloud:</span><br> <span class="hljs-attr">nacos:</span><br> <span class="hljs-attr">server-addr:</span> <span class="hljs-string">localhost:8848</span><br></code></pre></td></tr></table></figure><ul><li>在<code>application.yml</code>中添加自定义配置</li></ul><figure class="highlight yml"><table><tr><td class="code"><pre><code class="hljs yml"><span class="hljs-comment"># 配置属性</span><br><span class="hljs-attr">student:</span><br> <span class="hljs-attr">name:</span> <span class="hljs-string">zhangsan</span><br> <span class="hljs-attr">age:</span> <span class="hljs-number">18</span><br></code></pre></td></tr></table></figure><ul><li>新建对应的配置类,使用<code>@ConfigurationProperties</code>注解读取配置值</li></ul><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Data</span><br><span class="hljs-meta">@Component</span><br><span class="hljs-meta">@ConfigurationProperties(prefix = "student")</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Student</span> {<br> <span class="hljs-keyword">private</span> String name;<br> <span class="hljs-keyword">private</span> Integer age;<br>}<br></code></pre></td></tr></table></figure><h3 id="3-测试接口"><a href="#3-测试接口" class="headerlink" title="3. 测试接口"></a>3. 测试接口</h3><p>在controller层新增接口,并注入刚才的属性值</p><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@RestController</span><br><span class="hljs-meta">@RequiredArgsConstructor</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">CouponController</span> {<br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> Student student; <span class="hljs-comment">// 属性值来自application.yml的值</span><br> <span class="hljs-meta">@RequestMapping("/test")</span><br> <span class="hljs-keyword">public</span> R <span class="hljs-title function_">test</span><span class="hljs-params">()</span> {<br> <span class="hljs-keyword">return</span> R.ok().put(<span class="hljs-string">"name"</span>, student.getName()).put(<span class="hljs-string">"age"</span>, student.getAge());<br> }<br>}<br></code></pre></td></tr></table></figure><h3 id="4-测试接口"><a href="#4-测试接口" class="headerlink" title="4. 测试接口"></a>4. 测试接口</h3><p>根据接口路径发送对应请求,可以获取到对应的数据</p><p><img src="https://s2.loli.net/2024/12/26/AMzkYpxlV4173dE.png" alt="image-20241226202416312"></p><h3 id="5-热更新配置"><a href="#5-热更新配置" class="headerlink" title="5. 热更新配置"></a>5. 热更新配置</h3><blockquote><p>问题:如果配置的值需要变更,那么我就需要修改项目中的配置文件,然后重新启动项目,<strong>非常麻烦且耗费时间</strong></p><p>解决:</p><ol><li><ul><li>nacos配置文件格式为:**[spring.application.name]-(dev|prod|local).[文件后缀]**三部分</li><li>中间的作用范围可选,<strong>Data ID</strong>可以省略文件后缀,直接填微服务名</li><li>注意配置格式(文件后缀)对应你填写配置时用的格式</li></ul><img src="https://s2.loli.net/2024/12/26/He3P6mlYACUERyL.png" alt="image-20241226223313483" style="zoom:50%;" /></li><li><p>填写后点击发布,重新测试接口,数据发生变化而无需重启项目(配置中心的优先级比项目中的配置更高)</p></li><li><p>以后需要修改配置,只需要在nacos控制台修改配置文件后就能热更新了</p></li></ol></blockquote><h2 id="命名空间"><a href="#命名空间" class="headerlink" title="命名空间"></a>命名空间</h2><h3 id="命名空间-1"><a href="#命名空间-1" class="headerlink" title="命名空间"></a>命名空间</h3><blockquote><p><strong>Nacos的命名空间用于配置隔离</strong>,Nacos有一个默认的命名空间<code>public</code></p><ol><li><p>我们可以<strong>为每个微服务创建对应的命名空间</strong>实现<strong>微服务之间的配置隔离</strong>,也可以基于环境进行隔离(dev、test、prod)</p></li><li><p>例如分别为user,coupon微服务创建user、coupon命名空间,创建完命名空间后会生成对应的<strong>命名空间ID</strong></p></li><li><p>复制命名空间ID,在对应的<code>bootstrap.yml</code>添加配置<code>namespace</code>属性配置,然后重启项目</p><figure class="highlight yml"><table><tr><td class="code"><pre><code class="hljs yml"><span class="hljs-attr">spring:</span><br> <span class="hljs-attr">application:</span><br> <span class="hljs-attr">name:</span> <span class="hljs-string">ms-coupon</span><br> <span class="hljs-attr">cloud:</span><br> <span class="hljs-attr">nacos:</span><br> <span class="hljs-attr">server-addr:</span> <span class="hljs-string">localhost:8848</span><br> <span class="hljs-attr">config:</span><br> <span class="hljs-attr">namespace:</span> <span class="hljs-string">7a2cc573-0516-4655-8c69-525496095cfe</span><br></code></pre></td></tr></table></figure></li></ol></blockquote><p><img src="https://s2.loli.net/2024/12/31/y24IBPFDMid7keS.png" alt="image-20241231164449369"></p><p>在配置列表将public下的配置文件克隆到coupon下(也可以手动创建)</p><p><img src="https://s2.loli.net/2024/12/31/D38RTkwmPdIBWux.png" alt="image-20241231165831119"></p><p>然后修改配置,点击发布后重新测试,发现此时使用的是coupon环境下的配置</p><figure class="highlight properties"><table><tr><td class="code"><pre><code class="hljs properties"><span class="hljs-attr">student.name</span>=<span class="hljs-string">zs-coupon</span><br><span class="hljs-attr">student.age</span>=<span class="hljs-string">22</span><br></code></pre></td></tr></table></figure><h3 id="配置"><a href="#配置" class="headerlink" title="配置"></a>配置</h3><h4 id="配置集ID"><a href="#配置集ID" class="headerlink" title="配置集ID"></a>配置集ID</h4><p>即<code>Data ID</code>,其实就是配置文件名</p><h4 id="配置分组"><a href="#配置分组" class="headerlink" title="配置分组"></a>配置分组</h4><p>配置文件所属的分组,<strong>同样用于配置隔离</strong>,默认所有配置文件都属于<code>DEFAULT_GROUP</code>分组</p><p><img src="https://s2.loli.net/2024/12/31/8i2gcJe1XVxIDGt.png" alt="image-20241231181035617"></p><h3 id="如何使用"><a href="#如何使用" class="headerlink" title="如何使用"></a>如何使用</h3><p>怎么使用命名空间和配置分组进行各个微服务的配置隔离和不同环境的配置隔离?</p><blockquote><p>[!important]</p><ol><li><strong>为每个微服务创建对应的命名空间</strong>实现<strong>微服务之间的配置隔离</strong></li><li>在每个微服务的<strong>命名空间下创建多个分组</strong>实现<strong>多环境的配置隔离</strong></li></ol></blockquote><p><img src="https://s2.loli.net/2024/12/31/1HCIgZKY2pchkSR.png" alt="image-20241231171938560"></p><h3 id="多配置文件"><a href="#多配置文件" class="headerlink" title="多配置文件"></a>多配置文件</h3><blockquote><p>[!important]</p><p>当配置越来越多后,所有配置都放在<code>application.yml</code>中显得有些臃肿,我们可以把配置拆分为多个配置,在nacos分别创建对应的配置文件</p></blockquote><p>创建数据源配置 <code>datasource.yml</code></p><figure class="highlight yml"><table><tr><td class="code"><pre><code class="hljs yml"><span class="hljs-comment"># 数据源配置</span><br><span class="hljs-attr">spring:</span><br> <span class="hljs-attr">datasource:</span><br> <span class="hljs-attr">username:</span> <span class="hljs-string">root</span><br> <span class="hljs-attr">password:</span> <span class="hljs-string">root</span><br> <span class="hljs-attr">url:</span> <span class="hljs-string">jdbc:mysql://centos:3306/coupon?useSSL=false&serverTimezone=Asia/Shanghai</span><br> <span class="hljs-attr">driver-class-name:</span> <span class="hljs-string">com.mysql.cj.jdbc.Driver</span><br></code></pre></td></tr></table></figure><p>创建mybatis配置 <code>mybatis.yml</code></p><figure class="highlight yml"><table><tr><td class="code"><pre><code class="hljs yml"><span class="hljs-comment"># mybatis-plus配置</span><br><span class="hljs-attr">mybatis-plus:</span><br> <span class="hljs-attr">mapper-locations:</span> <span class="hljs-string">classpath*:/mapper/**/*.xml</span><br> <span class="hljs-attr">global-config:</span><br> <span class="hljs-attr">db-config:</span><br> <span class="hljs-attr">id-type:</span> <span class="hljs-string">auto</span><br></code></pre></td></tr></table></figure><p>创建其他配置 <code>others.yml</code></p><figure class="highlight yml"><table><tr><td class="code"><pre><code class="hljs yml"><span class="hljs-comment"># 优惠券</span><br><span class="hljs-attr">server:</span><br> <span class="hljs-attr">port:</span> <span class="hljs-number">12000</span><br><span class="hljs-attr">spring:</span><br> <span class="hljs-attr">application:</span><br> <span class="hljs-attr">name:</span> <span class="hljs-string">ms-coupon</span><br> <span class="hljs-attr">cloud:</span><br> <span class="hljs-comment"># nacos配置</span><br> <span class="hljs-attr">nacos:</span><br> <span class="hljs-attr">discovery:</span><br> <span class="hljs-attr">server-addr:</span> <span class="hljs-string">localhost:8848</span><br> <span class="hljs-comment"># 热更新</span><br> <span class="hljs-attr">devtools:</span><br> <span class="hljs-attr">restart:</span><br> <span class="hljs-attr">additional-paths:</span> <span class="hljs-string">src/main</span><br> <span class="hljs-attr">additional-exclude:</span> <span class="hljs-string">src/main/resources/static/**</span><br><span class="hljs-comment"># 自定义配置属性</span><br><span class="hljs-attr">student:</span><br> <span class="hljs-attr">name:</span> <span class="hljs-string">zs</span><br> <span class="hljs-attr">age:</span> <span class="hljs-number">18</span><br></code></pre></td></tr></table></figure><p><img src="https://s2.loli.net/2024/12/31/pFdJw1kBP8273i4.png" alt="image-20241231182304828"></p><p>在项目的<code>bootstrap.yml</code>文件中添加这些配置文件,项目启动后自动从nacos获取对应配置,优先级高于项目的<code>application.yml</code>配置(其实本地的配置删了都没问题,因为配置可以从nacos拉取)</p><figure class="highlight yml"><table><tr><td class="code"><pre><code class="hljs yml"><span class="hljs-attr">spring:</span><br> <span class="hljs-attr">cloud:</span><br> <span class="hljs-attr">nacos:</span><br> <span class="hljs-attr">config:</span><br> <span class="hljs-comment"># 添加这3个配置文件的相应配置</span><br> <span class="hljs-string">extension-configs[0]:</span><br> <span class="hljs-attr">data-id:</span> <span class="hljs-string">datasource.yml</span><br> <span class="hljs-attr">refresh:</span> <span class="hljs-literal">true</span><br> <span class="hljs-attr">group:</span> <span class="hljs-string">dev</span><br> <span class="hljs-string">extension-configs[1]:</span><br> <span class="hljs-attr">data-id:</span> <span class="hljs-string">mybatis.yml</span><br> <span class="hljs-attr">refresh:</span> <span class="hljs-literal">true</span><br> <span class="hljs-attr">group:</span> <span class="hljs-string">dev</span><br> <span class="hljs-string">extension-configs[2]:</span><br> <span class="hljs-attr">data-id:</span> <span class="hljs-string">others.yml</span><br> <span class="hljs-attr">refresh:</span> <span class="hljs-literal">true</span><br> <span class="hljs-attr">group:</span> <span class="hljs-string">dev</span><br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category> 后端 </category>
</categories>
<tags>
<tag> springcloud </tag>
<tag> nacos </tag>
</tags>
</entry>
<entry>
<title>shields.io标签生成</title>
<link href="/2024/03/08/shields.io%E6%A0%87%E7%AD%BE/"/>
<url>/2024/03/08/shields.io%E6%A0%87%E7%AD%BE/</url>
<content type="html"><![CDATA[<p><img src="https://s2.loli.net/2024/12/27/jbADFXHUvPEy8dV.png" alt="image-20241227135852435"></p><blockquote><p>github仓库页面的这种标签是怎么生成的?</p></blockquote><h2 id="1-基本使用"><a href="#1-基本使用" class="headerlink" title="1.基本使用"></a>1.基本使用</h2><p><a href="https://shields.io/badges">Static Badge | Shields.io</a> 可以生成各种标签,可以自定义,比如我想生成一个<mark>背景颜色</mark>蓝色标签,名称为<code>mytag</code>就可以使用<code>mytag-blue</code>,前面是名称后面是颜色(可以是16进制颜色)<img src="https://img.shields.io/badge/mytag-blue?style=flat" alt="mytag"></p><p><img src="https://s2.loli.net/2024/09/27/i1IeuUvKW4nFLHC.png" alt="image-20240308142750757"></p><h2 id="2-版本号"><a href="#2-版本号" class="headerlink" title="2.版本号"></a>2.版本号</h2><p>也可以指定版本号<code>mytag-v1.2.3-blue</code>,注意版本号在颜色前面<img src="https://img.shields.io/badge/mytag-v1.2.3-blue?style=flat" alt="version"></p><h2 id="3-图标"><a href="#3-图标" class="headerlink" title="3.图标"></a>3.图标</h2><p><a href="https://simpleicons.org/">Simple Icons</a> 提供了很多产品的logo,比如我想使用redis的logo,直接搜索redis,点击右下角可以复制名字,左下角可以复制配色</p><img src="https://s2.loli.net/2024/09/27/YuUsmk7tqvjQnfN.png" alt="image-20240308143650593" /><p>返回sheilds页面,点击展开显示更多参数</p><p><img src="https://s2.loli.net/2024/09/27/winJ5Dkdu2Hj38C.png" alt="image-20240308144647113"></p><p>生成后的效果 <img src="https://img.shields.io/badge/Redis-v7.0.12-%23DC382D?style=flat&logo=redis&logoColor=%23DC382D" alt="Static Badge"></p><h2 id="4-动态生成标签"><a href="#4-动态生成标签" class="headerlink" title="4.动态生成标签"></a>4.动态生成标签</h2><p>以仓库提交数为例,搜索commit,注意==cacheSeconds==的设置</p><img src="https://s2.loli.net/2024/09/27/oKbY3Hk5aQ2OwWD.png" alt="image-20240309173715898" style="zoom:80%;" /><p><img src="https://img.shields.io/github/commit-activity/t/tankingcao/java_design?style=flat&labelColor=red&cacheSeconds=3600" alt="GitHub commit activity"></p><table><thead><tr><th align="center">标签</th><th align="center">搜索关键字</th><th align="center">示例</th></tr></thead><tbody><tr><td align="center">仓库提交数</td><td align="center"><code>commit</code></td><td align="center"><img src="https://img.shields.io/github/commit-activity/t/tankingcao/java_design?style=flat&labelColor=red&cacheSeconds=3600" alt="GitHub commit activity"></td></tr><tr><td align="center">仓库发行版本</td><td align="center"><code>github release</code></td><td align="center"><img src="https://img.shields.io/github/v/release/tankingcao/java_design?include_prereleases&sort=date&display_name=release&style=flat&cacheSeconds=3600" alt="GitHub Release"></td></tr></tbody></table>]]></content>
<categories>
<category> tools </category>
</categories>
<tags>
<tag> shields.io </tag>
<tag> 标签 </tag>
</tags>
</entry>
<entry>
<title>markdown从入门到上天</title>
<link href="/2024/03/01/markdown%E4%BD%BF%E7%94%A8/"/>
<url>/2024/03/01/markdown%E4%BD%BF%E7%94%A8/</url>
<content type="html"><![CDATA[<h1 id="Markdown"><a href="#Markdown" class="headerlink" title="Markdown"></a>Markdown</h1><h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><blockquote><p><a href="https://markdown.com.cn/">Markdown</a> 是一种轻量级的标记语言,可用于在纯文本文档中添加格式化元素。Markdown 由 John Gruber 于 2004 年创建,如今已成为世界上最受欢迎的标记语言之一</p><ol><li>专注于文字内容;</li><li>纯文本,易读易写,可以方便地纳入版本控制;</li><li>语法简单,没有什么学习成本,能轻松在码字的同时做出美观大方的排版。</li></ol></blockquote><h2 id="基本使用"><a href="#基本使用" class="headerlink" title="基本使用"></a>基本使用</h2><h3 id="字体"><a href="#字体" class="headerlink" title="字体"></a>字体</h3><figure class="highlight markdown"><table><tr><td class="code"><pre><code class="hljs markdown"><span class="hljs-emphasis">*斜体文本*</span><br><span class="hljs-emphasis">_斜体文本_</span><br><span class="hljs-strong">**粗体文本**</span><br><span class="hljs-strong">__粗体文本__</span><br><span class="hljs-strong">**<span class="hljs-emphasis">*粗斜体文本*</span>**</span><br><span class="hljs-strong">__<span class="hljs-emphasis">_粗斜体文本_</span>__</span><br></code></pre></td></tr></table></figure><p><em>斜体文本</em>,<kbd>Ctrl I</kbd>(此处只针对<code>typora</code>编辑器)<br><em>斜体文本</em><br><strong>粗体文本</strong>,<kbd>Ctrl B</kbd><br><strong>粗体文本</strong><br><em><strong>粗斜体文本</strong></em><br><em><strong>粗斜体文本</strong></em></p><h3 id="分割线"><a href="#分割线" class="headerlink" title="分割线"></a>分割线</h3><p><kbd>——- enter</kbd></p><figure class="highlight markdown"><table><tr><td class="code"><pre><code class="hljs markdown"><span class="hljs-strong">***</span><br><span class="hljs-strong"></span><br><span class="hljs-strong">* * *</span><br><span class="hljs-strong"></span><br><span class="hljs-strong">**</span><span class="hljs-strong">***</span><br><span class="hljs-strong"></span><br><span class="hljs-strong">- - -</span><br><span class="hljs-strong"></span><br><span class="hljs-strong">----------</span><br></code></pre></td></tr></table></figure><hr><hr><hr><hr><hr><h3 id="删除线"><a href="#删除线" class="headerlink" title="删除线"></a>删除线</h3><p>如果段落上的文字要添加删除线,只需要在文字的两端加上两个波浪线 <strong>~~</strong> 即可,实例如下:<del>clb.pages.dev</del></p><figure class="highlight markdown"><table><tr><td class="code"><pre><code class="hljs markdown">~~clb.pages.dev~~<br></code></pre></td></tr></table></figure><h3 id="下划线"><a href="#下划线" class="headerlink" title="下划线"></a>下划线</h3><p><u>下划线</u>可以通过 HTML 的 <code><u></code> 标签来实现:<u>带下划线文本</u></p><figure class="highlight html"><table><tr><td class="code"><pre><code class="hljs html"><span class="hljs-tag"><<span class="hljs-name">u</span>></span>带下划线文本<span class="hljs-tag"></<span class="hljs-name">u</span>></span><br></code></pre></td></tr></table></figure><h3 id="脚注"><a href="#脚注" class="headerlink" title="脚注"></a>脚注</h3><p>脚注是对文本的<strong>补充说明</strong>,鼠标悬浮后会显示内容,脚注一般放在文章最后</p><figure class="highlight markdown"><table><tr><td class="code"><pre><code class="hljs markdown">我会使用[^markdown]<br><br>[<span class="hljs-symbol">^markdown</span>]:<span class="hljs-link">Markdown 是一种轻量级标记语言,它允许人们使用易读易写的纯文本格式编写文档</span><br></code></pre></td></tr></table></figure><p>我会使用[^markdown]</p><p>[^markdown]:Markdown 是一种轻量级标记语言,它允许人们使用易读易写的纯文本格式编写文档</p><h3 id="列表"><a href="#列表" class="headerlink" title="列表"></a>列表</h3><p>Markdown 支持有序列表和无序列表</p><p>无序列表使用星号(<em>****)、加号(<strong>+</strong>)或是减号(</em>*-**)作为列表标记,这些标记后面要添加一个空格,然后再填写内容:</p><figure class="highlight markdown"><table><tr><td class="code"><pre><code class="hljs markdown"><span class="hljs-bullet">*</span> 第一项<br><span class="hljs-bullet">*</span> 第二项<br><span class="hljs-bullet">*</span> 第三项<br><br><span class="hljs-bullet">+</span> 第一项<br><span class="hljs-bullet">+</span> 第二项<br><span class="hljs-bullet">+</span> 第三项<br><br><span class="hljs-bullet">1.</span> one<br><span class="hljs-bullet">2.</span> two<br><span class="hljs-bullet">3.</span> three<br></code></pre></td></tr></table></figure><ul><li>第一项</li><li>第二项</li><li>第三项</li></ul><ol><li>one</li><li>two</li><li>three</li></ol><p>嵌套使用</p><figure class="highlight markdown"><table><tr><td class="code"><pre><code class="hljs markdown"><span class="hljs-bullet">-</span> 1<br><span class="hljs-bullet"> -</span> 1.1<br><span class="hljs-bullet"> -</span> 1.1.1<br><span class="hljs-bullet"> -</span> 1.1.1.1<br></code></pre></td></tr></table></figure><ul><li>1<ul><li>1.1<ul><li>1.1.1<ul><li>1.1.1.1</li></ul></li></ul></li></ul></li></ul><h3 id="引用"><a href="#引用" class="headerlink" title="引用"></a>引用</h3><p>Markdown 引用是在段落开头使用 <strong>></strong> 符号 ,然后后面紧跟一个<strong>空格</strong>符号:</p><figure class="highlight markdown"><table><tr><td class="code"><pre><code class="hljs markdown"><span class="hljs-quote">> 引用</span><br><span class="hljs-quote">> 123</span><br><span class="hljs-quote">> 456</span><br></code></pre></td></tr></table></figure><blockquote><p>引用<br>123<br>456</p></blockquote><p>可嵌套</p><blockquote><p>123</p><blockquote><p>456</p></blockquote></blockquote><h3 id="代码"><a href="#代码" class="headerlink" title="代码"></a>代码</h3><p>如果是段落上的一个函数或片段的代码可以用反引号把它包起来(**`**),例如:<code>print()</code></p><figure class="highlight markdown"><table><tr><td class="code"><pre><code class="hljs markdown"><span class="hljs-code">`print()`</span><br></code></pre></td></tr></table></figure><p>你也可以用 <strong>```</strong> 包裹一段代码,并指定一种语言(也可以不指定):</p><figure class="highlight markdown"><table><tr><td class="code"><pre><code class="hljs markdown"><span class="hljs-code">```python</span><br><span class="hljs-code">print(666)</span><br><span class="hljs-code">```</span><br></code></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-built_in">print</span>(<span class="hljs-number">666</span>)<br></code></pre></td></tr></table></figure><hr><h3 id="链接"><a href="#链接" class="headerlink" title="链接"></a>链接</h3><p>链接使用方法如下:</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><code class="hljs plaintext">[链接名称](链接地址)<br><br>或者<br><br><链接地址><br></code></pre></td></tr></table></figure><h4 id="基本使用-1"><a href="#基本使用-1" class="headerlink" title="基本使用"></a>基本使用</h4><p><a href="https://clb.pages.dev/">我的主页</a></p><figure class="highlight markdown"><table><tr><td class="code"><pre><code class="hljs markdown">[<span class="hljs-string">我的主页</span>](<span class="hljs-link">https://clb.pages.dev</span>)<br></code></pre></td></tr></table></figure><h4 id="直接使用链接"><a href="#直接使用链接" class="headerlink" title="直接使用链接"></a>直接使用链接</h4><p><a href="https://clb.pages.dev/">https://clb.pages.dev</a></p><figure class="highlight markdown"><table><tr><td class="code"><pre><code class="hljs markdown"><span class="language-xml"><https://clb.pages.dev></span><br></code></pre></td></tr></table></figure><h4 id="高级链接"><a href="#高级链接" class="headerlink" title="高级链接"></a>高级链接</h4><blockquote><p>如果我的文档中<strong>很多地方都需要使用一个网址</strong>,可以将这个网址<strong>定义为一个变量</strong>放在文章末尾,其他需要使用这个网址的地方直接<strong>通过变量名访问</strong></p></blockquote><figure class="highlight markdown"><table><tr><td class="code"><pre><code class="hljs markdown">这是我的[<span class="hljs-string">主页</span>][<span class="hljs-symbol">home</span>]<br><br>这也是我的[<span class="hljs-string">主页</span>][<span class="hljs-symbol">home</span>]<br><br>这还是我的[<span class="hljs-string">主页</span>][<span class="hljs-symbol">home</span>]<br><br>[<span class="hljs-symbol">home</span>]:<span class="hljs-link">https://clb.pages.dev</span><br></code></pre></td></tr></table></figure><p>这是我的<a href="https://clb.pages.dev/" title="我的博客主页">主页</a></p><p>这也是我的<a href="https://clb.pages.dev/" title="我的博客主页">主页</a></p><p>这还是我的<a href="https://clb.pages.dev/" title="我的博客主页">主页</a></p><h3 id="图片"><a href="#图片" class="headerlink" title="图片"></a>图片</h3><p>Markdown 图片语法格式如下:</p><figure class="highlight markdown"><table><tr><td class="code"><pre><code class="hljs markdown">![<span class="hljs-string">alt 属性文本</span>](<span class="hljs-link">图片地址</span>)<br>![<span class="hljs-string">alt 属性文本</span>](<span class="hljs-link">图片地址 "鼠标悬浮时的提示文本"</span>)<br><br>![](<span class="hljs-link">https://s2.loli.net/2024/06/02/wuJknzxaFigDSdL.gif</span>)<br></code></pre></td></tr></table></figure><ul><li>开头一个感叹号 !</li><li>接着一个方括号,里面放上图片的替代文字(可以省略)</li><li>接着一个普通括号,里面放上图片的网址,最后还可以用引号包住并加上选择性的 ‘鼠标悬浮时的提示文本’(可以省略)</li><li>图片的链接也可以定义为变量后使用变量名使用</li><li><strong>如果要改变图片大小,只能使用<code>img</code>标签设定相应属性</strong></li></ul><p><img src="https://s2.loli.net/2024/06/02/wuJknzxaFigDSdL.gif" alt="boqi"></p><h3 id="表格"><a href="#表格" class="headerlink" title="表格"></a>表格</h3><p>制作表格使用 <strong>|</strong> 来分隔不同的单元格,使用 <strong>-</strong> 来分隔表头和其他行</p><h4 id="基本使用-2"><a href="#基本使用-2" class="headerlink" title="基本使用"></a>基本使用</h4><figure class="highlight markdown"><table><tr><td class="code"><pre><code class="hljs markdown">| 表头 | 表头 |<br>| ---- | ---- |<br>| 1 | 2|<br>| 3 | 4|<br></code></pre></td></tr></table></figure><table><thead><tr><th>表头</th><th>表头</th></tr></thead><tbody><tr><td>1</td><td>2</td></tr><tr><td>3</td><td>4</td></tr></tbody></table><h4 id="对齐方式"><a href="#对齐方式" class="headerlink" title="对齐方式"></a>对齐方式</h4><p><strong>我们可以设置表格的对齐方式:</strong></p><ul><li><strong>-:</strong> 设置内容和标题栏居右对齐。</li><li><strong>:-</strong> 设置内容和标题栏居左对齐。</li><li><strong>:-:</strong> 设置内容和标题栏居中对齐。</li></ul><p>实例如下:</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><code class="hljs plaintext">| 左对齐 | 右对齐 | 居中对齐 |<br>| :-----| ----: | :----: |<br>| 单元格 | 单元格 | 单元格 |<br>| 单元格 | 单元格 | 单元格 |<br></code></pre></td></tr></table></figure><blockquote><p><strong>表格语法比较繁琐,建议使用编辑器插入表格功能</strong></p></blockquote><h2 id="进阶"><a href="#进阶" class="headerlink" title="进阶"></a>进阶</h2><h3 id="HTML-元素"><a href="#HTML-元素" class="headerlink" title="HTML 元素"></a>HTML 元素</h3><p>不在 Markdown 涵盖范围之内的标签,都可以直接在文档里面用 HTML 撰写</p><p>目前支持的 HTML 元素有:<code><kbd> <b> <i> <em> <sup> <sub> <br></code>等</p><p>在Markdown中,您可以使用多种HTML标签。行内元素,如<code><span></code>、<code><cite></code>、<code><del></code>,可以在Markdown的段落、列表或标题中自由使用。此外,您还可以使用<code><a></code>和<code><img></code>等标签来创建链接和图片。</p><p>对于块级元素,如<code><div></code>、<code><table></code>、<code><pre></code>和<code><p></code>,您需要在它们前后添加空行,并且不要使用制表符或空格进行缩进。请注意,在HTML块级标签内部,Markdown语法将不会被处理。例如,<code><p>italic and **bold**</p></code>将不会显示为斜体和粗体。</p><p><strong>出于安全考虑,并非所有Markdown应用程序都支持在Markdown文档中添加HTML。如果您不确定您的应用程序支持哪些标签,请查看相应的手册或文档</strong></p><p>这里有一个简单的例子,展示了如何在Markdown中混合使用HTML和Markdown语法:</p><figure class="highlight markdown"><table><tr><td class="code"><pre><code class="hljs markdown">这是一个普通段落。<br><br><span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">span</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"color: red;"</span>></span></span>这段文字将显示为红色。<span class="language-xml"><span class="hljs-tag"></<span class="hljs-name">span</span>></span></span><br><br><span class="hljs-emphasis">*这是Markdown格式的斜体*</span><br><br><span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">div</span>></span></span><br>这是一个HTML块级元素。<br><span class="language-xml"><span class="hljs-tag"></<span class="hljs-name">div</span>></span></span><br><br><span class="hljs-strong">**这是Markdown格式的粗体**</span><br></code></pre></td></tr></table></figure><p>以上Markdown代码将产生以下效果:</p><p>这是一个普通段落</p><p><span style="color: red;">这段文字将显示为红色。</span></p><p><em>这是Markdown格式的斜体</em></p><div> 这是一个HTML块级元素。 </div><p><strong>这是Markdown格式的粗体</strong></p><hr><h3 id="任务列表"><a href="#任务列表" class="headerlink" title="任务列表"></a>任务列表</h3><p>任务列表使您可以创建带有<strong>复选框</strong>的项目列表。在支持任务列表的Markdown应用程序中,复选框将显示在内容旁边。要创建任务列表,请在任务列表项之前添加破折号<code>-</code>和方括号<code>[ ]</code>,并在<code>[ ]</code>前面加上空格。要选择一个复选框,请在方括号<code>[x]</code>之间添加 x</p><figure class="highlight text"><table><tr><td class="code"><pre><code class="hljs text">- [x] Write the press release<br>- [ ] Update the website<br>- [ ] Contact the media<br></code></pre></td></tr></table></figure><p><img src="https://s2.loli.net/2024/09/27/ODtdpIVmSHABYTL.png" alt="image-20240927134014360"></p><ul><li><input checked="" disabled="" type="checkbox"> Write the press release</li><li><input disabled="" type="checkbox"> Update the website</li><li><input disabled="" type="checkbox"> Contact the media</li></ul><hr><h3 id="Emoji-表情"><a href="#Emoji-表情" class="headerlink" title="Emoji 表情"></a>Emoji 表情</h3><p>有两种方法可以将表情符号添加到Markdown文件中:将表情符号复制并粘贴到Markdown格式的文本中,或者键入<em>emoji shortcodes</em>。</p><h4 id="复制和粘贴表情符号"><a href="#复制和粘贴表情符号" class="headerlink" title="复制和粘贴表情符号"></a>复制和粘贴表情符号</h4><p>在大多数情况下,您可以简单地从<a href="https://emojipedia.org/">Emojipedia</a> 等来源复制表情符号并将其粘贴到文档中。许多Markdown应用程序会自动以Markdown格式的文本显示表情符号。从Markdown应用程序导出的HTML和PDF文件应显示表情符号</p><p><strong>Tip:</strong> 如果您使用的是静态网站生成器,请确保将HTML页面编码为UTF-8</p><h4 id="使用表情符号简码"><a href="#使用表情符号简码" class="headerlink" title="使用表情符号简码"></a>使用表情符号简码</h4><p>一些Markdown应用程序允许您通过键入表情符号短代码来插入表情符号。这些以冒号开头和结尾,并包含表情符号的名称。</p><figure class="highlight markdown"><table><tr><td class="code"><pre><code class="hljs markdown">多一眼看一眼就会:boom:<br>真好笑! :joy:<br></code></pre></td></tr></table></figure><p><img src="https://s2.loli.net/2024/09/27/OGM4dRCVp7zyb3H.png" alt="image-20240927134104161"></p><p><strong>Note:</strong> 注意:您可以使用此<a href="https://gist.github.com/rxaviers/7360908">表情符号简码列表</a>,但请记住,表情符号简码因应用程序而异。有关更多信息,请参阅Markdown应用程序的文档</p><hr><h3 id="LaTex-数学公式"><a href="#LaTex-数学公式" class="headerlink" title="LaTex 数学公式"></a>LaTex 数学公式</h3><p><a href="https://mohu.org/info/symbols/symbols.htm">参考文章</a></p><p>行内使用$$包围 $a = \log_{10}100$</p><p>行间使用$$$$包围,也可以行内使用<br>$$<br>a = \log_{10}100<br>$$</p><ul><li>分数: \frac {a} {b} $\frac{a}{b}$</li><li>根号: \sqrt[3]{x^4} $\sqrt[3]{x^4}$</li><li>点乘: a \cdot b $a \cdot b$</li><li>叉乘: a \times b $a \times b$</li><li>上标: a^2 b^{1+2} $a^2$ $b^{1+2}$ <strong>如果上标是表达式,则加{},下同</strong></li><li>下标: a_i $a_6$ $a_{1+3}$</li><li>积分: <strong>\int_{a}^{b}</strong> $\int_{a}^{b}$</li><li>正弦: \sin{} $\sin{x}$</li><li>余弦: \cos{} $\cos{(y+x)}$</li><li>对数: <strong>\log_{6}</strong> $\log_{6}10$</li><li>大于号:(\textgreater) $\textgreater$</li><li>小于号:(\textless)</li><li>大于等于:(\geq) $\geq$</li><li>小于等于:(\leq) $\leq$</li></ul><h3 id="github风格警告框"><a href="#github风格警告框" class="headerlink" title="github风格警告框"></a>github风格警告框</h3><img src="https://s2.loli.net/2024/09/27/LIhvkjbP2WFJAQw.png" alt="image-20240927134137634" style="zoom:80%;" /><blockquote><p>[!NOTE]</p><p>提醒 <code>> [!note]</code> 该风格警告框适用于github,其他网站不保证兼容性</p></blockquote><blockquote><p>[!TIP]</p><p>建议 <code>> [!tip]</code></p></blockquote><blockquote><p>[!IMPORTANT]</p><p>重要 <code>[!important]</code></p></blockquote><blockquote><p>[!WARNING]</p><p>警告 <code>[!warning]</code></p></blockquote><blockquote><p>[!CAUTION]</p><p>注意 <code>[!caution]</code></p></blockquote><h3 id="IFrame标签"><a href="#IFrame标签" class="headerlink" title="IFrame标签"></a>IFrame标签</h3><h4 id="网页"><a href="#网页" class="headerlink" title="网页"></a>网页</h4><iframe src="https://ikun-ai.pages.dev" width="200" height="500" scrolling="no"/><h4 id="音乐播放器"><a href="#音乐播放器" class="headerlink" title="音乐播放器"></a>音乐播放器</h4><audio controls style="width: 800px;padding: 10px;margin: 20px auto;"> <source src="https://i-120.wwentua.com:446/04262200175320781bb/2024/04/16/090933971d80726c897bc21383fe7ace.mp3?st=DWYBqzF1MlCePRFfzBIRoQ&e=1714142574&b=VUsMQwhbA0NZXgR_aB7AGswXjC7FR5FT3Cb0Mj1TXU7RVvwmBV3lVPVl8BWA_c&fi=175320781&pid=223-167-164-3&up=2&mp=0&co=0" type="audio/mpeg"> 您的浏览器不支持 audio 元素。</audio>## typora<h3 id="自定义主题"><a href="#自定义主题" class="headerlink" title="自定义主题"></a><a href="https://theme.typora.io/doc/zh/Write-Custom-Theme/">自定义主题</a></h3><blockquote><p>在通用设置中打开调试模式</p></blockquote><p><img src="https://s2.loli.net/2024/09/27/RA8tVjh9JW3Mm47.png" alt="image-20240927134259142"></p><p>示例:假设我想修改<strong>引用前面竖杠</strong>的颜色</p><p><img src="https://s2.loli.net/2024/03/23/AgUN2Ve7P1dCaXz.gif" alt="recording"></p><p>步骤大概如下:</p><ol><li>右键检查元素</li><li>使用左上角的选择器选择引用框</li><li>在右边样式中找到<code>boder-left</code>样式,修改颜色直到满意为止</li></ol><blockquote><p><strong>这样修改只是暂时的,如果想要永久生效就要找到对应主题的css文件,找到刚才的属性修改好保存并重启typora(或切换2次主题重新加载当前主题)</strong></p></blockquote><ul><li><p>样式中信息说明这个属性在<code>maize.css</code>文件第<code>188</code>行</p></li><li><p>先复制修改好的颜色属性,找到<code>maize.css</code>(这是我使用的主题文件,不同主题是不一样的)</p></li><li><p>修改对应属性并保存</p></li><li><p>重启typora</p></li></ul><blockquote><p>你也可以直接将修改好css属性直接粘贴到主题css文件的最后进行覆盖,这样就不用找对应的css在哪了</p><p><span style="color:#ff6b6b;font-weight:bold;">最推荐的办法是在主题文件夹下创建一个文件名为 你使用的主题.user.css文件(例如我使用的是maize,则为maize.user.css),将要修改的属性直接加入这个文件即可</span></p><p><strong>这种办法适用于typora中大多数样式</strong></p></blockquote><hr><h3 id="图床"><a href="#图床" class="headerlink" title="图床"></a>图床</h3><blockquote><p>markdown中插入的图片会放到一个文件夹下,如果要将文档发给其他人查看,必须将图片全部一起发过去,并且md文件和图片文件夹要放在一起,路径对不上的话所有图片都会加载不出来,非常的麻烦,一般md中插入图片使用图片的url地址,只要有网就能加载</p></blockquote><p>可以使用一些免费图床(有风险),或者使用<code>github</code>搭建免费图床(<strong>不推荐</strong>),最好是使用付费图床(稳定),可自行上网搜索怎么使用</p><h3 id="自定义快捷键"><a href="#自定义快捷键" class="headerlink" title="自定义快捷键"></a>自定义快捷键</h3><p>打开高级设置</p><p>打开<code>conf.user.json</code>文件,添加快捷键,快捷键名称必须是在菜单栏中存在的,这是我设置的一些快捷键</p><p><img src="https://s2.loli.net/2024/04/01/dkI7sJr6v1nXw3j.png" alt="image-20240323141230241"></p><hr>]]></content>
<categories>
<category> tools </category>
</categories>
<tags>
<tag> markdown </tag>
</tags>
</entry>
<entry>
<title>pinia-vue存储库</title>
<link href="/2024/02/20/pinia-vue%E5%AD%98%E5%82%A8%E5%BA%93/"/>
<url>/2024/02/20/pinia-vue%E5%AD%98%E5%82%A8%E5%BA%93/</url>
<content type="html"><![CDATA[<h2 id="Pinia"><a href="#Pinia" class="headerlink" title="Pinia"></a><a href="https://pinia.vuejs.org/zh/">Pinia</a></h2><blockquote><p>[!note]</p><p>Pinia是什么?</p><p>Pinia 是 ==Vue 的存储库==,它允许您跨组件/页面共享状态</p><p>vue项目中有很多页面view,这些view之间相互独立,登录页面会拿到后端传回的token,但是其他页面并没有token,可以将token保存在<code>pinia</code>,其他页面都可以访问pinia(相当于全局变量),另外pinia基于内存存储,刷新浏览器数据就会丢失,使用<code>persist</code>插件可以将数据==持久化==</p></blockquote><p><img src="https://s2.loli.net/2024/09/27/dThzcYPij8eMZWb.png"></p><h2 id="1-安装"><a href="#1-安装" class="headerlink" title="1.安装"></a>1.安装</h2><p>用你最喜欢的包管理器安装 <code>pinia</code>:</p><figure class="highlight shell"><table><tr><td class="code"><pre><code class="hljs shell">yarn add pinia<br><span class="hljs-meta prompt_"># </span><span class="language-bash">或者使用 npm</span><br>npm install pinia<br></code></pre></td></tr></table></figure><p>安装pinia持久化插件<code>persist</code></p><figure class="highlight shell"><table><tr><td class="code"><pre><code class="hljs shell">yarn add pinia-persistedstate-plugin<br><span class="hljs-meta prompt_"># </span><span class="language-bash">或者使用 npm</span><br>npm install pinia-persistedstate-plugin<br></code></pre></td></tr></table></figure><h2 id="2-使用"><a href="#2-使用" class="headerlink" title="2.使用"></a>2.使用</h2><p>2.1 在<code>main.js</code>中导入pinia和persist并使用</p><figure class="highlight js"><table><tr><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">import</span> {createApp} <span class="hljs-keyword">from</span> <span class="hljs-string">'vue'</span><br><span class="hljs-keyword">import</span> <span class="hljs-title class_">App</span> <span class="hljs-keyword">from</span> <span class="hljs-string">'./App.vue'</span><br><br><span class="hljs-keyword">import</span> {createPinia} <span class="hljs-keyword">from</span> <span class="hljs-string">"pinia"</span>;<br><span class="hljs-keyword">import</span> {createPersistedState} <span class="hljs-keyword">from</span> <span class="hljs-string">"pinia-persistedstate-plugin"</span>;<br><br><span class="hljs-keyword">const</span> app = <span class="hljs-title function_">createApp</span>(<span class="hljs-title class_">App</span>)<br><br><span class="hljs-comment">// 使用pinia和persist保存状态并持久化</span><br><span class="hljs-keyword">const</span> pinia = <span class="hljs-title function_">createPinia</span>();<br><span class="hljs-keyword">const</span> persist = <span class="hljs-title function_">createPersistedState</span>();<br>pinia.<span class="hljs-title function_">use</span>(persist);<br><br><span class="hljs-comment">// 使用pinia</span><br>app.<span class="hljs-title function_">use</span>(pinia);<br><br>app.<span class="hljs-title function_">mount</span>(<span class="hljs-string">'#app'</span>)<br></code></pre></td></tr></table></figure><p>2.2 以保存token为例,创建<code>token.js</code>文件</p><figure class="highlight js"><table><tr><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">import</span> {defineStore} <span class="hljs-keyword">from</span> <span class="hljs-string">"pinia"</span>;<br><span class="hljs-keyword">import</span> {ref} <span class="hljs-keyword">from</span> <span class="hljs-string">'vue'</span>;<br><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> useTokenStore = <span class="hljs-title function_">defineStore</span>(<span class="hljs-string">'token'</span>, <span class="hljs-function">() =></span> {<br> <span class="hljs-comment">//1.定义描述token</span><br> <span class="hljs-keyword">const</span> token = <span class="hljs-title function_">ref</span>(<span class="hljs-string">''</span>)<br> <span class="hljs-comment">//2.定义修改token的方法</span><br> <span class="hljs-keyword">const</span> <span class="hljs-title function_">setToken</span> = (<span class="hljs-params">newToken</span>) => {<br> token.<span class="hljs-property">value</span> = newToken<br> }<br> <span class="hljs-comment">//3.定义移除token的方法</span><br> <span class="hljs-keyword">const</span> <span class="hljs-title function_">removeToken</span> = (<span class="hljs-params"></span>) => {<br> token.<span class="hljs-property">value</span> = <span class="hljs-string">''</span><br> }<br> <span class="hljs-keyword">return</span> {<br> token, setToken, removeToken<br> }<br> },<br> {<br> <span class="hljs-comment">//使用persis插件持久化</span><br> <span class="hljs-attr">persis</span>: <span class="hljs-literal">true</span><br> }<br>)<br></code></pre></td></tr></table></figure><p>2.3 在其他页面读取token</p><figure class="highlight js"><table><tr><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">import</span> { useTokenStore } <span class="hljs-keyword">from</span> <span class="hljs-string">"@/stores/token"</span>;<br><br><span class="hljs-keyword">const</span> tokenStore = <span class="hljs-title function_">useTokenStore</span>();<br><br><span class="hljs-comment">// 设置token</span><br>tokenStore.<span class="hljs-title function_">setToken</span>(<span class="hljs-string">'your token'</span>);<br><span class="hljs-comment">// 获取token</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">"tokenStore:"</span> + tokenStore.<span class="hljs-property">token</span>);<br><span class="hljs-comment">// 清除token</span><br>tokenStore.<span class="hljs-title function_">removeToken</span>();<br></code></pre></td></tr></table></figure><h2 id="3-其他示例"><a href="#3-其他示例" class="headerlink" title="3.其他示例"></a>3.其他示例</h2><p><code>reader.js</code></p><figure class="highlight js"><table><tr><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">import</span> {defineStore} <span class="hljs-keyword">from</span> <span class="hljs-string">"pinia"</span>;<br><span class="hljs-keyword">import</span> {ref} <span class="hljs-keyword">from</span> <span class="hljs-string">'vue'</span>;<br><br><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 保存登录时用户的信息</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> useReaderStore = <span class="hljs-title function_">defineStore</span>(<span class="hljs-string">'reader'</span>, <span class="hljs-function">() =></span> {<br> <span class="hljs-keyword">const</span> reader = <span class="hljs-title function_">ref</span>({<br> <span class="hljs-attr">id</span>: <span class="hljs-string">''</span>,<br> <span class="hljs-attr">username</span>: <span class="hljs-string">''</span>,<br> <span class="hljs-attr">nickname</span>: <span class="hljs-string">''</span>,<br> <span class="hljs-attr">gender</span>: <span class="hljs-string">''</span>,<br> <span class="hljs-attr">age</span>: <span class="hljs-string">''</span>,<br> <span class="hljs-attr">tel</span>: <span class="hljs-string">''</span>,<br> <span class="hljs-attr">token</span>: <span class="hljs-string">''</span><br> });<br> <span class="hljs-keyword">const</span> <span class="hljs-title function_">setReader</span> = (<span class="hljs-params">data</span>) => {<br> reader.<span class="hljs-property">value</span> = data;<br> }<br> <span class="hljs-comment">//清除信息</span><br> <span class="hljs-keyword">const</span> <span class="hljs-title function_">clearReader</span> = (<span class="hljs-params"></span>) => {<br> reader.<span class="hljs-property">value</span>.<span class="hljs-property">id</span> = <span class="hljs-string">''</span>;<br> reader.<span class="hljs-property">value</span>.<span class="hljs-property">username</span> = <span class="hljs-string">''</span>;<br> reader.<span class="hljs-property">value</span>.<span class="hljs-property">nickname</span> = <span class="hljs-string">''</span>;<br> reader.<span class="hljs-property">value</span>.<span class="hljs-property">gender</span> = <span class="hljs-string">''</span>;<br> reader.<span class="hljs-property">value</span>.<span class="hljs-property">age</span> = <span class="hljs-string">''</span>;<br> reader.<span class="hljs-property">value</span>.<span class="hljs-property">tel</span> = <span class="hljs-string">''</span>;<br> reader.<span class="hljs-property">value</span>.<span class="hljs-property">token</span> = <span class="hljs-string">''</span>;<br> }<br> <span class="hljs-keyword">return</span> {<br> reader, setReader, clearReader<br> }<br> },<br> <span class="hljs-comment">// 持久化,pinia保存在内存中,刷新即丢失</span><br> {<br> <span class="hljs-attr">persis</span>: <span class="hljs-literal">true</span><br> }<br>)<br></code></pre></td></tr></table></figure><p><code>admin.js</code></p><figure class="highlight js"><table><tr><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">import</span> {defineStore} <span class="hljs-keyword">from</span> <span class="hljs-string">"pinia"</span>;<br><span class="hljs-keyword">import</span> {ref} <span class="hljs-keyword">from</span> <span class="hljs-string">'vue'</span>;<br><br><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 保存登录时的管理员信息</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> useAdminStore = <span class="hljs-title function_">defineStore</span>(<span class="hljs-string">'admin'</span>, <span class="hljs-function">() =></span> {<br> <span class="hljs-keyword">const</span> admin = <span class="hljs-title function_">ref</span>({<br> <span class="hljs-attr">id</span>: <span class="hljs-string">''</span>,<br> <span class="hljs-attr">username</span>: <span class="hljs-string">''</span>,<br> <span class="hljs-attr">nickname</span>: <span class="hljs-string">''</span>,<br> <span class="hljs-attr">token</span>:<span class="hljs-string">''</span><br> });<br> <span class="hljs-comment">// 是否为管理员</span><br> <span class="hljs-keyword">let</span> isAdmin = <span class="hljs-title function_">ref</span>();<br> <span class="hljs-keyword">const</span> <span class="hljs-title function_">setIsAdmin</span> = (<span class="hljs-params">flag</span>)=>{<br> isAdmin.<span class="hljs-property">value</span> = flag;<br> }<br> <span class="hljs-keyword">const</span> <span class="hljs-title function_">setAdmin</span> = (<span class="hljs-params">data</span>) => {<br> admin.<span class="hljs-property">value</span> = data;<br> }<br> <span class="hljs-comment">//清除信息</span><br> <span class="hljs-keyword">const</span> <span class="hljs-title function_">clearAdmin</span> = (<span class="hljs-params"></span>)=>{<br> admin.<span class="hljs-property">value</span>.<span class="hljs-property">id</span> = <span class="hljs-string">''</span>;<br> admin.<span class="hljs-property">value</span>.<span class="hljs-property">username</span> = <span class="hljs-string">''</span>;<br> admin.<span class="hljs-property">value</span>.<span class="hljs-property">nickname</span> = <span class="hljs-string">''</span>;<br> admin.<span class="hljs-property">value</span>.<span class="hljs-property">token</span> = <span class="hljs-string">''</span>;<br> }<br><br> <span class="hljs-keyword">return</span> {<br> admin, setAdmin,isAdmin,setIsAdmin,clearAdmin<br> }<br> },<br> {<br> <span class="hljs-attr">persis</span>: <span class="hljs-literal">true</span><br> }<br>)<br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category> 前端 </category>
<category> vue </category>
</categories>
<tags>
<tag> pinia </tag>
</tags>
</entry>
<entry>
<title>redis二次校验实现jwt主动“失效”</title>
<link href="/2024/02/19/jwt%E4%BB%A4%E7%89%8C%E4%B8%BB%E5%8A%A8%E5%A4%B1%E6%95%88/"/>
<url>/2024/02/19/jwt%E4%BB%A4%E7%89%8C%E4%B8%BB%E5%8A%A8%E5%A4%B1%E6%95%88/</url>
<content type="html"><![CDATA[<blockquote><p>问题:jwt令牌一旦生成,就不能再更改,有时候想让令牌提前失效该怎么办?</p><p>解决方案:使用redis对token进行二次校验,由redis来管理token的过期时间</p></blockquote><h2 id="1-保存token到redis"><a href="#1-保存token到redis" class="headerlink" title="1.保存token到redis"></a>1.保存token到redis</h2><p>在用户登录方法中,生成token,并保存一份到redis中</p><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Override</span><br><span class="hljs-keyword">public</span> Result<ReaderVo> <span class="hljs-title function_">login</span><span class="hljs-params">(LoginDto reader)</span> {<br> <span class="hljs-type">Reader</span> <span class="hljs-variable">r</span> <span class="hljs-operator">=</span> readerMapper.selectByName(reader.getUsername());<br> <span class="hljs-comment">//用户不存在</span><br> <span class="hljs-keyword">if</span> (r == <span class="hljs-literal">null</span>) {<br> <span class="hljs-keyword">return</span> Result.error(Excep.USER_NOT_EXIST);<br> }<br> <span class="hljs-type">String</span> <span class="hljs-variable">pwd</span> <span class="hljs-operator">=</span> r.getPassword();<br> <span class="hljs-comment">//密码错误</span><br> <span class="hljs-keyword">if</span> (!pwd.equals(reader.getPassword())) {<br> <span class="hljs-keyword">return</span> Result.error(Excep.WRONG_PASSWORD);<br> }<br><br> <span class="hljs-comment">//生成令牌,在有效载荷中存储用户名和id</span><br> Map<String, Object> claims = <span class="hljs-keyword">new</span> <span class="hljs-title class_">HashMap</span><>();<br> claims.put(Common.ID, r.getId());<br> claims.put(Common.USERNAME, reader.getUsername());<br> <span class="hljs-type">String</span> <span class="hljs-variable">token</span> <span class="hljs-operator">=</span> JwtUtils.generateJwt(claims);<br><br> <span class="hljs-comment">// 将令牌保存到redis中</span><br> redisTemplate.opsForValue().set(token, token, Jwt.EXPIRE_TIME);<br><br> <span class="hljs-comment">// 封装信息并返回</span><br> <span class="hljs-type">ReaderVo</span> <span class="hljs-variable">readerVo</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">ReaderVo</span>();<br> readerVo.setToken(token);<br> BeanUtils.copyProperties(r, readerVo);<br><br> <span class="hljs-keyword">return</span> Result.success(readerVo);<br>}<br></code></pre></td></tr></table></figure><h2 id="2-二次校验token"><a href="#2-二次校验token" class="headerlink" title="2.二次校验token"></a>2.二次校验token</h2><p>在jwt拦截器中,校验redis中是否存在token,如果不存在,说明token已经过期</p><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Override</span><br><span class="hljs-keyword">public</span> <span class="hljs-type">boolean</span> <span class="hljs-title function_">preHandle</span><span class="hljs-params">(HttpServletRequest request, HttpServletResponse response, Object handler)</span> {<br> <span class="hljs-comment">// 静态资源直接放行</span><br> <span class="hljs-keyword">if</span> (!(handler <span class="hljs-keyword">instanceof</span> HandlerMethod)) {<br> <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;<br> }<br><br> <span class="hljs-comment">//token为空,不放行</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">token</span> <span class="hljs-operator">=</span> request.getHeader(Common.TOKEN);<br> log.debug(<span class="hljs-string">"token:{}"</span>, token);<br> <span class="hljs-keyword">if</span> (!StringUtils.hasLength(token)) {<br> log.error(Excep.TOKEN_NOT_EXIST);<br> response.setStatus(Code.NOT_LOGIN_CODE);<br> <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;<br> }<br><br> <span class="hljs-keyword">try</span> {<br> <span class="hljs-comment">// 校验redis中的令牌是否过期</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">redisToken</span> <span class="hljs-operator">=</span> redisTemplate.opsForValue().get(token);<br> <span class="hljs-keyword">if</span> (redisToken == <span class="hljs-literal">null</span>) {<br> <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">BaseException</span>(Excep.TOKEN_ALREADY_EXPIRED);<br> }<br> <span class="hljs-comment">//解析</span><br> <span class="hljs-type">Claims</span> <span class="hljs-variable">claims</span> <span class="hljs-operator">=</span> JwtUtils.parseJWT(token);<br> <span class="hljs-comment">// 保存用户信息</span><br> ThreadLocalUtil.set(claims);<br> <span class="hljs-type">String</span> <span class="hljs-variable">format</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">SimpleDateFormat</span>(<span class="hljs-string">"yyyy-MM-dd HH:mm:ss"</span>).format(claims.getExpiration());<br> log.debug(<span class="hljs-string">"id:{},username:{},令牌到期时间:{}"</span>, claims.get(Common.ID), claims.get(Common.USERNAME), format);<br> } <span class="hljs-keyword">catch</span> (Exception e) {<br> <span class="hljs-comment">//解析失败不放行</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">message</span> <span class="hljs-operator">=</span> e.getMessage();<br> log.error(<span class="hljs-string">"非法令牌 "</span> + message);<br> <span class="hljs-keyword">if</span> (message.contains(<span class="hljs-string">"expire"</span>)) {<br> log.error(<span class="hljs-string">"令牌过期!"</span>);<br> }<br> response.setStatus(Code.IDENTITY_EXPIRES);<br> <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;<br> }<br> <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;<br>}<br></code></pre></td></tr></table></figure><h2 id="3-创建删除token接口"><a href="#3-创建删除token接口" class="headerlink" title="3.创建删除token接口"></a>3.创建删除token接口</h2><p>创建一个logout退出登录接口,前端退出登录时调用此接口,将token从redis中删除</p><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@DeleteMapping("/logout")</span><br><span class="hljs-keyword">public</span> Result<String> <span class="hljs-title function_">logout</span><span class="hljs-params">(<span class="hljs-meta">@RequestHeader(Common.TOKEN)</span> String token)</span> {<br> log.debug(<span class="hljs-string">"token:{}"</span>, token);<br><br> redisTemplate.delete(token);<br> <span class="hljs-keyword">return</span> Result.success();<br>}<br></code></pre></td></tr></table></figure><h2 id="4-前端调用"><a href="#4-前端调用" class="headerlink" title="4.前端调用"></a>4.前端调用</h2><h3 id="4-1-定义登出接口,调用后端"><a href="#4-1-定义登出接口,调用后端" class="headerlink" title="4.1 定义登出接口,调用后端"></a>4.1 定义登出接口,调用后端</h3><figure class="highlight js"><table><tr><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">import</span> request <span class="hljs-keyword">from</span> <span class="hljs-string">"@/util/request.js"</span>;<br><span class="hljs-comment">// 用户退出登录</span><br><span class="hljs-keyword">const</span> logoutService = <span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) {<br> <span class="hljs-comment">// 调用后端接口清除redis中的令牌</span><br> <span class="hljs-keyword">return</span> request.<span class="hljs-title function_">delete</span>(<span class="hljs-string">'/logout'</span>);<br>}<br><br><span class="hljs-keyword">export</span> {logoutService}<br></code></pre></td></tr></table></figure><h3 id="4-2-登出功能中调用上面接口"><a href="#4-2-登出功能中调用上面接口" class="headerlink" title="4.2 登出功能中调用上面接口"></a>4.2 登出功能中调用上面接口</h3><figure class="highlight js"><table><tr><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">const</span> <span class="hljs-title function_">logout</span> = <span class="hljs-keyword">async</span> (<span class="hljs-params"></span>) => {<br> <span class="hljs-keyword">await</span> <span class="hljs-title function_">logoutService</span>();<br> <span class="hljs-title class_">ElMessage</span>.<span class="hljs-title function_">success</span>(<span class="hljs-string">"已退出登录!"</span>)<br> <span class="hljs-comment">// 退出后清除token、reader和admin信息</span><br> tokenStore.<span class="hljs-title function_">setToken</span>(<span class="hljs-literal">null</span>);<br> readerStore.<span class="hljs-title function_">clearReader</span>();<br> adminStore.<span class="hljs-title function_">clearAdmin</span>();<br> <span class="hljs-keyword">await</span> router.<span class="hljs-title function_">push</span>(<span class="hljs-string">"/login"</span>);<br>};<br></code></pre></td></tr></table></figure><h2 id="5-联调测试"><a href="#5-联调测试" class="headerlink" title="5.联调测试"></a>5.联调测试</h2>]]></content>
<categories>
<category> 后端 </category>
</categories>
<tags>
<tag> redis </tag>
<tag> jwt </tag>
</tags>
</entry>
<entry>
<title>axios不入门直接起飞</title>
<link href="/2024/02/19/Axios/"/>
<url>/2024/02/19/Axios/</url>
<content type="html"><![CDATA[<h2 id="Axios"><a href="#Axios" class="headerlink" title="Axios"></a><a href="https://www.axios-http.cn/">Axios</a></h2><blockquote><p>Axios是什么?</p><p>Axios 是一个基于 <em><a href="https://javascript.info/promise-basics">promise</a></em> 网络请求库,作用于<a href="https://nodejs.org/">node.js</a> 和浏览器中。 它是 <em><a href="https://www.lullabot.com/articles/what-is-an-isomorphic-application">isomorphic</a></em> 的(即同一套代码可以运行在浏览器和node.js中)。在服务端它使用原生 node.js <code>http</code> 模块, 而在客户端 (浏览端) 则使用 XMLHttpRequests</p></blockquote><h2 id="1-安装"><a href="#1-安装" class="headerlink" title="1.安装"></a>1.安装</h2><p>使用npm安装:</p><figure class="highlight sh"><table><tr><td class="code"><pre><code class="hljs sh">npm install axios<br></code></pre></td></tr></table></figure><p>使用yarn安装:</p><figure class="highlight sh"><table><tr><td class="code"><pre><code class="hljs sh">yarn add axios<br></code></pre></td></tr></table></figure><h2 id="2-包装统一请求工具"><a href="#2-包装统一请求工具" class="headerlink" title="2.包装统一请求工具"></a>2.包装统一请求工具</h2><p>因为后端地址是一样的,假设是localhost:8080,只是请求路径不一样,我们可以定义一个baseURL,此处使用<code>/api</code>是为了解决<strong>跨域问题</strong></p><p>1.先包装一个工具<code>request.js</code></p><figure class="highlight js"><table><tr><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">"axios"</span>;<br><br><span class="hljs-keyword">const</span> baseURL = <span class="hljs-string">"/api"</span>;<br><span class="hljs-keyword">const</span> instance = axios.<span class="hljs-title function_">create</span>({baseURL});<br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> instance;<br></code></pre></td></tr></table></figure><p>2.在<code>vite.config.js</code>文件中添加配置,将<code>/api</code>删除,替换为<code>http://localhost:8080</code>,这样就相当于使用前端服务发送请求而不是浏览器,解决了跨域请求问题</p><figure class="highlight js"><table><tr><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-title function_">defineConfig</span>({<br> <span class="hljs-attr">plugins</span>: [<br> <span class="hljs-title function_">vue</span>(),<br> <span class="hljs-title class_">AutoImport</span>({<br> <span class="hljs-attr">resolvers</span>: [<span class="hljs-title class_">ElementPlusResolver</span>()],<br> }),<br> <span class="hljs-title class_">Components</span>({<br> <span class="hljs-attr">resolvers</span>: [<span class="hljs-title class_">ElementPlusResolver</span>()],<br> }),<br> ],<br> <span class="hljs-attr">resolve</span>: {<br> <span class="hljs-attr">alias</span>: {<br> <span class="hljs-string">'@'</span>: <span class="hljs-title function_">fileURLToPath</span>(<span class="hljs-keyword">new</span> <span class="hljs-title function_">URL</span>(<span class="hljs-string">'./src'</span>, <span class="hljs-keyword">import</span>.<span class="hljs-property">meta</span>.<span class="hljs-property">url</span>))<br> }<br> },<br> <span class="hljs-comment">//代理http请求,解决跨域问题</span><br> <span class="hljs-attr">server</span>: {<br> <span class="hljs-attr">host</span>: <span class="hljs-string">'localhost'</span>,<br> <span class="hljs-attr">port</span>: <span class="hljs-number">5173</span>,<br> <span class="hljs-attr">proxy</span>: {<br> <span class="hljs-string">'/api'</span>: { <span class="hljs-comment">//匹配请求路径中含有 /api 的请求</span><br> <span class="hljs-attr">target</span>: <span class="hljs-string">'http://localhost:8080'</span>, <span class="hljs-comment">//后端服务地址</span><br> <span class="hljs-attr">changeOrigin</span>: <span class="hljs-literal">true</span>,<br> <span class="hljs-attr">rewrite</span>: <span class="hljs-function">(<span class="hljs-params">path</span>) =></span> path.<span class="hljs-title function_">replace</span>(<span class="hljs-regexp">/^\/api/</span>, <span class="hljs-string">''</span>) <span class="hljs-comment">//去除路径中的/api,还原请求路径</span><br> }<br> }<br> },<br>})<br></code></pre></td></tr></table></figure><h2 id="3-拦截器"><a href="#3-拦截器" class="headerlink" title="3.拦截器"></a>3.拦截器</h2><p>这是axios发送请求的一个示例,项目中会有很多这样的请求要发送,我们会发现发送请求和接收响应数据之前,我们都会做一些相同的事情,比如,<strong>发送请求之前我们都会给请求头中带上token</strong>,<strong>接收响应时我们都会先判断状态码</strong>,我们不妨将这些动作用一个统一的函数来实现,这就需要使用拦截器</p><figure class="highlight js"><table><tr><td class="code"><pre><code class="hljs js">axios.<span class="hljs-title function_">post</span>(<span class="hljs-string">'/user'</span>, {<br> <span class="hljs-attr">firstName</span>: <span class="hljs-string">'Fred'</span>,<br> <span class="hljs-attr">lastName</span>: <span class="hljs-string">'Flintstone'</span><br> })<br> .<span class="hljs-title function_">then</span>(<span class="hljs-keyword">function</span> (<span class="hljs-params">response</span>) {<br> <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(response);<br> })<br> .<span class="hljs-title function_">catch</span>(<span class="hljs-keyword">function</span> (<span class="hljs-params">error</span>) {<br> <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(error);<br> });<br></code></pre></td></tr></table></figure><p>这是一个响应拦截器的示例,axios会自动适配响应的http<strong>状态码为4xx或5xx的请求为失败回调</strong>,在失败回调中我们可以<strong>对各种状态码的失败回调统一处理</strong>,成功回调中如果自定义的code为0也表示有错误,这种统一处理的方式类似于Spring中的<code>AOP</code></p><figure class="highlight js"><table><tr><td class="code"><pre><code class="hljs js"><span class="hljs-comment">//响应拦截器,状态码为2xx时执行成功回调,否则执行失败回调</span><br>instance.<span class="hljs-property">interceptors</span>.<span class="hljs-property">response</span>.<span class="hljs-title function_">use</span>(<br> <span class="hljs-comment">//成功回调</span><br> <span class="hljs-function">(<span class="hljs-params">result</span>) =></span> {<br> <span class="hljs-comment">// 如果状态码为0,后端发生异常</span><br> <span class="hljs-keyword">if</span> (result.<span class="hljs-property">data</span>.<span class="hljs-property">code</span> = <span class="hljs-number">0</span>) {<br> <span class="hljs-title class_">ElMessage</span>.<span class="hljs-title function_">error</span>(result.<span class="hljs-property">data</span>.<span class="hljs-property">msg</span>);<br> <span class="hljs-keyword">return</span> <span class="hljs-title class_">Promise</span>.<span class="hljs-title function_">reject</span>(result);<br> }<br> <span class="hljs-keyword">return</span> result.<span class="hljs-property">data</span>;<br> },<br> <span class="hljs-comment">//失败回调</span><br> <span class="hljs-function">(<span class="hljs-params">error</span>) =></span> {<br> <span class="hljs-comment">// 状态码为401,419都跳转到登录界面</span><br> <span class="hljs-keyword">if</span> (error.<span class="hljs-property">response</span>) {<br> <span class="hljs-keyword">const</span> code = error.<span class="hljs-property">response</span>.<span class="hljs-property">status</span>;<br> <span class="hljs-keyword">if</span> (code = <span class="hljs-number">401</span>) {<br> <span class="hljs-title class_">ElMessage</span>({<span class="hljs-attr">message</span>: <span class="hljs-string">'请先登录!'</span>, <span class="hljs-attr">type</span>: <span class="hljs-string">"error"</span>,});<br> router.<span class="hljs-title function_">push</span>(<span class="hljs-string">'/login'</span>);<br> } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (code = <span class="hljs-number">419</span>) {<br> <span class="hljs-title class_">ElMessage</span>.<span class="hljs-title function_">error</span>(<span class="hljs-string">"身份已过期,请重新登录!"</span>);<br> router.<span class="hljs-title function_">push</span>(<span class="hljs-string">'/login'</span>);<br> } <span class="hljs-keyword">else</span> {<br> <span class="hljs-title class_">ElMessage</span>.<span class="hljs-title function_">error</span>(<span class="hljs-string">"服务器异常!"</span> + code);<br> }<br> }<br> <span class="hljs-comment">// 将异步的状态设置为失败状态</span><br> <span class="hljs-keyword">return</span> <span class="hljs-title class_">Promise</span>.<span class="hljs-title function_">reject</span>(error);<br> }<br>);<br></code></pre></td></tr></table></figure><p>这是一个请求拦截器的示例,在发送请求之前先判断是否有token,如果有就在请求头上带上token再发送请求,否则跳转到登录页面,当然,<strong>登录和注册请求都不需要token</strong>,可以直接发送请求</p><figure class="highlight js"><table><tr><td class="code"><pre><code class="hljs js"><span class="hljs-comment">// 请求拦截器</span><br>instance.<span class="hljs-property">interceptors</span>.<span class="hljs-property">request</span>.<span class="hljs-title function_">use</span>(<br> <span class="hljs-function">(<span class="hljs-params">config</span>) =></span> {<br> <span class="hljs-comment">//登录请求不需要token</span><br> <span class="hljs-keyword">if</span> (config.<span class="hljs-property">url</span>.<span class="hljs-title function_">endsWith</span>(<span class="hljs-string">'/login'</span>) || config.<span class="hljs-property">url</span>.<span class="hljs-title function_">endsWith</span>(<span class="hljs-string">'/register'</span>)) {<br> <span class="hljs-keyword">return</span> config;<br> }<br> <span class="hljs-comment">//如果有token,将token放入请求头中</span><br> <span class="hljs-keyword">const</span> token = tokenStore.<span class="hljs-property">token</span>;<br> <span class="hljs-keyword">if</span> (token != <span class="hljs-literal">null</span>) {<br> config.<span class="hljs-property">headers</span>[<span class="hljs-string">'token'</span>] = token;<br> } <span class="hljs-keyword">else</span> {<br> router.<span class="hljs-title function_">push</span>(<span class="hljs-string">'/login'</span>);<br> <span class="hljs-title class_">ElMessage</span>({<span class="hljs-attr">message</span>: <span class="hljs-string">'请先登录!'</span>, <span class="hljs-attr">type</span>: <span class="hljs-string">"error"</span>,});<br> <span class="hljs-keyword">return</span> <span class="hljs-title class_">Promise</span>.<span class="hljs-title function_">reject</span>(<span class="hljs-string">'token不存在!'</span>);<br> }<br> <span class="hljs-keyword">return</span> config;<br> },<br> <span class="hljs-function">(<span class="hljs-params">err</span>) =></span> {<br> <span class="hljs-comment">//请求错误的回调</span><br> <span class="hljs-keyword">return</span> <span class="hljs-title class_">Promise</span>.<span class="hljs-title function_">reject</span>(err);<br> }<br>);<br></code></pre></td></tr></table></figure><h2 id="4-完整axios包装工具示例"><a href="#4-完整axios包装工具示例" class="headerlink" title="4.完整axios包装工具示例"></a>4.完整axios包装工具示例</h2><figure class="highlight js"><table><tr><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">"axios"</span>;<br><span class="hljs-keyword">import</span> {<span class="hljs-title class_">ElMessage</span>} <span class="hljs-keyword">from</span> <span class="hljs-string">"element-plus"</span>;<br><span class="hljs-keyword">import</span> router <span class="hljs-keyword">from</span> <span class="hljs-string">"@/router"</span>;<br><span class="hljs-keyword">import</span> {useTokenStore} <span class="hljs-keyword">from</span> <span class="hljs-string">"@/stores/token.js"</span>;<br><br><span class="hljs-keyword">const</span> baseURL = <span class="hljs-string">"/api"</span>;<br><span class="hljs-keyword">const</span> instance = axios.<span class="hljs-title function_">create</span>({baseURL});<br><span class="hljs-keyword">const</span> tokenStore = <span class="hljs-title function_">useTokenStore</span>();<br><br><br><span class="hljs-comment">//响应拦截器,状态码为2xx时执行成功回调,否则执行失败回调</span><br>instance.<span class="hljs-property">interceptors</span>.<span class="hljs-property">response</span>.<span class="hljs-title function_">use</span>(<br> <span class="hljs-comment">//成功回调</span><br> <span class="hljs-function">(<span class="hljs-params">result</span>) =></span> {<br> <span class="hljs-comment">// 如果状态码为0,后端发生异常</span><br> <span class="hljs-keyword">if</span> (result.<span class="hljs-property">data</span>.<span class="hljs-property">code</span> = <span class="hljs-number">0</span>) {<br> <span class="hljs-title class_">ElMessage</span>.<span class="hljs-title function_">error</span>(result.<span class="hljs-property">data</span>.<span class="hljs-property">msg</span>);<br> <span class="hljs-keyword">return</span> <span class="hljs-title class_">Promise</span>.<span class="hljs-title function_">reject</span>(result);<br> }<br> <span class="hljs-keyword">return</span> result.<span class="hljs-property">data</span>;<br> },<br> <span class="hljs-comment">//失败回调</span><br> <span class="hljs-function">(<span class="hljs-params">error</span>) =></span> {<br> <span class="hljs-comment">// 状态码为401,419都跳转到登录界面</span><br> <span class="hljs-keyword">if</span> (error.<span class="hljs-property">response</span>) {<br> <span class="hljs-keyword">const</span> code = error.<span class="hljs-property">response</span>.<span class="hljs-property">status</span>;<br> <span class="hljs-keyword">if</span> (code = <span class="hljs-number">401</span>) {<br> <span class="hljs-title class_">ElMessage</span>({<span class="hljs-attr">message</span>: <span class="hljs-string">'请先登录!'</span>, <span class="hljs-attr">type</span>: <span class="hljs-string">"error"</span>,});<br> router.<span class="hljs-title function_">push</span>(<span class="hljs-string">'/login'</span>);<br> } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (code = <span class="hljs-number">419</span>) {<br> <span class="hljs-title class_">ElMessage</span>.<span class="hljs-title function_">error</span>(<span class="hljs-string">"身份已过期,请重新登录!"</span>);<br> router.<span class="hljs-title function_">push</span>(<span class="hljs-string">'/login'</span>);<br> } <span class="hljs-keyword">else</span> {<br> <span class="hljs-title class_">ElMessage</span>.<span class="hljs-title function_">error</span>(<span class="hljs-string">"服务器异常!"</span> + code);<br> }<br> }<br> <span class="hljs-comment">// 将异步的状态设置为失败状态</span><br> <span class="hljs-keyword">return</span> <span class="hljs-title class_">Promise</span>.<span class="hljs-title function_">reject</span>(error);<br> }<br>);<br><br><span class="hljs-comment">// 请求拦截器</span><br>instance.<span class="hljs-property">interceptors</span>.<span class="hljs-property">request</span>.<span class="hljs-title function_">use</span>(<br> <span class="hljs-function">(<span class="hljs-params">config</span>) =></span> {<br> <span class="hljs-comment">//登录请求不需要token</span><br> <span class="hljs-keyword">if</span> (config.<span class="hljs-property">url</span>.<span class="hljs-title function_">endsWith</span>(<span class="hljs-string">'/login'</span>) || config.<span class="hljs-property">url</span>.<span class="hljs-title function_">endsWith</span>(<span class="hljs-string">'/register'</span>)) {<br> <span class="hljs-keyword">return</span> config;<br> }<br> <span class="hljs-comment">//如果有token,将token放入请求头中</span><br> <span class="hljs-keyword">const</span> token = tokenStore.<span class="hljs-property">token</span>;<br> <span class="hljs-keyword">if</span> (token != <span class="hljs-literal">null</span>) {<br> config.<span class="hljs-property">headers</span>[<span class="hljs-string">'token'</span>] = token;<br> } <span class="hljs-keyword">else</span> {<br> router.<span class="hljs-title function_">push</span>(<span class="hljs-string">'/login'</span>);<br> <span class="hljs-title class_">ElMessage</span>({<span class="hljs-attr">message</span>: <span class="hljs-string">'请先登录!'</span>, <span class="hljs-attr">type</span>: <span class="hljs-string">"error"</span>,});<br> <span class="hljs-keyword">return</span> <span class="hljs-title class_">Promise</span>.<span class="hljs-title function_">reject</span>(<span class="hljs-string">'token不存在!'</span>);<br> }<br> <span class="hljs-keyword">return</span> config;<br> },<br> <span class="hljs-function">(<span class="hljs-params">err</span>) =></span> {<br> <span class="hljs-comment">//请求错误的回调</span><br> <span class="hljs-keyword">return</span> <span class="hljs-title class_">Promise</span>.<span class="hljs-title function_">reject</span>(err);<br> }<br>);<br><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> instance;<br></code></pre></td></tr></table></figure><p>在其他接口文件中导入使用,因为使用的是<code>export default instance</code>默认导出的,所以导入时可以自定义名字,这个文件中定义为<code>request</code>,其实就是<code>instance</code>实例</p><figure class="highlight js"><table><tr><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">import</span> request <span class="hljs-keyword">from</span> <span class="hljs-string">"@/util/request.js"</span>;<br><span class="hljs-comment">// 用户退出登录</span><br><span class="hljs-keyword">const</span> logoutService = <span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) {<br> <span class="hljs-comment">// 调用后端接口清除redis中的令牌</span><br> <span class="hljs-keyword">return</span> request.<span class="hljs-title function_">delete</span>(<span class="hljs-string">'/logout'</span>);<br>}<br><br><span class="hljs-keyword">export</span> {logoutService}<br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category> 前端 </category>
<category> vue </category>
</categories>
<tags>
<tag> axios </tag>
</tags>
</entry>
<entry>
<title>everything极速搜索</title>
<link href="/2024/02/18/Everything%E6%90%9C%E7%B4%A2/"/>
<url>/2024/02/18/Everything%E6%90%9C%E7%B4%A2/</url>
<content type="html"><![CDATA[<h1 id="everything"><a href="#everything" class="headerlink" title="everything"></a>everything</h1><h2 id="1-指定文件后缀"><a href="#1-指定文件后缀" class="headerlink" title="1.指定文件后缀"></a>1.指定文件后缀</h2><p>搜索文件名,并且==要求后缀==,假设要搜索 名称包含<code>main</code>的所有<code>java</code>文件,可以搜索</p><blockquote><p>[!note]</p><p><code>main ext:java</code></p><p>ext:java 就是指定后缀名,这个条件和main条件顺序随意,==注意中间要有一个空格==</p></blockquote><h2 id="2-指定文件路径"><a href="#2-指定文件路径" class="headerlink" title="2.指定文件路径"></a>2.指定文件路径</h2><p>搜索文件,并且要求==该文件包含指定路径==,假设要搜索<code>base.css</code>文件,并且该文件路径中包含<code>typora</code></p><blockquote><p>[!note]</p><p><code>base.css path:typora</code></p><p>使用<code>path</code>指定文件包含的路径</p></blockquote><h2 id="3-指定文件夹"><a href="#3-指定文件夹" class="headerlink" title="3.指定文件夹"></a>3.指定文件夹</h2><p>在<strong>指定文件夹</strong>下搜索文件,假设要在文件夹下<code>"D:\QQ\"</code>文件夹下搜索文件<code>qq.exe</code></p><blockquote><p><code>"D:\QQ\" qq.exe</code></p><p>前面写文件夹路径,后面接文件名,中间有空格</p></blockquote><h2 id="4-文本内容"><a href="#4-文本内容" class="headerlink" title="4.文本内容"></a>4.文本内容</h2><p>假如搜索到的文件有很多个,我只想要文件中<strong>内容包含123</strong>的怎么查询?</p><blockquote><p>使用<code>content</code>指定文件中包含的内容</p><p><code>1.txt content:123</code></p></blockquote><p>更多用法见<a href="https://www.voidtools.com/zh-cn/">voidtools</a></p>]]></content>
<categories>
<category> tools </category>
</categories>
<tags>
<tag> everything </tag>
<tag> 搜索 </tag>
</tags>
</entry>
<entry>
<title>springboot相关配置</title>
<link href="/2024/01/07/springboot%E7%9B%B8%E5%85%B3%E9%85%8D%E7%BD%AE/"/>
<url>/2024/01/07/springboot%E7%9B%B8%E5%85%B3%E9%85%8D%E7%BD%AE/</url>
<content type="html"><![CDATA[<h1 id="springboot相关配置"><a href="#springboot相关配置" class="headerlink" title="springboot相关配置"></a>springboot相关配置</h1><h2 id="1-自定义项目LOGO"><a href="#1-自定义项目LOGO" class="headerlink" title="1.自定义项目LOGO"></a>1.<a href="https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.spring-application.banner">自定义项目LOGO</a></h2><p>在<code>resources</code>文件夹下新建一个<code>banner.txt</code>文件,加入相关内容即可,<a href="https://www.bootschool.net/ascii-art">艺术字生成网站</a></p><figure class="highlight txt"><table><tr><td class="code"><pre><code class="hljs txt">---------------+---------------<br> ___ /^^[___ _<br> /|^+----+ |#___________//<br> ( -+ |____| ______-----+/<br> ==_________--' \<br> ~_|___|__<br></code></pre></td></tr></table></figure><h2 id="2-跨域请求"><a href="#2-跨域请求" class="headerlink" title="2. 跨域请求"></a>2. 跨域请求</h2><p>添加此配置到<code>WebMvc</code>配置类中(推荐),也可以在每个<code>Controller</code>类上添加注解<code>@CrossOrigin</code></p><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Configuration</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">WebMvcConfig</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">WebMvcConfigurer</span> {<br> <span class="hljs-comment">//允许所有跨域请求</span><br> <span class="hljs-meta">@Override</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">addCorsMappings</span><span class="hljs-params">(CorsRegistry registry)</span> {<br> registry.addMapping(<span class="hljs-string">"/**"</span>)<br> .allowedOriginPatterns(<span class="hljs-string">"*"</span>)<br> .allowedMethods(<span class="hljs-string">"*"</span>)<br> .allowedHeaders(<span class="hljs-string">"*"</span>)<br> .allowCredentials(<span class="hljs-literal">true</span>);<br> }<br>}<br></code></pre></td></tr></table></figure><h2 id="3-自动重启"><a href="#3-自动重启" class="headerlink" title="3. 自动重启"></a>3. 自动重启</h2><p>使用dev-tools实现项目代码修改后自动重启,<strong>相比于手动重启速度更快</strong></p><ul><li>导入依赖,刷新maven,然后重启项目</li></ul><figure class="highlight xml"><table><tr><td class="code"><pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.springframework.boot<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>spring-boot-devtools<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">scope</span>></span>runtime<span class="hljs-tag"></<span class="hljs-name">scope</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">optional</span>></span>true<span class="hljs-tag"></<span class="hljs-name">optional</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br></code></pre></td></tr></table></figure><ul><li>在IDEA设置中打开高级设置,搜索编译,打开下图中选项</li></ul><p><img src="https://s2.loli.net/2024/12/31/4JTy5dRIasjcmuQ.png" alt="image-20241231180541224"></p><ul><li>在左侧打开<strong>服务</strong>工具窗口,右键项目启动类配置,选择<strong>编辑所选配置</strong></li></ul><p><img src="C:/Users/12655/AppData/Roaming/Typora/typora-user-images/image-20250108164756438.png" alt="image-20250108164756438"></p><p>点击<strong>修改选项</strong></p><p><img src="C:/Users/12655/AppData/Roaming/Typora/typora-user-images/image-20250108164858397.png" alt="image-20250108164858397"></p><p>在 <strong>切换出IDE时</strong> 一项中选择 <strong>更新类和资源</strong>,这样只要光标焦点离开IDEA就会自动重新构建并启动项目,速度较快</p><p><img src="C:/Users/12655/AppData/Roaming/Typora/typora-user-images/image-20250108164933177.png" alt="image-20250108164933177"></p>]]></content>
<categories>
<category> 后端 </category>
</categories>
<tags>
<tag> springboot </tag>
</tags>
</entry>
<entry>
<title>计算机网络</title>
<link href="/2023/10/26/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/"/>
<url>/2023/10/26/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/</url>
<content type="html"><![CDATA[<h1 id="计算机网络"><a href="#计算机网络" class="headerlink" title="计算机网络"></a>计算机网络</h1><h2 id="1-计算机网络概述"><a href="#1-计算机网络概述" class="headerlink" title="1. 计算机网络概述"></a>1. 计算机网络概述</h2><h3 id="1-1-作用"><a href="#1-1-作用" class="headerlink" title="1.1 作用"></a>1.1 作用</h3><blockquote><p>计算机网络在信息时代中的作用:21世纪是以数字化、网络化、信息化为重要特征的信息时代,作为信息的最大载体和传输媒介,网络已成为这个信息时代的核心基础</p></blockquote><h3 id="1-2-定义与分类"><a href="#1-2-定义与分类" class="headerlink" title="1.2 定义与分类"></a>1.2 定义与分类</h3><h4 id="1-2-1-定义"><a href="#1-2-1-定义" class="headerlink" title="1.2.1 定义"></a>1.2.1 定义</h4><p>简单定义:<mark>硬件</mark>(独立和连接)+<mark>软件</mark>(协议)+<mark>目的</mark>(共享和通信)</p><blockquote><p><strong>计算机网络就是以传输信息为基本目的,用通信线路和通信设备将多个计算机连接起来的计算机系统的集合</strong></p></blockquote><h4 id="1-2-2-分类"><a href="#1-2-2-分类" class="headerlink" title="1.2.2 分类"></a>1.2.2 分类</h4><blockquote><p>按网络覆盖范围进行分类</p></blockquote><ol><li>局域网(Local Area Network,<code>LAN</code>,一个实验室、一幢楼、一个校园)</li><li>城域网(Metropolitan Area Network,<code>MAN</code>,几个街区甚至整个城市,5-50 km)</li><li>广域网(Wide Area Network,<code>WAN</code>,覆盖一个国家或地区甚至几个洲,也称为<strong>远程网</strong>)</li><li>个人区域网(Personal Area Network,<code>PAN</code>,10 m以内)</li></ol><blockquote><p>按网络的使用者进行分类</p></blockquote><ol><li>公用网(所有愿意缴纳费用的人都能使用)</li><li>专用网(专门为本单位的特殊业务工作建造的,例如,军队、铁路、电力等系统)</li></ol><img src="https://clb.pages.dev/img/pics/CopyQ.TZiqFW.png" alt="计算机网络分类" title='计算机网络的分类' /><h3 id="1-3-互联网概述"><a href="#1-3-互联网概述" class="headerlink" title="1.3 互联网概述"></a>1.3 互联网概述</h3><h4 id="1-3-1-网络的网络"><a href="#1-3-1-网络的网络" class="headerlink" title="1.3.1 网络的网络"></a>1.3.1 网络的网络</h4><ol><li>网络</li></ol><blockquote><p><mark>网络</mark>由<mark>若干节点</mark>(Node)和连接这些节点的<mark>链路</mark>(Link)组成</p><p>节点:可以是计算机、集线器、交换机、路由器</p></blockquote><img src="https://clb.pages.dev/img/pics/image-20231024224626941.png" title="网络" style="zoom:80%;" /><ol start="2"><li><mark>互连网络</mark>(internet,通用名词,注意和 Internet互联网 区分)</li></ol><blockquote><p>将网络用路由器连接起来就成了互连网络,因此互连网络是<mark>网络的网络</mark></p></blockquote><img src="https://clb.pages.dev/img/pics/image-20231024224534363.png" title="互连网络就是网络的网络" style="zoom:80%;" /><blockquote><p>总结:<mark><strong>网络把许多计算机连接在一起,而互联网络则把许多网络连接在一起</strong></mark></p></blockquote><ol start="3"><li><mark>互联网</mark>(Internet,也称 因特网)</li></ol><blockquote><p>当前全球最大的、开放的、由众多网络相互连接而成的特定计算机网络,<mark>采用TCP/IP协议作为通信规则</mark>,前身是美国的阿帕网(<mark>ARPANet</mark>)</p></blockquote><ol><li><mark>边缘部分</mark>:用户直接使用的</li><li><mark>核心部分</mark>:为边缘部分提供服务的</li></ol><img src="https://clb.pages.dev/img/pics/image-20231024224804841.png" style="zoom: 67%;" /><h4 id="1-3-2-互联网结构发展的三个阶段"><a href="#1-3-2-互联网结构发展的三个阶段" class="headerlink" title="1.3.2 互联网结构发展的三个阶段"></a>1.3.2 互联网结构发展的三个阶段</h4><ol><li>第一阶段:从单个网络ARPANET向互连网络发展</li><li>第二阶段:逐步建成三级结构的互联网</li><li>第三阶段:逐渐形成多层次的ISP结构的互联网</li></ol><img src="https://clb.pages.dev/img/pics/image-20231024225143618.png" title="ISP结构的互联网" style="zoom: 67%;"/><h3 id="1-4-电路交换和分组交换"><a href="#1-4-电路交换和分组交换" class="headerlink" title="1.4 电路交换和分组交换"></a>1.4 电路交换和分组交换</h3><h4 id="1-4-1-电路交换"><a href="#1-4-1-电路交换" class="headerlink" title="1.4.1 电路交换"></a>1.4.1 电路交换</h4><p><img src="https://clb.pages.dev/img/pics/image-20231024225354835.png" alt="电路交换"></p><blockquote><p>这种必须经过</p><ol><li><p><code>建立连接</code>(分配通信资源)</p></li><li><p><code>通信</code>(一直占用通信资源)</p></li><li><p><code>释放连接</code>(归还通信资源)</p></li></ol><p>三个步骤的交换方式称为<mark>电路交换</mark></p><p>电路交换:电路交换是指在通信开始之前,建立一条专用的物理连接,然后在通信过程中一直占用这条连接,直到通信结束。在这个过程中,数据被分成固定大小的数据包,每个数据包都被分配一个专用的物理通路。</p><p>优点:传输<mark>速度快</mark></p><p>缺点:连接建立需要时间,且连接一旦建立就无法被其他通信使用(<mark>效率很低</mark>)</p></blockquote><h4 id="1-4-2-报文交换"><a href="#1-4-2-报文交换" class="headerlink" title="1.4.2 报文交换"></a>1.4.2 报文交换</h4><blockquote><p>整个报文传送到相邻结点,全部存储下来后查找转发表,转发到下一个结点。</p><p>缺点:<mark>转发时延高</mark></p></blockquote><h4 id="1-4-3-分组交换"><a href="#1-4-3-分组交换" class="headerlink" title="1.4.3 分组交换"></a>1.4.3 分组交换</h4><img src="https://clb.pages.dev/img/pics/image-20231024225503293.png" alt="分组交换" style="zoom: 80%;" /><blockquote><p>我们通常把要发送的整块数据称为一个<mark>报文</mark>(Message)</p><p>发送报文之前,先将报文划分为一个个更小的等长的数据段,在数据段前面加上<mark>首部</mark>(又称为<mark>包头</mark>)就构成了<mark>分组</mark>(又称为<mark>包</mark>)</p></blockquote><blockquote><p>分组交换:分组交换是指<mark>将报文分成较小的数据包</mark>,每个数据包都带有<strong>目标地址</strong>和<strong>源地址</strong>等信息,然后通过网络传输。在传输过程中,每个数据包都独立传输,可以通过不同的路径到达目的地,由于每个分组交换机都是先存储下来再转发出去,因此该方法被称为<span style='color:rgb(255, 107, 107);font-weight:bold'>存储转发</span>方式</p></blockquote><p><img src="https://clb.pages.dev/img/pics/CopyQ.hVntPh.png" alt="交换方式对比"></p><h3 id="1-5-计算机网络的主要性能指标"><a href="#1-5-计算机网络的主要性能指标" class="headerlink" title="1.5 计算机网络的主要性能指标"></a>1.5 计算机网络的主要性能指标</h3><h4 id="1-速率"><a href="#1-速率" class="headerlink" title="1. 速率"></a>1. 速率</h4><blockquote><ol><li><p><mark>数据的传输速率</mark>,也称为<mark>数据率</mark>、<mark>比特率</mark> 1字节(byte)=8比特(bit)</p></li><li><p>单位是 <code>bit/s 或 bps</code> ,比特是数据量的单位,是一个<mark>二进制数字</mark>(0或1)</p></li><li><p>一般来说,表示速率时<br>$$<br>k = 10^{3}<br>$$<br>表示数据时<br>$$<br>k = 2^{10}<br>$$</p></li></ol></blockquote><img src='https://clb.pages.dev/img/pics/CopyQ.aGEcPX.png' alt='速率'><h4 id="2-带宽"><a href="#2-带宽" class="headerlink" title="2. 带宽"></a>2. 带宽</h4><ol><li><p>带宽本来是指某个<mark>信号具有的频带宽度</mark>,表示通信线路允许通过的信号频带范围,单位为赫兹</p></li><li><p>计算机网络中,带宽表示网络某通道传输能力,即<mark>最高数据率</mark>,单位为<mark>比特每秒</mark>(bit/s)</p><img src="https://clb.pages.dev/img/pics/CopyQ.UEfjHW.png" alt='带宽'/></li></ol><h4 id="3-吞吐量"><a href="#3-吞吐量" class="headerlink" title="3. 吞吐量"></a>3. 吞吐量</h4><blockquote><p>也称为<mark>吞吐率</mark>,表示单位时间内通过某个网络(或信道、接口)的数据量</p></blockquote><h4 id="4-时延"><a href="#4-时延" class="headerlink" title="4.时延"></a>4.时延</h4><blockquote><p>时延是指<mark>数据从网络的一端传送到另外一端所需要的时间</mark>,也称为<mark>延迟</mark>或<mark>迟延</mark></p><p>网络中的时延通常由以下几个不同的部分组成:</p><ol><li><p><mark>发送时延:主机或路由器将整个分组的所有比特发送到通信线路上所需要的时间</mark><br>$$<br>发送时延 = \frac{分组长度}{发送速率}<br>$$</p></li><li><p><mark>传播时延:是电磁波在信道中传播一定距离所花费的时间</mark><br>$$<br>传播时延 = \frac{信道长度}{电磁波在信道上的传播速度}<br>$$</p></li><li><p>处理时延:主机或路由器在收到分组时处理分组花费的时间(一般不方便计算)</p></li></ol></blockquote><img src="https://clb.pages.dev/img/pics/CopyQ.hzERwE.png" alt='时延'/><p><img src="https://clb.pages.dev/img/pics/202310261920366.png" alt="时延"></p><h4 id="5-时延带宽积-往返时间-利用率-丢包率"><a href="#5-时延带宽积-往返时间-利用率-丢包率" class="headerlink" title="5. 时延带宽积 往返时间 利用率 丢包率"></a>5. 时延带宽积 往返时间 利用率 丢包率</h4><img src="https://clb.pages.dev/img/pics/CopyQ.XBpSgv.png" alt="" style="zoom:;" /><h3 id="1-6-计算机网络体系结构"><a href="#1-6-计算机网络体系结构" class="headerlink" title="1.6 计算机网络体系结构"></a>1.6 计算机网络体系结构</h3><h4 id="1-6-1-网络协议"><a href="#1-6-1-网络协议" class="headerlink" title="1.6.1 网络协议"></a>1.6.1 网络协议</h4><p>网络协议主要由以下三要素组成:</p><ol><li><mark>语法:数据与控制信息的结构或格式</mark>(例如,地址字段多长以及它在分组的什么位置)</li><li><mark>语义:各个控制信息的具体含义</mark>(需要发出何种控制信息、完成何种动作及做出何种响应)</li><li><mark>同步:事件实现的顺序和时间的详细说明</mark>(数据何时发出以及以什么速率发出)</li></ol><h4 id="1-6-2-分层体系结构"><a href="#1-6-2-分层体系结构" class="headerlink" title="1.6.2 分层体系结构"></a>1.6.2 分层体系结构</h4><blockquote><p>[!tip]</p><p>口诀:<span alt='highlight'>物联网叔会使用</span></p></blockquote><p><img src="https://clb.pages.dev/img/pics/CopyQ.uQqXEZ.png" alt="常见的计算机网络分层体系结构"></p><h4 id="1-6-3-原理体系结构"><a href="#1-6-3-原理体系结构" class="headerlink" title="1.6.3 原理体系结构"></a>1.6.3 原理体系结构</h4><blockquote><p>我们学习的是具有五层协议的原理体系结构</p></blockquote><p><img src="https://clb.pages.dev/img/pics/CopyQ.EvnJQE.png" alt="原理体系结构"></p><h4 id="1-6-4-实体、协议和服务"><a href="#1-6-4-实体、协议和服务" class="headerlink" title="1.6.4 实体、协议和服务"></a>1.6.4 实体、协议和服务</h4><blockquote><p><strong><mark>协议是水平的,服务是垂直的</mark></strong></p><ol><li>==实体==:任何可发送或接收信息的硬件或软件进程</li><li>==协议==:<mark><strong>控制两个或多个对等实体之间通信的规则的集合</strong></mark></li><li><mark>透明:某个实际存在的事物看起来好像不存在一样</mark></li><li><mark><strong>下层通过层级接口向上层提供服务,下面的协议对上面的实体是”透明“的</strong></mark></li></ol></blockquote><p><img src="https://clb.pages.dev/img/pics/CopyQ.ZGZDeQ.png" alt="实体、协议和服务"></p><p><img src="https://clb.pages.dev/img/pics/62913fa9e4b0c71a3a81421f.jpeg" alt="TCP/IP参考模型中的协议"></p><h3 id="1-7-相关习题"><a href="#1-7-相关习题" class="headerlink" title="1.7 相关习题"></a>1.7 相关习题</h3><p><img src="https://clb.pages.dev/img/pics/202310261924745.png"></p><p><img src="https://clb.pages.dev/img/pics/202310261925145.png"></p><h3 id="1-8-思维导图和习题"><a href="#1-8-思维导图和习题" class="headerlink" title="1.8 思维导图和习题"></a>1.8 思维导图和习题</h3><ol><li><a href="https://www.kdocs.cn/view/l/cqzI95zH7t6v">第1章 概述(思维导图)</a></li><li><a href="https://www.kdocs.cn/view/l/cmCC5Updax17">第1章 概述 习题 (kdocs.cn)</a></li></ol><hr><h2 id="2-物理层"><a href="#2-物理层" class="headerlink" title="2. 物理层"></a>2. 物理层</h2><blockquote><p><mark><strong>任务:怎样在连接各种计算机的传输媒体上传输数据比特流</strong></mark>(使数据链路层感觉不到各种传输媒体之间的差异,只需要使用物理层服务就能传输比特流,不必关心具体怎么实现)</p></blockquote><h3 id="2-1-物理层的基本概念"><a href="#2-1-物理层的基本概念" class="headerlink" title="2.1 物理层的基本概念"></a>2.1 物理层的基本概念</h3><p><img src="https://clb.pages.dev/img/pics/202310261930116.png" alt="物理层的基本概念"></p><h3 id="2-2-数据通信的基础知识"><a href="#2-2-数据通信的基础知识" class="headerlink" title="2.2 数据通信的基础知识"></a>2.2 数据通信的基础知识</h3><h4 id="2-2-1-数据通信的基础知识"><a href="#2-2-1-数据通信的基础知识" class="headerlink" title="2.2.1 数据通信的基础知识"></a>2.2.1 数据通信的基础知识</h4><p><img src="https://clb.pages.dev/img/pics/image-20231228112913963.png"></p><h4 id="2-2-2-编码与调制"><a href="#2-2-2-编码与调制" class="headerlink" title="2.2.2 编码与调制"></a>2.2.2 编码与调制</h4><ol><li><p>定义</p><ul><li><p><mark>模拟信号</mark>:消息的参数的取值是连续的</p></li><li><p><mark>数字信号</mark>:消息的参数的取值是离散的</p></li><li><p><mark>编码</mark>:将数字数据转换成数字信号的过程</p></li><li><p><mark>调制</mark>:将数字数据转换成模拟信号的过程</p></li></ul></li></ol><p><img src="https://s2.loli.net/2025/01/05/VXEAhmTHlF34b1y.png" alt="编码与调制"></p><ol start="2"><li>常用编码方式</li></ol><p><img src="https://clb.pages.dev/img/pics/202310262040597.png" alt="常用编码方式"></p><ol start="3"><li>基本的调制方法</li></ol><p><img src="https://clb.pages.dev/img/pics/202310262047837.png" alt="基本的调制方法"></p><ol start="4"><li><strong><mark>相位调制</mark></strong></li></ol><blockquote><p><strong>8相位调制</strong>表示8种状态,只需要<strong>3个比特</strong>(二进制,$2^3=8$)即可,所以<strong>1个码元可以承载3个比特</strong>,**<mark>数据传输率 = 3*波特率</mark>**,以此类推</p></blockquote><h4 id="2-2-3-信道的极限容量"><a href="#2-2-3-信道的极限容量" class="headerlink" title="2.2.3 信道的极限容量"></a>2.2.3 信道的极限容量</h4><p><img src="https://clb.pages.dev/img/pics/202310262054137.png" alt="信号失真"></p><blockquote><p> [!important] </p><p><mark>奈式准则</mark></p></blockquote><p><img src="https://clb.pages.dev/img/pics/202310262059898.png" alt="奈式准则"></p><blockquote><p> [!important]</p><p><mark>香农公式</mark><br>$$<br>c = W \cdot \log_{2}(1 + \frac{S}{N})<br>$$<br><mark>知道信噪比(dB)求S/N,假设信噪比为30dB</mark></p><ol><li>$$30 = 10 \cdot \log_{10}(\frac{S}{N})$$</li><li>$$3 = \log_{10}(\frac{S}{N})$$</li><li>$$\frac{S}{N} = 10^3$$</li></ol></blockquote><p><img src="https://clb.pages.dev/img/pics/202310262102013.png" alt="香农公式"></p><blockquote><ol><li>在信道带宽一定的情况下,根据奈氏准则和香农公式,要想<mark>提高信息的传输速率</mark>就必须采用<mark>多元制</mark>(更好的调制方法)和努力<mark>提高信道中的信噪比</mark></li><li>自从香农公式发表后,各种新的信号处理和调制方法就不断出现,其目的都是为了尽可能地接近香农公式给出的传输速率极限,但<mark>实际信道能达到的传输速率要低不少</mark></li></ol></blockquote><p>总结:</p><p><img src="https://s2.loli.net/2024/09/06/OKHxW9eA5ci38fz.png" alt="image-20240906144004528"></p><h4 id="2-2-4-传输方式"><a href="#2-2-4-传输方式" class="headerlink" title="2.2.4 传输方式"></a>2.2.4 传输方式</h4><ol><li>并行传输和串行传输</li></ol><blockquote><ul><li>并行传输速度是串行传输的<mark>n倍</mark>,但是<span color='rgb(1,2,3)'>成本高</span><mark>所以长距离传输使用串行传输</mark></li><li>远距离传输使用串行传输,计算机内部使用并行传输</li></ul></blockquote><p><img src="https://clb.pages.dev/img/pics/202310262005937.png" alt="并行传输和串行传输"></p><ol start="2"><li>异步传输和同步传输</li></ol><blockquote><p>异步传输<mark>字节之间异步</mark>,但是要添加<mark>开始码</mark>和<mark>停止码</mark>作为引导</p></blockquote><img src="https://clb.pages.dev/img/pics/202310262010168.png" alt="异步传输和同步传输" /><ol start="3"><li>单工、半双工和全双工通信</li></ol><p><img src="https://clb.pages.dev/img/pics/202310262014965.png" alt="单工、半双工和全双工通信"></p><h3 id="2-3-物理层下的传输媒体"><a href="#2-3-物理层下的传输媒体" class="headerlink" title="2.3 物理层下的传输媒体"></a>2.3 物理层下的传输媒体</h3><h4 id="2-3-1-导引型传输媒体"><a href="#2-3-1-导引型传输媒体" class="headerlink" title="2.3.1 导引型传输媒体"></a>2.3.1 导引型传输媒体</h4><ol><li><strong>同轴电缆</strong></li></ol><p><img src="https://s2.loli.net/2024/09/06/VhXYTlr37265ygI.png" alt="同轴电缆"></p><ol start="2"><li><strong>双绞线</strong></li></ol><p><img src="https://clb.pages.dev/img/pics/202310261942966.png" alt="双绞线"></p><ol start="3"><li><strong>光纤</strong></li></ol><p><img src="https://clb.pages.dev/img/pics/202310261946552.png" alt="光纤"></p><h4 id="2-3-2-非导引型传输媒体"><a href="#2-3-2-非导引型传输媒体" class="headerlink" title="2.3.2 非导引型传输媒体"></a>2.3.2 非导引型传输媒体</h4><ol><li><strong>微波</strong></li></ol><p><img src="https://clb.pages.dev/img/pics/202310261954118.png" alt="微波"></p><ol start="2"><li>其他无线电波</li><li>红外线和可见光</li></ol><p>总结:</p><p><img src="https://s2.loli.net/2024/12/11/rJEqC5WxGv2jPgN.png" alt="89af6958d4fa2d26fa9f7a3d711032cb"></p><h3 id="2-4-信道复用技术"><a href="#2-4-信道复用技术" class="headerlink" title="2.4 信道复用技术"></a>2.4 信道复用技术</h3><blockquote><p>复用:当网络中传输媒体的传输容量大于多条单一信道传输的总通信量时,可利用复用技术<mark>在一条物理线路上建立多条通信信道</mark>来共享传输媒体的带宽。</p></blockquote><img src="https://clb.pages.dev/img/pics/image-20231027222210209.png" alt="信道复用" style="zoom:80%;" /><h4 id="2-4-1-频分复用"><a href="#2-4-1-频分复用" class="headerlink" title="2.4.1 频分复用"></a>2.4.1 频分复用</h4><blockquote><p><strong>频分复用</strong>(Frequency-division multiplexing,<code>FDM</code>):<mark>将多路基带信号调制到不同频率载波</mark>上,再进行<mark>叠加形成一个复合信号</mark>,接收端的分用器通过滤波将各路信号滤出,将合成的复合信号恢复成原始的多路信号。</p></blockquote><img src="https://clb.pages.dev/img/pics/202310272254062.png" style="zoom: 67%;" /><h4 id="2-4-2-时分复用"><a href="#2-4-2-时分复用" class="headerlink" title="2.4.2 时分复用"></a>2.4.2 时分复用</h4><blockquote><p>时分复用(Time Division Multiplexing,<code>TDM</code>):将传输线路的带宽资源<mark>按时间轮流分配</mark>给不同的用户,用户只能在分配的时间里使用线路传输数据(类似于<mark>时间片轮转</mark>)</p><p>存在的不足:使用时分复用系统传送计算机数据时,由于计算机数据的突发性质,用户对分配到的子信道的利用率一般不高。(如果A暂时不使用信道,其他用户正常使用,时分复用还是会给A分配时间,而且这段时间内其他用户也不能使用)</p></blockquote><img src="https://clb.pages.dev/img/pics/202310272254453.png" alt="时分复用" style="zoom: 67%;" /><blockquote><p>统计时分复用(statistical time division multiplexing,<code>STDM</code>):<mark>动态地按需分配</mark>共用信道的时隙,只将需要传送数据的终端接入共用信道,可以提高信道的利用率</p><p>帧与帧之间留有空隙用来存储用户的地址信息,因为不是固定的分配给某个用户</p></blockquote><p><img src="https://clb.pages.dev/img/pics/202310281701023.png" alt="STDM"></p><h4 id="2-4-3-波分复用"><a href="#2-4-3-波分复用" class="headerlink" title="2.4.3 波分复用"></a>2.4.3 波分复用</h4><blockquote><p>波分复用(Wavelength Division Multiplexing,<code>WDM</code>)就是<mark>光的频分复用</mark>。使用一根光纤来同时传输多个光载波信号。现在已能做到在一根光纤上复用几十路或更多路数的光载波信号。</p></blockquote><p><img src="https://clb.pages.dev/img/pics/202310282020333.png" alt="波分复用"></p><h4 id="2-4-3-码分复用"><a href="#2-4-3-码分复用" class="headerlink" title="2.4.3 码分复用"></a>2.4.3 码分复用</h4><blockquote><ul><li><p>码分复用(Code Division Multiplexing,<code>CDM</code>)一种共享信道的方法。</p></li><li><p>由于该技术主要用于无线多址接入(本书中我们不严格区分多址与复用)人们更常用的名词是码分多址CDMA(Code Division Multiple Access)。</p></li><li><p>每一个用户可以在同样的时间使用同样的频带进行通信。<mark>各用户使用经过特殊挑选的不同码型,因此彼此不会造成干扰。</mark></p></li><li><p>最初用于军事,因为这种系统发送的信号<mark>有很强的抗干扰能力,其频谱类似于白噪声,不易被敌人发现</mark></p></li></ul></blockquote><p><img src="https://clb.pages.dev/img/pics/202310282039043.png" alt="码分复用"></p><blockquote><p>规格化内积:将码片序列先写成+1和-1的向量</p><p>例如:<mark>码片序列为(1101)>>> 向量为(+1,+1,-1,+1)</mark></p><p>向量相乘/向量维数 = 内积</p><ul><li><strong>内积为1 –> 发送信号1</strong> <mark>发送的是自己的码片序列</mark></li><li><strong>内积为0 –> 未发送信号</strong> <mark>未发送数据</mark></li><li><strong>内积为-1 –> 发送信号0</strong> <mark>发送的是自己的码片序列的二进制反码</mark></li></ul></blockquote><p>总结:</p><p><img src="https://s2.loli.net/2024/12/12/1qcYJ6iEDtfBUeK.png" alt="54cb1e479b1bb4e0db17cedc1ab76e6e"></p><h3 id="2-5-物理层设备"><a href="#2-5-物理层设备" class="headerlink" title="2.5 物理层设备"></a>2.5 物理层设备</h3><p><img src="https://s2.loli.net/2024/12/11/CVw4bF3Str2GvAN.png" alt="6e769f342636a2622125da20f2e14e0c"></p><h3 id="2-6-相关习题和资料"><a href="#2-6-相关习题和资料" class="headerlink" title="2.6 相关习题和资料"></a>2.6 相关习题和资料</h3><ol><li><a href="https://www.kdocs.cn/view/l/cfixYkxNJ7Ep">第2章 物理层 习题</a></li><li><a href="https://www.kdocs.cn/view/l/ckmnMjp5UQvK">第2章 物理层(思维导图)</a></li></ol><hr><h2 id="3-数据链路层"><a href="#3-数据链路层" class="headerlink" title="3. 数据链路层"></a>3. 数据链路层</h2><blockquote><p><strong><mark>任务:解决数据包在一个网络或一段链路上传输的问题</mark></strong></p></blockquote><h3 id="3-1-数据链路和帧"><a href="#3-1-数据链路和帧" class="headerlink" title="3.1 数据链路和帧"></a>3.1 数据链路和帧</h3><blockquote><ul><li><mark>链路</mark>(Link)是指从一个节点到相邻节点的一段物理线路,而中间没有任何其他的交换节点</li><li><mark>数据链路</mark>(Data Link)是基于链路的。当在一条链路上传送数据时,除需要链路本身,还需要一些必要的通信协议来控制这些数据的传输,把<mark>实现这些协议的硬件和软件加到链路</mark>上,就构成了数据链路</li><li>计算机中的<mark>网络适配器</mark>(俗称网卡)和其相应的软件驱动程序就实现了这些协议。一般的网络适配器都包含了物理层和数据链路层这两层的功能</li><li><mark>帧</mark>(Frame)是数据链路层的协议数据单元</li></ul></blockquote><h3 id="3-2-封装成帧和透明传输"><a href="#3-2-封装成帧和透明传输" class="headerlink" title="3.2 封装成帧和透明传输"></a>3.2 封装成帧和透明传输</h3><h4 id="3-2-1-封装成帧"><a href="#3-2-1-封装成帧" class="headerlink" title="3.2.1 封装成帧"></a>3.2.1 封装成帧</h4><p><img src="https://clb.pages.dev/img/pics/202310291347647.png" alt="封装成帧"></p><h4 id="3-2-2-透明传输问题"><a href="#3-2-2-透明传输问题" class="headerlink" title="3.2.2 透明传输问题"></a>3.2.2 <span style = 'color:#ff6b6b'>透明传输问题</span></h4><blockquote><p>**<mark>2. 透明传输问题出现原因</mark>**:上层应用交付的数据单元中可能<mark>含有和帧定界符相同的数据</mark>而被接收端误认为帧定界符从而导致错误。</p><p>如果不解决上述问题,则数据链路层就会<mark>对上层交付的PDU的内容有所限制</mark>,即<mark>PDU中不能包含帧定界符</mark>。显然,这样的数据链路层没有什么应用价值。</p><p><mark><strong>透明传输</strong></mark>:数据链路层对上层交付的PDU的内容<mark>没有任何限制</mark>,就好像数据链路层不存在一样</p></blockquote><img src="https://clb.pages.dev/img/pics/202310291859630.png" alt="透明传输问题" style="zoom: 67%;" /><h4 id="3-2-3-解决透明传输问题"><a href="#3-2-3-解决透明传输问题" class="headerlink" title="3.2.3 解决透明传输问题"></a>3.2.3 解决透明传输问题</h4><ol><li><strong><mark>字符填充</mark>(字节填充)</strong></li></ol><p><img src="https://clb.pages.dev/img/pics/image-20231029190744213.png" alt="字符填充"></p><ol start="2"><li><strong><mark>零比特填充</mark></strong></li></ol><p><strong>扫描要发送的信息,<span style='color:#ff6b6b'>如果有连续的5个1,立即插入1个0</span>,接收端逆操作即可(01111110为帧定界符)</strong></p><p><img src="https://clb.pages.dev/img/pics/image-20231029192326597.png" alt="零比特填充"></p><h3 id="3-3-差错检测"><a href="#3-3-差错检测" class="headerlink" title="3.3 差错检测"></a>3.3 差错检测</h3><blockquote><ol><li><mark><strong>比特差错</strong></mark>:比特在传输过程中可能会产生差错,0可能变为1,1可能变为0,这叫比特差错,也称为**<mark>误码</mark>**</li><li>**<mark>误码率</mark>**:传输<mark>错误的</mark>比特数与所传输比特<mark>总数</mark>的比率称为误码率</li><li>使用<mark>差错检测码</mark>来检测数据在传输过程中是否产生了比特差错,是数据链路层所要解决的重要问题之一</li></ol></blockquote><h4 id="1-使用循环冗余检验CRC"><a href="#1-使用循环冗余检验CRC" class="headerlink" title="1. 使用循环冗余检验CRC"></a>1. 使用<mark>循环冗余检验<code>CRC</code></mark></h4><p><img src="https://clb.pages.dev/img/pics/image-20231101192256040.png" alt="CRC"></p><h4 id="2-示例"><a href="#2-示例" class="headerlink" title="2. 示例"></a>2. 示例</h4><ol><li>发送方</li></ol><p><img src="https://clb.pages.dev/img/pics/image-20231101193502184.png" alt="CRC"></p><ol start="2"><li>接收方</li></ol><p><img src="https://clb.pages.dev/img/pics/image-20231101193959244.png"></p><h4 id="3-总结"><a href="#3-总结" class="headerlink" title="3. 总结"></a>3. 总结</h4><p><img src="https://clb.pages.dev/img/pics/image-20231101194313756.png"></p><h3 id="3-4-可靠传输"><a href="#3-4-可靠传输" class="headerlink" title="3.4 可靠传输"></a>3.4 可靠传输</h3><h4 id="1-概念"><a href="#1-概念" class="headerlink" title="1. 概念"></a>1. 概念</h4><p><img src="https://clb.pages.dev/img/pics/image-20231101194720144.png"></p><p><img src="https://clb.pages.dev/img/pics/image-20231101195208168.png"></p><h3 id="3-5-可靠传输的实现机制"><a href="#3-5-可靠传输的实现机制" class="headerlink" title="3.5 可靠传输的实现机制"></a>3.5 可靠传输的实现机制</h3><h4 id="3-5-1-停止-等待协议-SW"><a href="#3-5-1-停止-等待协议-SW" class="headerlink" title="3.5.1 停止-等待协议(SW)"></a>3.5.1 停止-等待协议(SW)</h4><ol><li>基本原理</li></ol><blockquote><p>发送一个数据分组<code>DATA0</code>然后等待接收方发来确认信息<code>ACK0</code>后再传输下一个数据分组<code>DATA1</code> ,如果<mark>超时未接收到</mark>接收方发送的确认信息<code>ACK0</code>或者接<mark>收到否认信息</mark><code>NAK</code>就重新发送上一个分组数据分组,数据分组和确认信息的编号都是用来区分和上一个分组或信息是否相同</p></blockquote><p><img src="https://clb.pages.dev/img/pics/image-20231101204223808.png" alt="SW"></p><ol start="2"><li>停止等待协议的信道利用率</li></ol><blockquote><p>$$<br>利用率 = \frac{数据发送时延}{数据发送时延+2个传播时延+确认信息发送时延}<br>$$</p><p><span style='color:#ff6b6b'>确认信息的发送时延一般忽略不计</span></p><p><strong>总结:当往返时间远大于数据帧的发送时延时,信道利用率很低,如果出现超时重传,信道利用率更低</strong>(卫星链路)</p></blockquote><h4 id="3-5-2-回退N帧协议(GBN)"><a href="#3-5-2-回退N帧协议(GBN)" class="headerlink" title="3.5.2 回退N帧协议(GBN)"></a>3.5.2 回退N帧协议(GBN)</h4><p><img src="https://clb.pages.dev/img/pics/image-20231101215803331.png"></p><p><img src="https://clb.pages.dev/img/pics/image-20231101211215263.png"></p><h4 id="3-5-3-选择重传协议(SR)"><a href="#3-5-3-选择重传协议(SR)" class="headerlink" title="3.5.3 选择重传协议(SR)"></a>3.5.3 选择重传协议(SR)</h4><p><img src="https://s2.loli.net/2024/12/11/2RK4Aav3UPYSn9G.png" alt="4f653e6b2efbadc5cdb96f218f95a18a"></p><p><img src="https://clb.pages.dev/img/pics/image-20231101212500499.png"></p><h3 id="3-6-点对点协议-PPP"><a href="#3-6-点对点协议-PPP" class="headerlink" title="3.6 点对点协议(PPP)"></a>3.6 点对点协议(<code>PPP</code>)</h3><ol><li>概述</li></ol><blockquote><p><mark>点对点协议</mark>(Point-to-Point Protocol,PPP)是目前使用最广泛的点对点数据链路层协议,主要组成:</p><ol><li><mark>网络控制协议NCPs</mark></li><li>封装成帧</li><li><mark>链路控制协议LCP</mark></li></ol></blockquote><p><img src="https://clb.pages.dev/img/pics/image-20231101213541707.png" alt="PPP"></p><ol start="2"><li>PPP的<mark>帧格式</mark></li></ol><p><img src="https://clb.pages.dev/img/pics/image-20231101221019454.png"></p><ol start="3"><li>PPP的<mark>透明传输</mark></li></ol><p><img src="https://clb.pages.dev/img/pics/image-20231103154439848.png"></p><ol start="4"><li>PPP的<mark>状态图</mark></li></ol><p><img src="https://clb.pages.dev/img/pics/image-20231101221315932.png"></p><h3 id="3-7-媒体接入控制问题"><a href="#3-7-媒体接入控制问题" class="headerlink" title="3.7 媒体接入控制问题"></a>3.7 媒体接入控制问题</h3><h4 id="3-7-1-媒体接入控制-多址接入-问题"><a href="#3-7-1-媒体接入控制-多址接入-问题" class="headerlink" title="3.7.1 媒体接入控制/多址接入 问题"></a>3.7.1 媒体接入控制/多址接入 问题</h4><blockquote><p>如何协调多个发送站点和接收站点对一个共享传输媒体的占用(<mark>多个媒体同时发送信号导致冲突的问题</mark>)</p></blockquote><h4 id="3-7-2-静态划分信道"><a href="#3-7-2-静态划分信道" class="headerlink" title="3.7.2 静态划分信道"></a>3.7.2 静态划分信道</h4><blockquote><ul><li>频分多址</li><li>时分多址</li><li>码分多址</li></ul><p><mark><strong>这种固定划分信道的方法非常不灵活,通常在物理层中使用,而不是数据链路层</strong></mark></p></blockquote><h4 id="3-7-3-CSMA-CD协议"><a href="#3-7-3-CSMA-CD协议" class="headerlink" title="3.7.3 CSMA/CD协议"></a>3.7.3 <code>CSMA/CD</code>协议</h4><p><img src="https://clb.pages.dev/img/pics/image-20231103161532858.png"></p><ol><li><mark>帧发送流程图</mark></li></ol><p><img src="https://clb.pages.dev/img/pics/image-20231103162311360.png"></p><p><img src="https://clb.pages.dev/img/pics/image-20231103163826951.png"></p><ol start="2"><li><strong><mark>争用期</mark> 2τ ,即端到端传播时延的2倍</strong></li></ol><p><img src="https://clb.pages.dev/img/pics/image-20231103162720907.png"></p><ol start="3"><li><mark>最小帧长</mark><blockquote><p><strong>最小帧长 = 争用期 x 数据传输速率</strong><br> $$<br>l = 2τ \times 数据传输速率<br> $$</p></blockquote></li></ol><p><img src="https://clb.pages.dev/img/pics/image-20231103162936995.png"></p><ol start="4"><li><mark>动态退避</mark>:发生碰撞后进行重传,随着重传次数增加,退避时间也会逐渐增加,减小了碰撞的概率</li></ol><p><img src="https://s2.loli.net/2024/12/11/pCOUsju9bAtvnzm.png" alt="a2087c67a9552b2d56da2809ce24d946"></p><ol start="5"><li><mark>帧接收流程图</mark></li></ol><p><img src="https://s2.loli.net/2024/11/04/CgTJobOG5219Sza.png"></p><p><strong>以太网帧格式</strong>⭐⭐⭐</p><blockquote><p>[!tip]</p><p><span alt='highlight'>6 6 2 N 4,收发协数验,N是46~1500</span></p></blockquote><p><img src="https://s2.loli.net/2024/12/11/rAef2m6tWMEd4cN.png" alt="6e4014c7802054e65afd06e8751ec988"></p><h4 id="3-7-4-CSMA-CA协议"><a href="#3-7-4-CSMA-CA协议" class="headerlink" title="3.7.4 CSMA/CA协议"></a>3.7.4 <code>CSMA/CA</code>协议</h4><p><img src="https://clb.pages.dev/img/pics/202311062053040.png"></p><p>802.11帧格式</p><p><img src="https://s2.loli.net/2024/12/11/URke3lq6tdQBMVW.png" alt="3abfd033ed3297b05864096ce75d898c"></p><h3 id="3-8-MAC、IP地址、ARP协议"><a href="#3-8-MAC、IP地址、ARP协议" class="headerlink" title="3.8 MAC、IP地址、ARP协议"></a>3.8 <code>MAC</code>、<code>IP</code>地址、<code>ARP</code>协议</h3><h4 id="3-8-1-MAC地址"><a href="#3-8-1-MAC地址" class="headerlink" title="3.8.1 MAC地址"></a>3.8.1 <code>MAC</code>地址</h4><p><strong>MAC地址</strong>是<mark>对网络上各接口的唯一标识</mark>,而不是对网络上各设备的唯一标识(路由器有多个接口,所以有多个MAC地址)</p><ol start="2"><li>MAC地址格式<blockquote><p>广播MAC地址为**<mark>FF-FF-FF-FF-FF-FF</mark>**</p></blockquote></li></ol><p><img src="https://s2.loli.net/2024/11/04/NseApHv7KI1bcuz.png"></p><ol start="3"><li>MAC帧有三种:<mark>单播、广播、多播</mark></li></ol><blockquote><p>单播帧:<mark>一对一</mark></p></blockquote><p><img src="https://clb.pages.dev/img/pics/image-20231109213737953.png"></p><blockquote><p>广播帧:<mark>一对全体</mark></p><p>广播地址:<code>FF-FF-FF-FF-FF-FF</code></p></blockquote><p><img src="https://clb.pages.dev/img/pics/image-20231109214347270.png"></p><blockquote><p>多播帧:<mark>一对多</mark></p><p>多播地址:<mark>第二个数是奇数代表是多播地址</mark></p></blockquote><p><img src="https://clb.pages.dev/img/pics/62942804e4b055af8b78ba06.jpeg"></p><h4 id="3-8-2-IP地址"><a href="#3-8-2-IP地址" class="headerlink" title="3.8.2 IP地址"></a>3.8.2 <code>IP</code>地址</h4><blockquote><p>(网络层再详细介绍)</p></blockquote><p><img src="https://clb.pages.dev/img/pics/image-20231109220004217.png"></p><h4 id="3-8-3-ARP协议"><a href="#3-8-3-ARP协议" class="headerlink" title="3.8.3 ARP协议"></a>3.8.3 <code>ARP</code>协议</h4><blockquote><p>主机中保存了一个高速缓存表,<mark>记录了IP和MAC的对应关系</mark></p></blockquote><img src="https://clb.pages.dev/img/pics/image-20231109221234731.png" style="zoom: 67%;" /><blockquote><p>ARP协议动态获取MAC地址</p></blockquote><p><img src="https://s2.loli.net/2024/11/04/8Bc26aSYsHvuFMZ.png"></p><blockquote><p><strong>总结:<code>ARP</code>协议只能逐链路(相邻)使用,不能跨网络使用</strong>(因为需要使用广播帧)</p></blockquote><h3 id="3-9-集线器和交换机的区别"><a href="#3-9-集线器和交换机的区别" class="headerlink" title="3.9 集线器和交换机的区别"></a>3.9 集线器和交换机的区别</h3><p><img src="https://s2.loli.net/2024/11/04/wFTyOZJNMmUB2p9.png"></p><p><img src="https://s2.loli.net/2024/11/04/CPMexZFSpwcYUN1.png"></p><h3 id="3-10-透明网桥的生成树协议STP"><a href="#3-10-透明网桥的生成树协议STP" class="headerlink" title="3.10 透明网桥的生成树协议STP"></a>3.10 透明网桥的生成树协议<code>STP</code></h3><blockquote><ul><li>为了提高以太网的<mark>可靠性</mark>,有时需要在两个以太网之间使用多个透明网桥来提供<mark>冗余链路</mark></li><li>为了避免广播帧在环路中永久兜圈,透明网桥使用生成树协议<code>Spanning Tree Protocol</code>,可以在增加冗余链路提高网络可靠性的同时,又避免环路带来的问题</li></ul></blockquote><p><img src="https://clb.pages.dev/img/pics/image-20231111200010935.png"></p><h3 id="3-11-虚拟局域网VLAN"><a href="#3-11-虚拟局域网VLAN" class="headerlink" title="3.11 虚拟局域网VLAN"></a>3.11 虚拟局域网<code>VLAN</code></h3><blockquote><p>虚拟局域网(<code>Virtual Local Area Network</code>,VLAN)是一种将局域网内的站点划分成与物理位置无关的逻辑组的技术,一个逻辑组就是一个VLAN,VLAN中的各站点具有某些共同的应用需求</p></blockquote><blockquote><p><mark>广播风暴</mark>:<strong>广播风暴会浪费网络资源和各主机的CPU资源</strong></p><ul><li>可以使用路由器切割广播域,防止广播风暴,但是<mark>成本较高</mark></li><li>使用vlan技术划分广播域</li></ul></blockquote><blockquote><ul><li><p><code>A</code>发送广播帧,<code>Access</code>接口接收并<code>打标签</code>,<code>PVID</code>和<code>VID</code>相同的Access端口可以接收该帧,进行<mark>去标签转发</mark></p></li><li><p><code>Trunk</code>端口的<code>PVID</code>如果等于该帧的<code>VID</code>,也会<mark>先去标签转发,然后再打标签</mark>,否则直接转发</p></li><li><p><strong>Trunk端口是连接两个交换机的</strong></p></li></ul></blockquote><p><img src="https://clb.pages.dev/img/pics/image-20231111193532792.png"></p><h3 id="4-思维导图和习题"><a href="#4-思维导图和习题" class="headerlink" title="4. 思维导图和习题"></a>4. 思维导图和习题</h3><p><a href="https://www.kdocs.cn/view/l/cgV0rfS0qZEA">第3章 数据链路层(思维导图)-1 (kdocs.cn)</a></p><p><a href="https://www.kdocs.cn/view/l/ciOEeLV6ffuJ">第3章 数据链路层(思维导图)-2 (kdocs.cn)</a></p><p><a href="https://www.kdocs.cn/view/l/cnRBS8NFgTll">第3章 数据链路层(思维导图)-3 (kdocs.cn)</a></p><p><a href="https://www.kdocs.cn/view/l/cl60fYfO1qZe">第3章 数据链路层 习题(第1部分)</a></p><p><a href="https://www.kdocs.cn/view/l/cenjHDfYLe95">第3章 数据链路层 习题(第2部分)</a></p><hr><h2 id="4-网络层"><a href="#4-网络层" class="headerlink" title="4. 网络层"></a>4. 网络层</h2><h3 id="4-1-概述"><a href="#4-1-概述" class="headerlink" title="4.1 概述"></a>4.1 概述</h3><blockquote><p>网络层的主要任务:<mark>将分组从源主机经过多个网络和多段链路传输到目的主机</mark>,可以将该任务划分为<mark>分组转发</mark>和<mark>路由选择</mark>两种重要的功能</p></blockquote><h3 id="4-2-网络层提供的两种服务"><a href="#4-2-网络层提供的两种服务" class="headerlink" title="4.2 网络层提供的两种服务"></a>4.2 网络层提供的两种服务</h3><h4 id="4-2-1-面向连接的虚电路服务"><a href="#4-2-1-面向连接的虚电路服务" class="headerlink" title="4.2.1 面向连接的虚电路服务"></a>4.2.1 面向连接的虚电路服务</h4><blockquote><ul><li>虚电路表示这是一条逻辑上的连接,分组沿着这条逻辑连接按照存储转发方式传送,而<mark>不是真正建立了一条物理连接</mark></li><li>分组的首部<strong>仅在连接建立阶段使用完整的目的主机地址</strong>,之后每个分组的首部只需要携带一条<mark>虚电路编号</mark>即可</li><li>虚电路服务采用的是<strong>分组交换方式</strong>!!</li></ul></blockquote><p><img src="https://clb.pages.dev/img/pics/image-20231112213851481.png"></p><h4 id="4-2-2-无连接的数据报服务"><a href="#4-2-2-无连接的数据报服务" class="headerlink" title="4.2.2 无连接的数据报服务"></a>4.2.2 无连接的数据报服务</h4><blockquote><ul><li><code>TCP/IP</code>使用的就是这种<mark>简单灵活的</mark>、<mark>无连接的</mark>、<mark>不可靠的</mark>数据报服务</li><li><strong>核心思想:<mark>可靠通信应由用户主机来保证</mark></strong></li><li><strong>由于网络自身不提供端到端的可靠传输服务,这就使得网络中的路由器可以做得比较简单,<mark>大大降低了网络造价</mark></strong></li><li><strong>这种设计思想的<mark>运行方式灵活</mark>、能够适应多种应用。因特网能够发展到今日的规模,充分证明了当初采取这种设计思想的<mark>正确性</mark></strong></li></ul></blockquote><p><img src="https://clb.pages.dev/img/pics/image-20231112220730923.png"></p><h4 id="4-2-3-IP地址"><a href="#4-2-3-IP地址" class="headerlink" title="4.2.3 IP地址"></a>4.2.3 IP地址</h4><blockquote><p><strong>概念</strong></p></blockquote><ul><li>网际协议(<code>Internet Protocol</code>,<code>IP</code>)是<code>TCP/IP</code>体系结构网际层中的核心协议</li><li><code>IPv4</code>地址就是给因特网(Internet)上的每一台主机(或路由器)的每一个<mark>接口</mark>分配一个在全世界范围内是唯一的<mark>32比特</mark>的标识符</li></ul><h4 id="4-2-4-分类编址的IP"><a href="#4-2-4-分类编址的IP" class="headerlink" title="4.2.4 分类编址的IP"></a>4.2.4 分类编址的<code>IP</code></h4><p><img src="https://s2.loli.net/2024/10/16/RY6ZTIlj72U9r3m.png"></p><h4 id="4-2-5-划分子网的IP"><a href="#4-2-5-划分子网的IP" class="headerlink" title="4.2.5 划分子网的IP"></a>4.2.5 划分子网的<code>IP</code></h4><blockquote><p><strong>分类编址方法不够灵活且容易造成<mark>大量地址浪费</mark>,划分子网编址方法对其进行改进(“打补丁”)</strong></p></blockquote><img src="https://clb.pages.dev/img/pics/image-20231113184551320.png" style="zoom:80%;" /><blockquote><p>划分子网案例</p></blockquote><img src="https://clb.pages.dev/img/pics/image-20231113185650959.png" style="zoom:67%;" /><h4 id="4-2-6-无分类编制的IP"><a href="#4-2-6-无分类编制的IP" class="headerlink" title="4.2.6 无分类编制的IP"></a>4.2.6 <mark>无分类编制</mark>的<code>IP</code></h4><img src="https://clb.pages.dev/img/pics/image-20231113190642722.png" style="zoom:67%;" /><blockquote><p><mark>无分类域间路由选择</mark>(<code>C</code>lassless <code>I</code>nter-<code>D</code>omain <code>R</code>outing,<code>CIDR</code> )</p></blockquote><img src="https://clb.pages.dev/img/pics/image-20231113190939322.png" style="zoom:67%;" /><blockquote><p><strong>练习</strong></p></blockquote><p><img src="https://clb.pages.dev/img/pics/image-20231113193113576.png"></p><blockquote><p><strong><mark>路由聚合</mark></strong></p><p><strong>找出共同前缀,其余位取0</strong></p></blockquote><img src="https://clb.pages.dev/img/pics/image-20231113193616181.png" style="zoom:67%;" /><h3 id="4-3-IP数据报的发送和转发过程"><a href="#4-3-IP数据报的发送和转发过程" class="headerlink" title="4.3 IP数据报的发送和转发过程"></a>4.3 IP数据报的发送和转发过程</h3><blockquote><p><strong>IP数据报的<mark>发送</mark>和<mark>转发</mark>过程</strong></p><ul><li><p><strong>主机发送IP数据报</strong></p><ul><li><mark>判断目的主机是否与自己在同一个网络:</mark><ul><li>若在同一个网络,则属于<mark>直接交付</mark>,直接发送给目的主机</li><li>若不在同一个网络,则属于<mark>间接交付</mark>,传输给主机所在网络的默认网关(路由器),<mark>由默认网关帮忙转发</mark>)</li></ul></li></ul></li><li><p><strong>路由器转发IP数据报</strong></p><ul><li><mark>检查IP数据报首部是否出错:</mark><ul><li>若出错,则直接丢弃该IP数据报并通告源主机</li><li>若没有出错,则进行转发</li></ul></li><li><mark>根据IP数据报的目的地址在路由表中查找匹配的条目:</mark><ul><li>若找到匹配的条目,则转发给条目中指示的下一跳;</li><li>若找不到,则丢弃该IP数据报并通告源主机;</li></ul></li></ul></li></ul></blockquote><p><img src="https://clb.pages.dev/img/pics/010ac0446c7c01e405dd1b7baf38d50a.png"></p><blockquote><p><mark>路由器不转发广播IP数据报</mark>,即路由器隔离广播域,如果因特网中数量巨大的路由器收到广播IP数据报后都进行转发,则会<mark>造成巨大的广播风暴,严重浪费因特网资源</mark></p><ul><li><mark>中继器</mark>和<mark>集线器</mark>工作在<mark>物理层</mark>,<strong>既不隔离冲突域也不隔离广播域</strong></li><li><mark>网桥</mark>和<mark>交换机</mark>(多端口网桥)工作在<mark>数据链路层</mark>,<strong>可以隔离冲突域,不能隔离广播域</strong></li><li><mark>路由器</mark>工作在<mark>网络层</mark>,<strong>既隔离冲突域,也隔离广播域</strong></li></ul></blockquote><h3 id="4-4-静态路由配置"><a href="#4-4-静态路由配置" class="headerlink" title="4.4 静态路由配置"></a>4.4 静态路由配置</h3><h4 id="1-静态路由的配置"><a href="#1-静态路由的配置" class="headerlink" title="1. 静态路由的配置"></a>1. 静态路由的配置</h4><img src="https://clb.pages.dev/img/pics/image-20231117222851885.png" style="zoom:67%;" /><h4 id="2-默认路由"><a href="#2-默认路由" class="headerlink" title="2. 默认路由"></a>2. 默认路由</h4><ol><li><p>默认路由:默认路由条目中的目的网络<code>0.0.0.0/0</code>,其中<code>0.0.0.0</code>表示任意网络,而网络前缀“/0”(相应的地址掩码为<code>0.0.0.0</code>)是<mark>最短的网络前缀</mark></p></li><li><p>特定主机路由:特定主机路由条目中的目的网络<code>192.168.2.1/32</code>,其中<code>192.168.2.1</code>是特定主机的IP地址,而网络前缀“/32”(相应地址掩码为<code>255.255.255.255</code>)是<mark>最长的网络前缀</mark></p></li><li><p>路由器在查找转发表转发IP数据报时,遵循“最长前缀匹配”的原则,因此<mark>默认路由匹配优先级最低,特定主机路由条目的匹配优先级最高</mark></p></li></ol><img src="https://clb.pages.dev/img/pics/image-20231117223841323.png" style="zoom:67%;" /><h4 id="3-路由环路"><a href="#3-路由环路" class="headerlink" title="3. 路由环路"></a>3. 路由环路</h4><ul><li>错误配置静态路由有可能导致<mark>路由环路</mark>问题</li><li>为了<mark>防止IP数据报在环路中永久兜圈</mark>,在IP数据报首部设有<mark>生存时间TTL</mark>,TTL为0时会被丢弃</li></ul><p><strong>路由环路问题产生原因:</strong></p><ol><li>路由配置错误</li></ol><img src="https://clb.pages.dev/img/pics/image-20231118125828914.png" style="zoom:67%;" /><ol start="2"><li>聚合了不存在的IP地址</li></ol><blockquote><p>需要给不存在的IP地址配置黑洞路由,防止IP数据报转发到其他路由器</p></blockquote><img src="https://clb.pages.dev/img/pics/image-20231118133214440.png" style="zoom:67%;" /><ol start="3"><li>网络故障</li></ol><blockquote><p>可以在网络发生故障时<mark>添加一条针对该IP的黑洞路由</mark>,待网络正常后又将其设置为失效状态</p></blockquote><img src="https://clb.pages.dev/img/pics/image-20231118134111043.png" style="zoom: 67%;" /><h3 id="4-5-路由选择协议"><a href="#4-5-路由选择协议" class="headerlink" title="4.5 路由选择协议"></a>4.5 路由选择协议</h3><ol><li>路由选择分为两类</li></ol><table><thead><tr><th align="left"><mark>静态路由选择</mark></th><th align="left"><mark>动态路由选择</mark></th></tr></thead><tbody><tr><td align="left">采用<mark>人工配置</mark>的方式给路由器添加网络路由、默认路由和特定主机路由等路由条目</td><td align="left">路由器通过路由选择协议<mark>自动获取</mark>路由信息</td></tr><tr><td align="left">静态路由选择<mark>简单、开销小</mark>,但<mark>不能及时适应网络状态(流量、拓扑等)的变化</mark></td><td align="left">动态路由选择比较<mark>复杂、开销比较大</mark>,但<mark>能较好地适应网络状态的变化</mark></td></tr><tr><td align="left">静态路由选择一般只在<mark>小规模网络</mark>中采用</td><td align="left">动态路由选择适用于<mark>大规模网络</mark></td></tr></tbody></table><ol start="2"><li>因特网采用<mark>分层次</mark>的路由选择协议</li></ol><p>因特网是全球最大的互联网,它所采取的路由选择协议具有以下三个主要特点:</p><ul><li>自适应:因特网采用<mark>动态路由</mark>选择,能较好地适应网络状态的变化。</li><li>分布式:因特网中的各路由器通过相互间的信息交互,<mark>共同完成路由信息的获取和更新</mark>。</li><li>分层次:将整个因特网划分为许多较小的<mark>自治系统</mark>(<code>Autonomous System,AS</code>),在自治系统内部和外部采用不同类别的路由选择协议,分别进行路由选择</li></ul><img src="https://clb.pages.dev/img/pics/image-20231118150928383.png" style="zoom:67%;" /><h3 id="4-6-RIP"><a href="#4-6-RIP" class="headerlink" title="4.6 RIP"></a>4.6 <code>RIP</code></h3><p>路由信息协议(<code>Routing Information Protocol,RIP</code>)是内部网关协议中最先得到广泛使用的协议之一</p><ol><li>相关概念</li></ol><ul><li><p>RIP使用<mark>跳数</mark>(<code>Hop Count</code>)作为度量(<code>Metric</code>)来衡量到达目的网络的距离</p><ul><li><p>RIP将<mark>路由器到直连网络的距离定义为1</mark></p></li><li><p>RIP将路由器到非直连网络的距离定义为<mark>所经过的路由器数加1</mark></p></li><li><p>RIP允许一条路径最多只能包含15个路由器,<mark>距离等于16时相当于不可达</mark>,因此RIP只适用于<mark>小型互<br>联网</mark></p></li></ul></li></ul><img src="https://clb.pages.dev/img/pics/image-20231118140619521.png" style="zoom: 50%;" /><ol start="2"><li><p>RIP认为好的路由就是“距离短”的路由,也就是所<mark>通过路由器数量最少的路由</mark>,和传输速率和物理距离等无关</p></li><li><p>RIP的<mark>3个重要特点</mark></p></li></ol><img src="https://clb.pages.dev/img/pics/image-20231118141136250.png" style="zoom: 67%;" /><ol start="4"><li><p><strong><mark>RIP的工作原理</mark></strong></p><ol><li>基本流程</li></ol><img src="https://clb.pages.dev/img/pics/image-20231118141750507.png" style="zoom:67%;" /><ol start="2"><li><strong><mark>RIP路由条目的更新规则</mark></strong></li></ol><img src="https://clb.pages.dev/img/pics/image-20231118142447662.png" style="zoom:80%;" /><ol start="3"><li><p><mark><strong>坏消息传播的慢</strong></mark></p><p><img src="https://clb.pages.dev/img/pics/image-20231118220443883.png"></p></li></ol></li><li><p>RIP的优缺点</p></li></ol><table><thead><tr><th>优点</th><th>缺点</th></tr></thead><tbody><tr><td>实现<mark>简单</mark>,路由器<mark>开销小</mark></td><td>RIP限制了<mark>最大RIP距离为<code>15</code></mark>这就限制了使用RIP的自治系统AS的规模</td></tr><tr><td>如果一个路由器发现了RIP距离更短的路由,那么这种更新信息就传播得很快,即“<mark>好消息传播得快</mark>”</td><td>相邻路由器之间交换的路由信息是路由器中的完整路由表,因而<mark>随着网络规模的扩大,开销也随之增大</mark></td></tr><tr><td></td><td>“<mark><strong>坏消息传播得慢</strong></mark>”,使更新过程的收敛时间过长。因此,对于规模较大的自治系统AS,应当使用<code>OSPF</code>协议</td></tr></tbody></table><h3 id="4-7-OSPF"><a href="#4-7-OSPF" class="headerlink" title="4.7 OSPF"></a>4.7 <code>OSPF</code></h3><h4 id="1-基本概念"><a href="#1-基本概念" class="headerlink" title="1. 基本概念"></a>1. 基本概念</h4><blockquote><p>开放最短路径优先(<code>Open Shortest Path First,OSPF</code>)协议是为了克服路由信息协议RIP的缺点在1989年开发出来的</p><ul><li>“<mark>开放</mark>”表明<code>OSPF</code>协议不是受某一厂商控制,而是公开发表的</li><li>“<mark>最短路径优先</mark>”是因为使用了Dijkstra提出的<mark>最短路径算法</mark>(<code>Shortest Path First,SPF</code>)</li></ul></blockquote><h4 id="2-特点"><a href="#2-特点" class="headerlink" title="2. 特点"></a>2. 特点</h4><blockquote><ul><li><code>OSPF</code>是<mark>基于<strong>链路状态</strong></mark>的,而不像<code>RIP</code>是基于距离向量的</li><li><code>OSPF</code>基于链路状态并采用最短路径算法计算路由,<mark>从算法上保证了不会产生路由环路</mark></li><li><code>OSPF</code><mark>不限制网络规模,更新效率高,收敛速度快</mark></li></ul></blockquote><h4 id="3-相关概念"><a href="#3-相关概念" class="headerlink" title="3. 相关概念"></a>3. 相关概念</h4><ol><li><p>链路状态(<code>Link State,LS</code>)是指本路由器都和哪些路由器相邻,以及相应链路的“代价(cost)”,类似RIP中的距离</p><blockquote><p>“代价”用来表示费用、距离、时延和带宽等,这些都由网络管理人员来决定</p></blockquote></li></ol><img src="https://clb.pages.dev/img/pics/image-20231118223059765.png" style="zoom:80%;" alt='举例-思科路由器的代价计算标准' /><ol start="2"><li><strong><code>OSPF</code>路由器邻居关系的建立和维护</strong><ul><li><strong>如果在死亡倒计时为0时还未收到邻居的问候分组,则认为该邻居不可达,</strong></li><li><strong>在接受到邻居的问候分组后,刷新死亡倒计时为40s</strong></li><li><strong>路由器每10s会向邻居发送问候分组</strong></li></ul></li></ol><img src="https://clb.pages.dev/img/pics/image-20231118223958856.png" style="zoom:67%;" /><ol start="3"><li><mark><strong>链路状态数据库</strong></mark>(<code>Link State Database,LSDB</code>)</li></ol><p><img src="https://clb.pages.dev/img/pics/image-20231118224728007.png"></p><blockquote><p>使用<code>OSPF</code>的各路由器,基于链路状态数据库LSDB进行最短路径优先计算,构建出各自到达其他各路由器的最短路径,即构建各自的路由表(<mark>根据链路状态数据库得出全局带权有向图,使用Dijkstra算法得出个路由器的最短路径</mark>)</p></blockquote><p><img src="https://clb.pages.dev/img/pics/image-20231118225157334.png"></p><ol start="4"><li><strong><code>OSPF</code>的五种分组类型</strong><ul><li>类型1,<mark>问候(<code>Hello</code>)分组</mark>:用来发现和维护邻居路由器的可达性。</li><li>类型2,<mark>数据库描述(<code>Database Description</code>)分组</mark>:向邻居路由器给出自己的链路状态数据库中的所有链路状态项目的摘要信息</li><li>类型3,<mark>链路状态请求(<code>Link State Request</code>)分组</mark>:向邻居路由器请求发送某些链路状态项目的详细信息。</li><li>类型4,<mark>链路状态更新(<code>Link State Update</code>)分组</mark>:路由器使用这种分组将其链路状态进行洪泛发送,即用洪泛法对全网更新链路状态</li><li>类型5,<mark>链路状态确认(<code>Link State Acknowledgment</code>)分组</mark>:这是对链路状态更新分组的确认分组。</li></ul></li></ol><h4 id="4-基本工作原理"><a href="#4-基本工作原理" class="headerlink" title="4. 基本工作原理"></a>4. 基本工作原理</h4><img src="https://clb.pages.dev/img/pics/image-20231119152547862.png" style="zoom:67%;" /><blockquote><p>采用划分区域的方法,虽然使交换信息的种类增多了,同时也使OSPF协议更加复杂了,但这样做能<mark>使每一个区域内部交换路由信息的通信量大大减小,因而使OSPF协议能够用于规模更大的自治系统AS</mark></p></blockquote><img src="https://clb.pages.dev/img/pics/image-20231119152848527.png" style="zoom:67%;" /><h3 id="4-8-BGP"><a href="#4-8-BGP" class="headerlink" title="4.8 BGP"></a>4.8 <code>BGP</code></h3><h4 id="1-基本概念-1"><a href="#1-基本概念-1" class="headerlink" title="1. 基本概念"></a>1. 基本概念</h4><blockquote><ul><li><strong>边界网关协议</strong>(<code>Border Gateway Protocol,BGP</code>)属于<mark>外部网关协议</mark><code>EGP</code>这个类别,用于自治系统<code>AS</code>之间的路由选择协议</li><li>由于在不同AS内度量路由的“代价”(距离、带宽、费用等)可能不同,因此<mark>对于AS之间的路由选择,使用统一的“代价”作为度量来寻找最佳路由是不行的</mark></li><li>AS之间的路由选择还必须考虑相关策略(政治、经济、安全等)</li></ul><p><strong>总结:<code>BGP</code>只能是力求寻找一条能够到达目的网络且比较好的路由(即不能兜圈子),而并非要寻找一条最佳路由</strong></p></blockquote><img src="https://clb.pages.dev/img/pics/image-20231119154933166.png" style="zoom: 80%;" /><blockquote><ul><li><p>在配置BGP时,每个AS的管理员要选择至少一个路由器作为该AS的“<mark>BGP发言人</mark>”</p></li><li><p>一般来说,两个BGP发言人都是通过一个共享网络连接在一起的,而BGP发言人往往就是<mark>BGP边界路由器</mark></p></li><li><p>使用<span alt='highlight'>TCP</span>连接交换路由信息的两个BGP发言人,彼此称为对方的<mark>邻站</mark>(neighbor)或<mark>对等站</mark>(peer)</p></li><li><p>BGP发言人除了运行BGP协议外,还必须运行自己所在AS所使用的内部网关协议IGP,例如RIP或OSPF</p></li><li><p>BGP发言人交换网络可达性的信息,也就是<mark>要到达某个网络所要经过的一系列自治系统</mark></p></li><li><p>当BGP发言人相互交换了网络可达性的信息后,各BGP发言人就<mark>根据所采用的策略</mark>,从收到的路由信息中<mark>找出到达各自治系统的较好的路由</mark>,也就是<mark>构造出树形结构且不存在环路的自治系统连通图</mark></p></li></ul></blockquote><blockquote><p>[!important]</p><p>BGP会话主要有以下几种类型:</p><ol><li><strong>内部BGP (iBGP) 会话</strong>:在同一个自治系统(AS)内部的BGP路由器之间建立的会话。IBGP的主要特点是它不改变AS-path属性,因为它不跨越AS边界。</li><li><strong>外部BGP (eBGP) 会话</strong>:在不同AS之间建立的BGP会话。EBGP会增加AS-path属性,记录路径中经过的AS。</li><li>联盟外部BGP (confed-EBGP) 会话:这种会话类型是在同一个联盟内部不同成员AS之间的BGP会话,用于在更大的AS内部划分更小的自治区域并管理路由信息。</li></ol></blockquote><h4 id="2-四种BGP报文"><a href="#2-四种BGP报文" class="headerlink" title="2. 四种BGP报文"></a>2. 四种BGP报文</h4><img src="https://clb.pages.dev/img/pics/image-20231119155610676.png" /><img src="https://clb.pages.dev/img/pics/image-20231119160021206.png" /><h3 id="4-9-IPv4分组格式"><a href="#4-9-IPv4分组格式" class="headerlink" title="4.9 IPv4分组格式"></a>4.9 IPv4分组格式</h3><p><img src="https://clb.pages.dev/img/pics/image-20231119161128658.png"></p><table><thead><tr><th align="center">字段</th><th>含义</th></tr></thead><tbody><tr><td align="center">版本</td><td>长度为<mark>4</mark>个比特,用来表示IP协议的版本,<mark>通信双方使用的IP协议的版本必须一致</mark>,目前广泛使用的IP协议的版本号为4(即IPv4)</td></tr><tr><td align="center"><strong>首部长度</strong></td><td>长度为<mark>4</mark>个比特,该字段的取值<mark>以4B为单位</mark>,用来表示IPV4数据报的首部长度。<br/>最小取值为二进制的0101,即十进制的5,再乘以4字节单位,<mark>表示IPv4数据报首部只有20字节固定部分</mark><br />最大取值为二进制的1111,即十进制的15,再乘以4字节单位,<mark>表示IPV4数据报首部包含20字节固定部分和最大40字节可变部分</mark></td></tr><tr><td align="center"><strong>总长度</strong></td><td>长度为<mark>16</mark>个比特,该字段的取值<mark>以字节为单位</mark>,用来<mark>表示IPv4数据报的长度</mark>(<mark>首部长度+数据载荷长度</mark>)<br />最大取值为二进制的16个比特1,即十进制的65535(很少传输这么长的IPv4数据报)</td></tr><tr><td align="center"><strong>标识</strong></td><td>长度为<mark>16</mark>个比特,<mark>属于同一个IPv4数据报的各分片数据报应该具有相同的标识</mark></td></tr><tr><td align="center"><strong>标志</strong></td><td>最低位(<code>More Fragment,MF</code>)<br /> MF=1表示本分片后面还有分片<br/> MF=0表示本分片后面没有分片<br/>中间位(<code>Don't Fragment,DF</code>)<br/> DF=1表示不允许分片<br/> DF=0表示允许分片<br/>最高位为保留位,必须设置为0</td></tr><tr><td align="center"><strong>片偏移</strong></td><td>长度为<mark>16</mark>个比特,该字段的取值,<mark>以8B为单位</mark>,用来指出分片IPv4数据报的<mark>数据载荷偏移其在原IPv4数据报的位置有多远,只能为整数,否则必须调整前一个分片长度</mark></td></tr><tr><td align="center"><strong>生存时间</strong></td><td>长度为<mark>8</mark>个比特,最大取值为二进制的11111111,即十进制的255。该字段的取值最初以秒为单位。因此,IPv4数据报的最大生存时间最初为255秒。路由器转发IPv4数据报时,将其首部中该字段的值减去该数据报在路由器上所耗费的时间,若结果不为0就转发,否则就丢弃<br />生存时间字段<mark>后来改为以“跳数”为单位,路由器收到待转发的IPv4数据报时,将其首部中的该字段的值减1,若结果不为0就转发,否则就丢弃</mark></td></tr><tr><td align="center"><strong>协议</strong></td><td>长度为8个比特,用来<mark>指明IPv4数据报的数据载荷是何种协议数据单元PDU</mark><br /><img src="https://clb.pages.dev/img/pics/image-20231119165547509.png"></td></tr><tr><td align="center"><strong>首部检验和</strong></td><td>长度为<mark>16</mark>个比特,用于<mark>检测IPv4数据报在传输过程中其首部是否出现了差错</mark><br />IPv4数据报<mark>每经过一个路由器</mark>,其首部中的某些字段的值(例如生存时间TTL、标志以及片偏移等)都可能发生变化,因此路由器都要<mark>重新计算一下首部检验和</mark></td></tr><tr><td align="center"><strong>源IP地址和目的IP地址</strong></td><td>长度都为<mark>32</mark>个比特,用来填写发送(接收)IPv4数据报的源(目的)主机的IPv4地址</td></tr></tbody></table><p><img src="https://s2.loli.net/2024/11/04/dfVT5pR8rqUnzoK.png"></p><blockquote><p><code>MTU</code>:<mark>最大传送单元,超过最大传送单元的长度的IP数据报必须分片</mark></p></blockquote><p><img src="https://clb.pages.dev/img/pics/image-20231119164911210.png"></p><p><img src="https://clb.pages.dev/img/pics/image-20231119171554203.png"></p><h3 id="4-10-IPv6分组格式"><a href="#4-10-IPv6分组格式" class="headerlink" title="4.10 IPv6分组格式"></a>4.10 IPv6分组格式</h3><p>1.数据报格式</p><p><img src="https://s2.loli.net/2024/11/01/5TyWeNH2iXwIkc8.png" alt="image-20241014124411673"></p><p>2.IPv4和IPv6的区别</p><blockquote><p> [!important] </p><p> 1.IPv6将地址从32位(4B)扩大到<strong>128位(16B)<strong>,更大的地址空间($2^{96}$)。<br> 2.IPv6将IPv4的</strong>校验和字段彻底移除</strong>,以减少每跳的处理时间。<br> 3.IPv6将IPv4的可选字段移出首部,变成了<strong>扩展首部</strong>,成为灵活的首部格式,路由器通常不对扩展首部进行检查,<br> 大大提高了路由器的处理效率。<br> 4.IPv6支持<strong>即插即用(即自动配置)<strong>,不需要DHCP协议。<br> 5.IPv6首部长度必须是</strong>8B的整数倍</strong>,IPv4首部是4B的整数倍:<br> 6.IPv6<strong>只能在主机处分片</strong>,IPv4可以在路由器和主机处分片。<br> 7.ICMPv6:附加报文类型“分组过大”<br> 8.IPv6支持资源的预分配,支持实时视像等要求,保证一定的带宽和时延的应用。<br> 9.IPv6取消了协议字段,改成下一个首部字段。<br> 10.IPv6取消了总长度字段,改用有效载荷长度字段。<br> 11.IPv6取消了服务类型字段。</p></blockquote><p>3.从IPv4向IPv6过渡</p><blockquote><ol><li><strong>双栈协议</strong>:一台设备上同时装有IPv4和IPv6两个协议栈</li><li><strong>隧道技术</strong>:IPv6数据报要进入IPv4网络时<strong>将整个IPv6数据报封装成IPv4数据报的数据部分</strong>传输,离开IPv4网络时再将数据部分拆出IPv6数据报</li></ol></blockquote><h3 id="4-11-ICMP"><a href="#4-11-ICMP" class="headerlink" title="4.11 ICMP"></a>4.11 <code>ICMP</code></h3><h4 id="1-基本概念-2"><a href="#1-基本概念-2" class="headerlink" title="1. 基本概念"></a>1. 基本概念</h4><blockquote><ul><li>为了更有效地转发IP数据报以及提高IP数据报交付成功的机会,TCP/IP体系结构的网际层使用了网际控制报文协议(<code>Internet Control Message Protocol,ICMP</code>)</li><li>主机或路由器使用ICMP来发送<mark>差错报告报文</mark>和<mark>询问报文</mark></li><li>ICMP报文被<mark>封装在IP数据报中作为数据载荷</mark>发送</li></ul></blockquote><h4 id="2-ICMP报文数据类型"><a href="#2-ICMP报文数据类型" class="headerlink" title="2. ICMP报文数据类型"></a>2. ICMP报文数据类型</h4><ul><li><p><strong>差错报告报文</strong>:用来<mark>向主机或路由器报告差错情况</mark></p><ul><li><p><strong>终点不可达</strong>:<mark>当路由器或主机不能交付IP数据报时,就向源点发送终点不可达报文</mark>,具体可再根据ICMP的代码字段细分为目的网络不可达、目的主机不可达、目的协议不可达、目的端口不可达、目的网络未知、目的主机未知等13种</p><p><img src="https://clb.pages.dev/img/pics/image-20231119222854859.png"></p></li><li><p><strong>源点抑制</strong>:<mark>当路由器或主机由于拥塞而丢弃IP数据报时,就向发送该IP数据报的源点发送源点抑制报文</mark>,使源点知道应当把IP数据报的发送速率放慢</p><img src="https://clb.pages.dev/img/pics/image-20231119222400135.png" /></li><li><p><strong>超时</strong>:</p><ul><li>当路由器收到一个目的IP地址不是自己的IP数据报时,会将其首部中生存时间TTL字段的值减1。若结果不为0,则路由器将该数据报转发出去;<mark>若结果为0,路由器不但要丢弃该数据报,还要向发送该IP数据报的源点发送时间超过(超时)报文</mark></li><li>另外,当终点在预先规定的时间内<mark>未能收到一个数据报的全部数据报分片</mark>时,就把已收到的数据报片都<mark>丢弃</mark>,也会<mark>向源点发送时间超过(超时)报文</mark></li></ul><img src="https://clb.pages.dev/img/pics/image-20231120194354557.png" /></li><li><p><strong>参数问题</strong>:当路由器或目的主机收到IP数据报后,根据其首部中的检验和字段的值发现首部在传送过程中出现了误码,就丢弃该数据报,并向发送该数据报的源点发送参数问题报文</p><p><img src="https://clb.pages.dev/img/pics/image-20231120194840526.png"></p></li><li><p><strong>改变路由(重定向)</strong>:路由器把<mark>改变路由报文</mark>发送给主机,让主机知道下次<mark>应将IP数据报发送给另外的路由器</mark>,这样可以<mark>通过更好的路由到达目的主机</mark></p><p><img src="https://clb.pages.dev/img/pics/image-20231120195049808.png"></p></li></ul></li><li><p>**以下情况<mark>不应发送ICMP差错报告报文</mark>**:</p><ul><li><p>对<mark>ICMP差错报告报文</mark>不再发送ICMP差错报告报文</p></li><li><p>对第一个分片的IP数据报片的所有<mark>后续数据报片</mark>都不发送ICMP差错报告报文</p></li><li><p>对<mark>具有多播地址的IP数据报</mark>都不发送ICMP差错报告报文</p></li><li><p>对具有<mark>特殊地址</mark>(例如<code>127.0.0.0</code>或<code>0.0.0.0</code>)的IP数据报不发送ICMP差错报告报文。</p></li><li><p><strong>询问报文</strong>:用来<mark>向主机或路由器询问情况</mark></p><ul><li><p><strong>回送请求和回答</strong>:</p><p>由主机或路由器向一个特定的目的主机或路由器发出。收到此报文的主机或路由器必须给发送该报文的源主机或路由器发送ICMP回送回答报文。这种询问报文<mark>用来测试目的站是否可达以及了解其有关状态</mark></p></li><li><p><strong>时间戳请求和回答</strong>:</p><p>用来请求某个主机或路由器回答当前的日期和时间。在ICMP时间戳回答报文中有一个32比特的字段,其中写入的整数代表从1900年1月1日起到当前时刻一共有多少秒。这种询问报文<mark>用来进行时钟同步和测量时间</mark></p></li></ul></li></ul></li></ul><h4 id="3-ICMP的典型应用"><a href="#3-ICMP的典型应用" class="headerlink" title="3. ICMP的典型应用"></a>3. ICMP的典型应用</h4><h5 id="1-PING"><a href="#1-PING" class="headerlink" title="1. PING"></a>1. <code>PING</code></h5><blockquote><p>分组网间探测(Packet InterNet Groper,<code>PING</code>)</p><p>在命令行中<mark>使用ping命令用来测试主机或路由器之间的连通性</mark></p><ul><li>PING是TCP/IP体系结构的<mark>应用层直接使用网际层ICMP</mark>的一个例子,它并不使用运输层的TCP或UDP</li><li>PING应用所使用的ICMP报文类型为<mark>回送请求和回答</mark></li></ul></blockquote><h5 id="2-traceroute"><a href="#2-traceroute" class="headerlink" title="2. traceroute"></a>2. <code>traceroute</code></h5><blockquote><p>跟踪路由应用traceroute,<mark>用于探测IP数据报从源主机到达目的主机要经过哪些路由器</mark></p><p>在不同操作系统中,traceroute应用的命令和实现机制有所不同:</p><ul><li>在UNIX版本中,具体命令为<code>traceroute</code>,其<mark>在运输层使用UDP协议</mark>,<mark>在网络层使用ICMP报文类型只有差错报告报文</mark></li><li>在Windows版本中,具体命令为<code>tracert</code>,其<mark>应用层直接使用网际层的ICMP协议,所使用的ICMP报文类型有回送请求和回答报文以及差错报告报文</mark></li></ul><p>原理:设置IP数据报TTL为1,经过第一个路由器时,TTL减为0,IP数据报被丢弃,路由器向源主机发送超时差错报告报文,这样就知道了经过的第一个路由器的IP地址,以此类推依次设置TTL为2,3,4,……,直到接收到目的主机的回送请求回答报文</p></blockquote><h3 id="4-12-VPN"><a href="#4-12-VPN" class="headerlink" title="4.12 VPN"></a>4.12 <code>VPN</code></h3><blockquote><p><mark>虚拟专用网</mark>(<code>Virtual Private Network,VPN</code>):<strong>利用公用的<mark>因特网</mark>作为本机构各<mark>专用网之间的通信载体</mark>,这样形成的网络又称为虚拟专用网</strong></p></blockquote><p><strong>专用地址</strong>:</p><ul><li><code>10.0.0.0-10.255.255.255</code>(CIDR地址块10/8)</li><li><code>172.16.0.0-172.31.255.255</code>(CIDR地址块172.16/12)</li><li><code>192.168.0.0-192.168.255.255</code>(CIDR地址块192.168/16)</li></ul><blockquote><p>很显然,全世界可能有很多不同机构的专用网具有相同的专用IP地址,但这并不会引起麻烦,因为<mark>这些专用地址仅在机构内部使用</mark></p><p><strong>注意</strong>:在因特网中的所有路由器,<mark>对目的地址是专用地址的IP数据报一律不进行转发</mark>,这需要由因特网服务提供者ISP对其拥有的因特网路由器进行设置来实现(需要自己对路由器额外配置)</p></blockquote><p><img src="https://s2.loli.net/2024/11/04/BAhIUnlZ5sxk26H.png"></p><h3 id="4-12-NAT"><a href="#4-12-NAT" class="headerlink" title="4.12 NAT"></a>4.12 <code>NAT</code></h3><h4 id="1-概述"><a href="#1-概述" class="headerlink" title="1. 概述"></a>1. 概述</h4><blockquote><ul><li><p><strong>背景</strong>:尽管因特网采用了无分类编址方法来减缓IPv4地址空间耗尽的速度,但由于<mark>因特网用户数量的急剧增长</mark>,特别是<mark>大量小型办公室</mark>和<mark>家庭网络</mark>接入因特网的需求不断增加,I<mark>Pv4地址空间即将耗尽的危险然仍没有解除</mark>(实际上,因特网号码分配管理局IANN于2011年2月3日宣布,IPv4地址已经分配完毕)</p></li><li><p>**<mark>网络地址转换</mark>**(<code>Network Address Translation,NAT</code>)技术于1994年被提出,<mark>用来缓解IPv4地址空间即将耗尽的问题</mark></p><ul><li>NAT能<mark>使大量使用内部专用地址的专用网络用户共享少量外部全球地址来访问因特网上的主机和资源</mark></li><li>这种方法需要在专用网络连接到因特网的路由器上<mark>安装NAT软件</mark>。装有NAT软件的路由器称为<mark>NAT路由器</mark>,它<mark>至少要有一个有效的外部全球地址IPG</mark>。这样,所有使用内部专用地址的主机在和外部因特网通信时,都要<mark>在NAT路由器上将其内部专用地址转换成IPG</mark></li></ul></li></ul></blockquote><p><img src="https://clb.pages.dev/img/pics/image-20231120210441321.png"></p><h4 id="2-NAPT"><a href="#2-NAPT" class="headerlink" title="2. NAPT"></a>2. <code>NAPT</code></h4><blockquote><p><strong><mark>网络地址与端口号转换方法</mark></strong>:将NAT和运输层端口号结合使用,称为网络地址与端口号转换(<code>Network Address and Port Translation,NAPT</code>)</p><ul><li>由于目前绝大多数基于TCP/IP协议栈的网络应用,都使用运输层的传输控制协议TCP或用户数据报协议UDP,<mark>为了更加有效地利用NAT路由器中的全球IP地址</mark>,现在常<mark>将NAT转换和运输层端口号结合使用</mark><ul><li>这样就可以使内部专用网中使用专用地址的<mark>大量主机,共用NAT路由器上的1个全球IP地址</mark>,因而可以同时与因特网中的不同主机进行通信</li><li>现在很多家用路由器将家中各种智能设备(手机、平板、笔记本电脑、台式电脑、物联网设备等)接入因特网,这种路由器实际上就是一个<mark>NAPT路由器</mark>,但往往并不运行路由选择协议</li></ul></li></ul></blockquote><p><img src="https://clb.pages.dev/img/pics/image-20231120211722902.png"></p><blockquote><p><mark>由于NAT对外网屏蔽了内网主机的网络地址,能为内网的主机提供一定的安全保护</mark></p></blockquote><h3 id="5-思维导图和习题"><a href="#5-思维导图和习题" class="headerlink" title="5. 思维导图和习题"></a>5. 思维导图和习题</h3><p><a href="https://www.kdocs.cn/view/l/ceLxGZ0br8Gy">第4章 网络层(思维导图)-1 (kdocs.cn)</a></p><p><a href="https://www.kdocs.cn/view/l/cu2tek1gzIPq">第4章 网络层(思维导图)-2 (kdocs.cn)</a></p><p><a href="https://www.kdocs.cn/view/l/cjXVALCjxr0X">第4章 网络层(思维导图)-3 (kdocs.cn)</a></p><p><a href="https://www.kdocs.cn/view/l/cbSlBNdR8CSr">第4章 网络层(思维导图)-4 (kdocs.cn)</a></p><p><a href="https://www.kdocs.cn/view/l/ccu9ATsOpoZf">第4章 网络层 习题(第1部分)</a></p><p><a href="https://www.kdocs.cn/view/l/cqBldcygudFg">第4章 网络层 习题(第2部分)</a></p><hr><h2 id="5-运输层"><a href="#5-运输层" class="headerlink" title="5. 运输层"></a>5. 运输层</h2><h3 id="1-概述-1"><a href="#1-概述-1" class="headerlink" title="1. 概述"></a>1. 概述</h3><ul><li>第2~4章依次介绍了计算机网络体系结构中的<mark>物理层</mark>、<mark>数据链路层</mark>和<mark>网络层</mark>,它们共同解决了将主机通过异构网络互联起来所面临的问题,实现了<mark>主机到主机的通信</mark></li><li>然而在计算机网络中实际进行<mark>通信的真正实体,是位于通信两端主机中的进程</mark></li><li>如何<mark><strong>为运行在不同主机上的应用进程提供直接的逻辑通信服务</strong></mark>,就是<mark>运输层的主要任务</mark>,运输层协议又称为端到端协议</li></ul><p><img src="https://clb.pages.dev/img/pics/image-20231124190055469.png"></p><h3 id="2-端口号,复用和分用"><a href="#2-端口号,复用和分用" class="headerlink" title="2. 端口号,复用和分用"></a>2. 端口号,复用和分用</h3><h4 id="1-运输层端口号"><a href="#1-运输层端口号" class="headerlink" title="1. 运输层端口号"></a>1. 运输层端口号</h4><ul><li>运行在计算机上的进程是使用<mark>进程标识符</mark>(<code>P</code>rocess <code>ld</code>entification,<code>PID</code>)来标识的。<ul><li>然而,因特网上的计算机并不是使用统一的操作系统,而<mark>不同操作系统</mark>(Windows、Linux、.MacOS)<br>又<mark>使用不同格式的进程标识符</mark></li><li>为了使运行不同操作系统的计算机的应用进程之间能够基于网络进行通信,就必须<mark>使用统一的方法<br>对TCPP体系的应用进程进行标识</mark></li></ul></li><li><code>TCP/IP</code>体系结构的运输层使用端口号来标识和区分应用层的不同应用进程。端口号的<mark>长度为6比特,取<br>值范围是0~65535</mark></li></ul><img src="https://clb.pages.dev/img/pics/image-20231124191009578.png" style="zoom:100%;" /><blockquote><p><mark><strong>端口号只具有本地意义,即端口号只是为了标识本计算机网络协议栈应用层中的各应用进程。在因特网中,不同计算机中的相同端口号是没有关系的,即相互独立。另外,<code>TCP</code>和<code>UDP</code>端口号之间也是没有关系的</strong></mark></p></blockquote><h4 id="2-发送方的复用和接收方的分用"><a href="#2-发送方的复用和接收方的分用" class="headerlink" title="2. 发送方的复用和接收方的分用"></a>2. 发送方的复用和接收方的分用</h4><ol><li><strong>复用(Multiplexing):</strong><ul><li><strong>定义:</strong> 复用是指<mark>将多个应用程序的数据流合并到一个共享的通信通道上</mark></li><li><strong>TCP中的复用:</strong> 在TCP中,复用通过源端口号来实现。TCP连接的两端使用IP地址和端口号来唯一标识。源端口号表示发送端的应用程序,目的端口号表示接收端的应用程序。这样,在单个TCP连接中,多个应用程序的数据可以共享同一个物理通信通道</li><li><strong>UDP中的复用:</strong> 在UDP中,复用同样通过源端口号来实现。UDP报文的源端口号用于标识发送端的应用程序,目的端口号用于标识接收端的应用程序</li></ul></li><li><strong>分用(Demultiplexing):</strong><ul><li><strong>定义:</strong> 分用是指<mark>根据数据流中的标识信息将合并的数据流分发给正确的应用程序</mark></li><li><strong>TCP中的分用:</strong> 在TCP中,分用通过目的端口号来实现。接收端根据目的端口号将接收到的数据分发给相应的应用程序。这样,TCP层能够将数据正确地传递给目标应用程序</li><li><strong>UDP中的分用:</strong> 在UDP中,同样通过目的端口号来实现分用。接收端通过目的端口号确定应该将数据交付给哪个应用程序</li></ul></li></ol><p><img src="https://clb.pages.dev/img/pics/image-20231124192346522.png"></p><blockquote><p><mark>常见协议的分类</mark></p></blockquote><p><img src="https://clb.pages.dev/img/pics/image-20231124192909256.png"></p><p><strong><a href='https://www.bilibili.com/video/BV1c4411d7jb?t=353.6&p=58'>运输层端口号应用举例</a></strong></p><h3 id="3-TCP和UDP的对比"><a href="#3-TCP和UDP的对比" class="headerlink" title="3. TCP和UDP的对比"></a>3. <code>TCP</code>和<code>UDP</code>的对比</h3><p><img src="https://clb.pages.dev/img/pics/image-20231124195001220.png"></p><p>**<mark>注意</mark>**:</p><ul><li>TCP面向连接是<mark>逻辑连接,并非真实物理连接</mark></li><li><mark>TCP面向字节流,UDP面向应用报文</mark>(只是给数据报添加一个UDP首部)</li><li>TCP只支持单播,UDP支持单播、多播和广播</li><li><mark>TCP提供可靠服务,UDP提供不可靠服务</mark></li></ul><h3 id="4-TCP的流量控制"><a href="#4-TCP的流量控制" class="headerlink" title="4. TCP的流量控制"></a>4. TCP的<strong>流量控制</strong></h3><h4 id="1-概述-2"><a href="#1-概述-2" class="headerlink" title="1. 概述"></a>1. 概述</h4><blockquote><p>TCP为应用程序提供了<mark>流量控制</mark>(<code>Flow Control</code>)机制,以解决因<mark>发送方发送数据太快而导致接收方来不及接收,造成接收方的接收缓存溢出</mark>的问题</p><p><strong>流量控制的基本方法:</strong><mark>接收方根据自己的接收能力(接收缓存的可用空间大小)控制发送方的发送速率</mark></p></blockquote><h4 id="2-流量控制方法"><a href="#2-流量控制方法" class="headerlink" title="2. 流量控制方法"></a>2. <a href='https://www.bilibili.com/video/BV1c4411d7jb?t=44.1&p=60'>流量控制方法</a></h4><ol><li><strong>流程</strong></li></ol><p><img src="https://clb.pages.dev/img/pics/image-20231124203621190.png"></p><p><img src="https://clb.pages.dev/img/pics/image-20231124205122410.png"></p><ol start="2"><li><p>例题</p><p><img src="https://clb.pages.dev/img/pics/image-20231124210146804.png"></p></li></ol><h3 id="5-TCP的拥塞-se-控制"><a href="#5-TCP的拥塞-se-控制" class="headerlink" title="5. TCP的拥塞(se)控制"></a>5. TCP的<strong>拥塞(se)控制</strong></h3><h4 id="1-基本概念-3"><a href="#1-基本概念-3" class="headerlink" title="1. 基本概念"></a>1. 基本概念</h4><img src="https://clb.pages.dev/img/pics/image-20231124211432668.png" /><h4 id="2-4种拥塞控制方法"><a href="#2-4种拥塞控制方法" class="headerlink" title="2. 4种拥塞控制方法"></a>2. 4种拥塞控制方法</h4><p><img src="https://clb.pages.dev/img/pics/image-20231124212653543.png"></p><h5 id="1-慢开始、拥塞避免"><a href="#1-慢开始、拥塞避免" class="headerlink" title="1. 慢开始、拥塞避免"></a><mark>1. 慢开始、拥塞避免</mark></h5><p><img src="https://clb.pages.dev/img/pics/image-20231124213905436.png"></p><h5 id="2-快重传、快恢复"><a href="#2-快重传、快恢复" class="headerlink" title="2. 快重传、快恢复"></a><mark>2. 快重传、快恢复</mark></h5><blockquote><p>快重传算法和快恢复算法(<mark>改进TCP性能</mark>,1990年Reno版本)</p></blockquote><ol><li>问题:只是个别报文丢失,没有出现拥塞,这种情况下还是会将拥塞窗口设置为1,<strong>降低了网络利用率</strong></li></ol><p><img src="https://clb.pages.dev/img/pics/image-20231124214535400.png"></p><ol start="2"><li><p><strong><mark>快重传</mark></strong></p><ul><li><strong>采用快重传算法可以让发送方尽早知道发生了<mark>个别TCP报文段的丢失</mark></strong></li><li>“快重传”是指<strong>使发送方尽快(尽早)进行重传,而不是等重传计时器超时再重传</strong><ul><li>这就要求<mark>接收方不要</mark>等待自己发送数据时才进行<mark>捎带确认</mark>,而是<mark>要立即发送确认</mark>,即使收到了<mark>失序的报文段</mark>也要<mark>立即</mark>发出对已收到的报文段的<mark>重复确认</mark></li><li><mark>发送方</mark>一旦<mark>收到3个连续的重复确认</mark>,就将相应的报文段<mark>立即重传</mark>,而不是等该报文段的重传计时器超时再重传</li></ul></li></ul><p><img src="https://clb.pages.dev/img/pics/image-20231124215953819.png"></p></li><li><p><mark><strong>快恢复</strong></mark></p></li></ol><blockquote><p>与快重传算法配合使用的是<strong>快恢复算法</strong>,<mark>发送方</mark>一旦<mark>收到3个重复确认</mark>,就知道现在<mark>只是丢失了个别的报文段</mark>,于是<mark>不启动慢开始算法,<strong>而是执行快恢复算法</strong></mark></p><ul><li><strong>快恢复算法:</strong><mark>发送方将慢开始门限ssthresh的值和拥塞窗口cwnd的值<strong>都调整为当前cwnd值的一半</strong>,并开始执行拥塞避免算法</mark></li><li>也有的快恢复实现是把快恢复开始时的cwnd值再增大一些,即cwnd=新ssthresh+3</li></ul></blockquote><p><img src="https://clb.pages.dev/img/pics/image-20231125205520540.png"></p><h3 id="6-TCP超时重传时间的选择"><a href="#6-TCP超时重传时间的选择" class="headerlink" title="6. TCP超时重传时间的选择"></a>6. TCP超时重传时间的选择</h3><blockquote><p><mark>TCP超时重传时间RTO的选择</mark>是TCP<mark>最复杂</mark>的问题之一</p><p>问题:</p><ul><li><mark>超时重传时间设置过小</mark>,在确认报文段发送给接收方的过程中,发送方重传数据报文,<mark>增大了网络负荷</mark></li><li><mark>超时重传时间设置过大</mark>,需要重传数据报文时,推迟时间太长,<mark>网络空闲时间大,降低了传输效率</mark></li><li>超时重传时间<code>RTO</code>应略大于往返时间<code>RTT</code></li></ul></blockquote><h4 id="RTO的选择"><a href="#RTO的选择" class="headerlink" title="RTO的选择"></a><strong><mark>RTO的选择</mark></strong></h4><h5 id="1-RTTs的计算"><a href="#1-RTTs的计算" class="headerlink" title="1. RTTs的计算"></a>1. RTTs的计算</h5><p><img src="https://clb.pages.dev/img/pics/image-20231125211946213.png"></p><h5 id="2-RRTd和RTO的计算"><a href="#2-RRTd和RTO的计算" class="headerlink" title="2. RRTd和RTO的计算"></a>2. RRTd和RTO的计算</h5><p><img src="https://clb.pages.dev/img/pics/image-20231125212210328.png"></p><h4 id="发生超时重传时无法测准RTT"><a href="#发生超时重传时无法测准RTT" class="headerlink" title="发生超时重传时无法测准RTT"></a>发生超时重传时无法测准RTT</h4><p><img src="https://clb.pages.dev/img/pics/image-20231125212838059.png"></p><blockquote><p><strong>通过上述两个例子可以看出:当发送方出现超时重传后,收到确认报文段时是无法判断出该确认到底是对原数据报文段的确认还是对重传数据报文段的确认,也就是无法准确测量出RTT,进而无法正确计算RTO</strong></p></blockquote><h5 id="Karn算法及修正"><a href="#Karn算法及修正" class="headerlink" title="Karn算法及修正"></a>Karn算法及修正</h5><p><img src="https://clb.pages.dev/img/pics/image-20231125213341384.png"></p><h5 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h5><p><img src="https://clb.pages.dev/img/pics/image-20231125213649193.png"></p><h3 id="7-TCP可靠传输的实现"><a href="#7-TCP可靠传输的实现" class="headerlink" title="7. TCP可靠传输的实现"></a>7. TCP可靠传输的实现</h3><ul><li><strong>TCP的窗口以字节为单位</strong></li></ul><p><img src="https://clb.pages.dev/img/pics/image-20231126212908308.png"></p><ul><li>发送方<ul><li>发送窗口内的已发送数据如果迟迟未收到确认,会发生<mark>超时重传</mark></li><li>只有处于发送窗口内的数据才能发送</li></ul></li></ul><p><img src="https://clb.pages.dev/img/pics/image-20231126213314315.png"></p><ul><li>接收方<ul><li>接收方<mark><strong>只能对按序收到的数据中的最高序号给出累计确认</strong></mark>,3次重复确认会导致发送方<mark><strong>快重传</strong></mark></li><li><strong>序号落入接收窗口内的数据是允许接收的数据</strong></li></ul></li></ul><p><img src="https://clb.pages.dev/img/pics/image-20231126213800870.png"></p><ul><li><strong><mark>总结</mark></strong></li></ul><p><img src="https://clb.pages.dev/img/pics/image-20231126214833403.png"></p><h3 id="8-TCP的运输连接管理"><a href="#8-TCP的运输连接管理" class="headerlink" title="8. TCP的运输连接管理"></a>8. TCP的运输连接管理</h3><h4 id="1-TCP连接的建立"><a href="#1-TCP连接的建立" class="headerlink" title="1. TCP连接的建立"></a>1. TCP连接的建立</h4><p><img src="https://clb.pages.dev/img/pics/image-20231203192027726.png"></p><blockquote><p>TCP双方连接的建立要解决的三个问题</p></blockquote><p><img src="https://clb.pages.dev/img/pics/image-20231203192339624.png"></p><h4 id="2-三报文握手"><a href="#2-三报文握手" class="headerlink" title="2. 三报文握手"></a>2. 三报文握手</h4><p><img src="https://s2.loli.net/2024/10/20/iBJuZkFgIDYlWU1.png"></p><blockquote><p><strong>思考:第三次确认是否多余,<mark>能不能两报文握手?</mark></strong></p><p>答案:不能,如下图所示</p></blockquote><p><img src="https://clb.pages.dev/img/pics/image-20231203194753830.png"></p><h4 id="3-四报文挥手"><a href="#3-四报文挥手" class="headerlink" title="3. 四报文挥手"></a>3. 四报文挥手</h4><blockquote><p><strong>思考:为什么客户端发送完最后一个确认报文段后不立刻关闭而是等待2个MSL时间后才关闭?</strong></p><p>答案:如图所示</p></blockquote><p><img src="https://s2.loli.net/2024/11/04/hXY48jEWu1OeUnJ.png" alt="image-20241104141359755"></p><blockquote><p><mark>TCP保活计时器的作用</mark></p></blockquote><p><img src="https://clb.pages.dev/img/pics/image-20231203201004403.png"></p><h3 id="9-TCP、UDP报文段首部格式对比"><a href="#9-TCP、UDP报文段首部格式对比" class="headerlink" title="9. TCP、UDP报文段首部格式对比"></a>9. TCP、UDP报文段首部格式对比</h3><blockquote><p><a href="https://www.kdocs.cn/view/l/cgoja6Lpohhj">参阅思维导图1</a></p></blockquote><p><img src="https://s2.loli.net/2024/10/20/vSiA1tlLHWqjOho.png" alt="img"></p><h3 id="10-UDP的校验"><a href="#10-UDP的校验" class="headerlink" title="10. UDP的校验"></a>10. UDP的校验</h3><blockquote><p>[!IMPORTANT]</p><p><strong>伪首部只是计算校验和的时候添加的,计算完后会拆除,不参与运输!</strong></p></blockquote><p><img src="https://s2.loli.net/2024/11/26/sbJ8Hnc3BuyQZrk.png" alt="UDP校验"></p><h3 id="11-思维导图和习题"><a href="#11-思维导图和习题" class="headerlink" title="11. 思维导图和习题"></a>11. 思维导图和习题</h3><p><a href="https://www.kdocs.cn/view/l/cgoja6Lpohhj">第5章 运输层(思维导图)-1 (kdocs.cn)</a></p><p><a href="https://www.kdocs.cn/view/l/chkSS9vn9Zgd">第5章 运输层(思维导图)-2 (kdocs.cn)</a></p><p><a href="https://www.kdocs.cn/view/l/cpaRQtp0G6Ou">第5章 运输层 习题 (kdocs.cn)</a></p><hr><h2 id="6-应用层"><a href="#6-应用层" class="headerlink" title="6. 应用层"></a>6. 应用层</h2><h3 id="1-概述-3"><a href="#1-概述-3" class="headerlink" title="1. 概述"></a>1. 概述</h3><ul><li>应用层是计算机网络体系结构的<mark><strong>最顶层</strong></mark>,是<mark><strong>设计和建立计算机网络的最终目的</strong></mark>,也是计算机网络中发展最快的部分<ul><li>早期基于文本的应用(电子邮件、远程登录、文件传输、新闻组)</li><li>20世纪90年代将因特网带入千家万户的万维网WWW</li><li>当今流行的即时通信、P2P文件共享及各种音视频应用</li><li>计算设备的小型化和“无处不在”,宽带住宅接入和无线接入的日益普及和迅速发展,为未来更多的新型应用提供了广阔的舞台</li></ul></li></ul><h3 id="2-客户服务器和对等方式"><a href="#2-客户服务器和对等方式" class="headerlink" title="2. 客户服务器和对等方式"></a>2. 客户服务器和对等方式</h3><h4 id="1-C-S方式"><a href="#1-C-S方式" class="headerlink" title="1. C/S方式"></a>1. <code>C/S</code>方式</h4><p><img src="https://clb.pages.dev/img/pics/image-20231203210526922.png"></p><p><img src="https://clb.pages.dev/img/pics/image-20231203210406504.png"></p><h4 id="2-P2P方式"><a href="#2-P2P方式" class="headerlink" title="2. P2P方式"></a>2. <code>P2P</code>方式</h4><p><img src="https://clb.pages.dev/img/pics/image-20231203210957388.png"></p><p><img src="https://clb.pages.dev/img/pics/image-20231203210908603.png"></p><h3 id="3-DHCP"><a href="#3-DHCP" class="headerlink" title="3. DHCP"></a>3. <code>DHCP</code></h3><h4 id="1-概述-4"><a href="#1-概述-4" class="headerlink" title="1. 概述"></a>1. 概述</h4><blockquote><p>动态主机配置协议(<code>D</code>ynamic <code>h</code>ost <code>c</code>onfiguration <code>p</code>rotocol)</p><p>作用:</p><ul><li><code>DHCP</code>可为计算机<mark>自动配置网络参数</mark>,包括:<ul><li>IP地址</li><li>子网掩码</li><li>默认网关</li><li>DNS服务器</li></ul></li></ul></blockquote><h4 id="2-DHCP基本工作过程"><a href="#2-DHCP基本工作过程" class="headerlink" title="2. DHCP基本工作过程"></a>2. <code>DHCP</code>基本工作过程</h4><p><img src="https://clb.pages.dev/img/pics/image-20231203212822735.png"></p><h4 id="3-DHCP中继代理"><a href="#3-DHCP中继代理" class="headerlink" title="3. DHCP中继代理"></a>3. <code>DHCP</code>中继代理</h4><p><img src="https://clb.pages.dev/img/pics/image-20231203213528827.png"></p><h3 id="4-DNS"><a href="#4-DNS" class="headerlink" title="4. DNS"></a>4. <code>DNS</code></h3><blockquote><p><strong>域名系统</strong>(<code>D</code>omain <code>N</code>ame <code>S</code>ystem,<code>DNS</code>)</p></blockquote><h4 id="1-域名系统的作用"><a href="#1-域名系统的作用" class="headerlink" title="1. 域名系统的作用"></a>1. 域名系统的作用</h4><p><img src="https://clb.pages.dev/img/pics/image-20231204215706663.png"></p><p><img src="https://clb.pages.dev/img/pics/image-20231204215946064.png"></p><h4 id="2-因特网的域名结构"><a href="#2-因特网的域名结构" class="headerlink" title="2. 因特网的域名结构"></a>2. 因特网的域名结构</h4><blockquote><p>因特网采用**<mark>层次树状结构的域名结构</mark>**</p></blockquote><h5 id="1-域名规范"><a href="#1-域名规范" class="headerlink" title="1. 域名规范"></a>1. 域名规范</h5><p><img src="https://clb.pages.dev/img/pics/image-20231204220316573.png"></p><h5 id="2-不同国家的域名"><a href="#2-不同国家的域名" class="headerlink" title="2. 不同国家的域名"></a>2. 不同国家的域名</h5><p><img src="https://clb.pages.dev/img/pics/image-20231204220844413.png"></p><h5 id="3-因特网的域名空间"><a href="#3-因特网的域名空间" class="headerlink" title="3. 因特网的域名空间"></a>3. 因特网的域名空间</h5><p><img src="https://clb.pages.dev/img/pics/image-20231204221139510.png"></p><h4 id="3-域名服务器"><a href="#3-域名服务器" class="headerlink" title="3. 域名服务器"></a>3. 域名服务器</h4><p><img src="https://clb.pages.dev/img/pics/image-20231204221502337.png"></p><h4 id="4-域名解析"><a href="#4-域名解析" class="headerlink" title="4. 域名解析"></a>4. 域名解析</h4><h5 id="1-递归查询"><a href="#1-递归查询" class="headerlink" title="1. 递归查询"></a>1. 递归查询</h5><p><img src="https://clb.pages.dev/img/pics/image-20231204221840693.png"></p><h5 id="2-迭代查询"><a href="#2-迭代查询" class="headerlink" title="2. 迭代查询"></a>2. 迭代查询</h5><p><img src="https://clb.pages.dev/img/pics/image-20231204222346748.png"></p><h5 id="3-高速缓存"><a href="#3-高速缓存" class="headerlink" title="3. 高速缓存"></a>3. 高速缓存</h5><p><img src="https://clb.pages.dev/img/pics/image-20231204222807218.png"></p><p><img src="https://clb.pages.dev/img/pics/image-20231204222817296.png"></p><h3 id="5-FTP"><a href="#5-FTP" class="headerlink" title="5. FTP"></a>5. <code>FTP</code></h3><blockquote><p><strong>文件传送协议</strong>(<code>F</code>ile <code>T</code>ransfer <code>P</code>rotocol,<code>FTP</code>)</p></blockquote><p><img src="https://clb.pages.dev/img/pics/image-20231204223526085.png"></p><blockquote><p><strong>基本工作原理</strong></p></blockquote><ol><li>主动模式</li></ol><p><img src="https://clb.pages.dev/img/pics/image-20231204224108197.png"></p><ol start="2"><li>被动模式</li></ol><p><img src="https://clb.pages.dev/img/pics/image-20231204224211222.png"></p><h3 id="6-电子邮件"><a href="#6-电子邮件" class="headerlink" title="6. 电子邮件"></a>6. 电子邮件</h3><h4 id="1-E-mail"><a href="#1-E-mail" class="headerlink" title="1. E-mail"></a>1. <code>E-mail</code></h4><p><img src="https://clb.pages.dev/img/pics/image-20231205090041032.png"></p><h4 id="2-电子邮件系统的组成"><a href="#2-电子邮件系统的组成" class="headerlink" title="2. 电子邮件系统的组成"></a>2. 电子邮件系统的组成</h4><p><img src="https://clb.pages.dev/img/pics/image-20231205090359331.png"></p><h4 id="3-邮件发送和接收过程"><a href="#3-邮件发送和接收过程" class="headerlink" title="3. 邮件发送和接收过程"></a>3. 邮件发送和接收过程</h4><p><img src="https://clb.pages.dev/img/pics/image-20231205090654917.png"></p><h4 id="4-SMTP"><a href="#4-SMTP" class="headerlink" title="4. SMTP"></a>4. <code>SMTP</code></h4><blockquote><p>简单邮件传送协议(Simple Mail Transfer Protocol,<code>SMTP</code>)的基本工作过程</p></blockquote><p><img src="https://clb.pages.dev/img/pics/image-20231205091401858.png"></p><h4 id="5-电子邮件的信息格式"><a href="#5-电子邮件的信息格式" class="headerlink" title="5. 电子邮件的信息格式"></a>5. 电子邮件的信息格式</h4><p><img src="https://clb.pages.dev/img/pics/image-20231205091646693.png"></p><h4 id="6-MIME"><a href="#6-MIME" class="headerlink" title="6. MIME"></a>6. <code>MIME</code></h4><blockquote><p><strong>多用途因特网邮件扩展</strong> (Multipurpose Internet Mail Extensions,<code>MIME</code>)</p></blockquote><p><img src="https://clb.pages.dev/img/pics/image-20231205091958949.png"></p><blockquote><p><strong><code>MIME</code>对邮件内容中的非ASCII字符转换成ASCII字符</strong></p></blockquote><p><img src="https://clb.pages.dev/img/pics/image-20231205092205673.png"></p><h4 id="7-常用邮件读取协议"><a href="#7-常用邮件读取协议" class="headerlink" title="7. 常用邮件读取协议"></a>7. 常用邮件读取协议</h4><blockquote><p><strong>邮局协议</strong>(Post Office Protocol,<code>POP</code>)</p><p><strong>因特网邮件访问协议</strong>(Internet Message Access Protocol,<code>IMAP</code>)</p></blockquote><p><img src="https://clb.pages.dev/img/pics/image-20231205092629754.png"></p><h4 id="8-基于万维网的电子邮件"><a href="#8-基于万维网的电子邮件" class="headerlink" title="8. 基于万维网的电子邮件"></a>8. 基于万维网的电子邮件</h4><p><img src="https://clb.pages.dev/img/pics/image-20231205093140031.png"></p><h3 id="7-万维网"><a href="#7-万维网" class="headerlink" title="7. 万维网"></a>7. 万维网</h3><h4 id="1-概述-5"><a href="#1-概述-5" class="headerlink" title="1. 概述"></a>1. 概述</h4><blockquote><ul><li>**<mark>万维网</mark>**(World Wide Web,<code>WWW</code>)<mark>并非某种特殊的计算机网络</mark>。它是一个大规模的、联机式的信息储藏所,是<mark>运行在因特网上的一个分布式应用</mark></li><li>万维网利用网页之间的<mark>超链接</mark>将不同网站的网页链接成一张逻辑上的信息网</li><li>万维网是欧洲粒子物理实验室的<mark>Tim Berners-Lee</mark>最初于1989年3月提出的</li></ul></blockquote><h4 id="2-URL"><a href="#2-URL" class="headerlink" title="2. URL"></a>2. <code>URL</code></h4><blockquote><p><mark>统一资源定位符</mark>(<code>U</code>niform <code>R</code>esource <code>L</code>ocator)</p></blockquote><p><img src="https://clb.pages.dev/img/pics/image-20231205094301978.png" alt="image-20231205094301978"></p><h4 id="3-万维网文档"><a href="#3-万维网文档" class="headerlink" title="3. 万维网文档"></a>3. 万维网文档</h4><p><img src="https://clb.pages.dev/img/pics/image-20231205094813054.png"></p><h4 id="4-HTTP"><a href="#4-HTTP" class="headerlink" title="4. HTTP"></a>4. <code>HTTP</code></h4><h5 id="1-概述-6"><a href="#1-概述-6" class="headerlink" title="1. 概述"></a>1. 概述</h5><blockquote><p><strong><mark>超文本传输协议</mark></strong><code>HTTP</code>(HyperText Transfer Protocol)</p><p>HTTP定义了<strong>浏览器(即万维网客户进程)怎样向万维网服务器请求万维网文档,以及万维网服务器怎样把万维网文档传送给浏览器</strong></p></blockquote><p><img src="https://clb.pages.dev/img/pics/image-20231205095412658.png"></p><h5 id="2-报文格式"><a href="#2-报文格式" class="headerlink" title="2. 报文格式"></a>2. 报文格式</h5><ul><li><mark>请求报文格式</mark></li></ul><p><img src="https://clb.pages.dev/img/pics/image-20231205095830137.png"></p><ul><li><mark>响应报文格式</mark></li></ul><p><img src="https://clb.pages.dev/img/pics/image-20231205100113338.png"></p><h4 id="5-Cookie"><a href="#5-Cookie" class="headerlink" title="5. Cookie"></a>5. <code>Cookie</code></h4><ul><li>早期的万维网应用非常简单,仅仅是用户查看存放在不同服务器上的各种静态的文档。因此HTTP被设计为一种<mark>无状态</mark>的协议。这样可以简化服务器的设计</li><li>现在,用户可以通过万维网进行各种复杂的应用,如网上购物、电子商务等。这些应用往往需要万维网服务器能够识别用户</li><li>Cookie提供了一种机制使得万维网服务器能够“记住”用户,而无需用户主动提供用户标识信息。也就是说,<mark>Cookie是一种对无状态的HTTP进行状态化的技术</mark></li></ul><p><img src="https://clb.pages.dev/img/pics/image-20231205103323858.png"></p><h4 id="6-万维网缓存与代理服务器"><a href="#6-万维网缓存与代理服务器" class="headerlink" title="6. 万维网缓存与代理服务器"></a>6. 万维网缓存与代理服务器</h4><p><img src="https://clb.pages.dev/img/pics/image-20231205103638305.png"></p><h4 id="7-思维导图和相关习题"><a href="#7-思维导图和相关习题" class="headerlink" title="7. 思维导图和相关习题"></a>7. 思维导图和相关习题</h4><p><a href="https://www.kdocs.cn/view/l/cle5dxMBHZIH">第6章 应用层(思维导图)-1 (kdocs.cn)</a></p><p><a href="https://www.kdocs.cn/view/l/cabHsokbKiNO">第6章 应用层(思维导图)-2 (kdocs.cn)</a></p><p><a href="https://www.kdocs.cn/view/l/cv9vfzDopPvi">第6章 应用层 习题 (kdocs.cn)</a></p><hr><h2 id="7-计算机网络相关术语"><a href="#7-计算机网络相关术语" class="headerlink" title="7. 计算机网络相关术语"></a>7. <em>计算机网络相关术语</em></h2><p><strong>ACK</strong> (Acknowledgement) 确认</p><p><strong>ADSL</strong> (Asymmetric Digital Subscriber Line) 非对称数字用户线</p><p><strong>AP</strong> (Access Point) 接入点</p><p><strong>AP</strong> (Application) 应用程序</p><p><strong>API</strong> (Application Programming Interface) 应用编程接口</p><p><strong>APNIC</strong> (Asia Pacific Network Information Centre) 亚太网络信息中心</p><p><strong>ARIN</strong> (American Registry for Internet Numbers) 美国因特网号码注册机构</p><p><strong><mark>ARP (Address Resolution Protocol) 地址解析协议</mark></strong></p><p><strong>ARPA</strong> (Advanced Research Projects Agency) 美国国防部远景研究规划局 (高级研究计划署) </p><p><strong>ARQ</strong> (Automatic Repeat-request) 自动重传请求</p><p><strong>AS</strong> (autonomous system) 自制系统</p><p><mark><strong>BGP</strong> (Border Gateway Protocol ) 边界网关协议</mark></p><p><strong>BOOTP</strong> (Bootstrap Protocol) 引导程序协议</p><p><strong>BSA</strong> (Basic Service Area) 基本服务区</p><p><strong>BT</strong> (Bit Torrent) 一种P2P程序</p><p><strong>CA</strong> (Certificate Authority) 认证中心</p><p><strong>CA</strong> (Collision Avoidance) 碰撞避免</p><p><strong>CATV</strong> ( Community Antenna Television) 有线电视</p><p><mark><strong>CDM</strong> (Code Division Multiplexing) 码分复用</mark></p><p><mark><strong>CDMA</strong> (Code Division Multiple Access) 码分多址</mark></p><p><strong>CGI</strong> (Common Gateway Interface) 通用网关接口</p><p><mark><strong>CIDR</strong> (Classless Inter-Domain Routing) 无分类域间路由选择</mark></p><p><mark><strong>CRC</strong> (Cyclic Redundancy Check) 循环冗余校验</mark></p><p><mark><strong>CSMA/CD</strong> (Carrier Sense Multiple Access/Collision Detect) 载波监听 多址接入/碰撞检测</mark></p><p><strong>CSMA/CA</strong> (Carrier Sense Multiple Access with Collision Avoidance) 载波监听多点接入/冲突避免</p><p><strong>CTS</strong> (Clear To Send) 允许发送</p><p><strong>DACS</strong> (Digital Access and Cross-connect System) 数字交接系统</p><p><strong><mark>DHCP (Dynamic Host Configuration Protocol) 动态主机配置协议</mark></strong></p><p><strong>DiffServ (Differentiated Services) 区分服务</strong></p><p><strong>DLCI</strong> (Data Link Connection Identifier) 数据链路连接标识符</p><p><strong>DMT</strong> (Discrete Multi-Tone) 离散多音 (调制) </p><p><mark><strong>DNS</strong> (Domain Name System) 域名系统</mark></p><p><strong>DoS</strong> (Denial of Service) 拒绝服务</p><p><strong>DS</strong> (Distribution System) 分配系统</p><p><strong>DSL</strong> (Digital Subscriber Line) 数字用户线</p><p><strong>DSLAM</strong> ( DSL Access Multiplexer) 数字用户线接入复用器</p><p><strong>DSSS</strong> (Direct Sequence Spread Spectrum) 直接序列扩频</p><p><strong>EFM</strong> (Ethernet in the First Mile ) 第一英里的以太网</p><p><strong>EFPHB</strong> (Expedited Forwarding Per-Hop Behavior) 迅速转发每跳行为</p><p><strong>EGP</strong> (Exterior Gateway Protocol) 外部网关协议</p><p><strong>EIA</strong> (Electronic Industries Association) 美国电子工业协会</p><p><strong>FCS</strong> (Frame Check Sequence) 帧检验序列</p><p><strong>FDDI</strong> (fiber-distributed data interface) 光纤分布式数据接口</p><p><mark><strong>FDM</strong> (Frequency-division multiplexing) 频分复用</mark></p><p><strong>FEC</strong> (Forwarding Equivalence Class) 转发等价类</p><p><strong>FFD</strong> (Full-Function Device) 全功能设备</p><p><strong>FHSS</strong> (Frequency-Hopping Spread Spectrum) 跳频扩频</p><p><strong>FIFO</strong> (First In First Out) 先进先出</p><p><strong>FQ</strong> (Fair Queuing) 公平排队</p><p><mark><strong>FTP (File Transfer Protocol) 文件传输协议</strong></mark></p><p><strong>GIF</strong> (Graphics System for Mobile) 全球移动通信系统,GSM体制</p><p><strong>HDLC</strong> ( High-Level Data Link Control) 高级数据链路控制</p><p><strong>HDSL</strong> (High-speed DSL) 高速数字用户线</p><p><strong>HSSG</strong> (High Speed Study Group) 高速研究组</p><p><strong>HTML</strong> (Hyper Text Markup Language) 超文本标记语言</p><p><strong><mark>HTTP (Hyper Text Transfer Protocol) 超文本传送协议</mark></strong></p><p><strong>IAB</strong> (Internet Architecture Board) 因特网体系结构委员</p><p><strong>IANA</strong> (Internet Assigned Numbers Authority) 因特网赋号管理局</p><p><strong><mark>ICMP (Internet Control Message Protocol) 网际控制报文协议</mark></strong></p><p><strong>IEEE</strong> (Institute of Electrical and Electronic Engineering) (美国) 电气和电子工程师学会</p><p><strong>IFS</strong> (Inter Frame Space) 帧间间隔</p><p><strong>IGMP</strong> (Internet Group Management Protocol) 网际组管理协议</p><p><strong>IGP</strong> (Interior Gateway Protocol) 内部网关协议</p><p><strong>IM</strong> (Instant Messaging) 及时传信</p><p><strong><mark>IMAP ( Internet Message Access Protocol) 因特网报文存取协议</mark></strong></p><p><strong>IntServ</strong> (Integrated Services) 综合服务</p><p><strong><mark>IP (Internet Protocol) 网际协议</mark></strong></p><p><strong>IPCP</strong> (IP Control Protocol) IP控制协议</p><p><strong>IPng</strong> (IP Next Protocol) 下一代IP</p><p><strong>IPsec</strong> (IP security) IP安全协议</p><p><strong>ISDN</strong> (Internet Services Digital Network) 综合业务数字网</p><p><strong>ISO</strong> (International Organization for Standardization ) 国际标准化组织</p><p><strong>ISOC</strong> (Internet Society) 因特网协会</p><p><strong>ISP</strong> (Internet Service Provider) 因特网服务提供者</p><p><mark><strong>LAN</strong> (Local Area Network) 局域网</mark></p><p><strong>LCP</strong> (Link Control Protocol) 链路控制协议</p><p><strong>LDP</strong> (Label Distribution Protocol) 标记分配协议</p><p><strong>LLC</strong>(Logical Link Control) 逻辑链路控制</p><p><strong>LSR</strong>(Label Switched Router) 标记交换路由器</p><p><strong>MAC</strong>(Medium Access Control) 媒体接入控制</p><p><strong>MACA</strong>(Multiple Access with Collision Avoidance)具有碰撞避免的多点接入</p><p><mark><strong>MAN</strong>(Metropolitan Area Network) 城域网</mark></p><p><strong>MBONE</strong>(Multicast Backbone On the InterNet) 多播主干网</p><p><strong>MCU</strong>(Multipoint Control Unit) 多点控制单元</p><p><strong>MD</strong>(Message Digest) 报文摘要</p><p><strong>MF</strong>(More Fragment) 还有分片</p><p><strong>MRU</strong>(Maximum Receive Unit) 最大接收单元</p><p><strong>MSS</strong>(Maximum Segment Size) 最长报文段</p><p><strong>MTU</strong>(Maximum Transfer Unit) 最大传送单元</p><p><strong>NAP</strong>(Network Access Point) 网络接入点</p><p><mark><strong>NAT</strong>(Network Address Translation) 网络地址转换</mark></p><p><strong>NAV</strong>(Network Allocation Vector) 网络分配向量</p><p><strong>NCP</strong>(Network Control Protocol) 网络控制协议</p><p><strong>NFS</strong>(Network File System) 网络文件系统</p><p><strong>NOC</strong>(Network Operations Center) 网络运行中心</p><p><strong>NSAP</strong>(Network Service Access Point) 网络层服务访问点</p><p><strong>NSF</strong>(National Service Foundation) (美国)国家科学基金会</p><p><strong>OFDM</strong>(Orthogonal Frequency Division Multiplexing) 正交频分复用</p><p><strong>OSI/RM</strong> (Open Systems Interconnection Reference Model) 开发系统互连基本参考模型</p><p><mark><strong>OSPF(Open Shortest Path First) 开放最短通路优先</strong></mark></p><p><mark><strong>P2P</strong>(Peer-to-Peer) 对等方式</mark></p><p><mark><strong>PAN</strong>(Personal Area Network) 个人区域网</mark></p><p><strong>PAP</strong>(Password Authentication Protocol) 口令鉴别协议</p><p><strong>PCA</strong>(Policy Certification Authority) 政策认证中心</p><p><strong>PCF</strong>(Point Coordination Function) 点协调功能</p><p><strong>PCM</strong>(Pulse Code Modulation) 脉码调制</p><p><strong>PEM</strong>(Privacy Enhanced Mail) 因特网的正式邮件加密标准</p><p><strong>PGP</strong>(Pretty Good Privacy) 一种电子邮件的机密标准</p><p><strong>PHB</strong>(Per-Hop Behavior) 每跳行为</p><p><strong>PIFS</strong>(Point Coordination Function IFS) 点协调功能帧间间隔</p><p><strong>PK</strong>(public key) 公钥,公开密钥</p><p><strong>PoP</strong>(Point of Presence) 汇接点</p><p><mark><strong>POP</strong>(Post Office Protocol) 邮局协议</mark></p><p><strong><mark>PPP(Point-to-Point Protocol) 点对点协议</mark></strong></p><p><strong>PPPoE</strong>(Point-to-Point Protocol over Ethernet) 以太网上的点对点协议</p><p><strong>QAM</strong>(Quadrature Amplitude Modulation) 正交幅度调制</p><p><strong>QoS</strong>(Quality of Service) 服务质量</p><p><strong>QPSK</strong>(Quarternary Phase Shift Keying)正交相移键控</p><p><strong>RARP</strong>(Reverse Address Resolution Protocol)逆地址解析协议</p><p><strong>RFD</strong>(Reduced-Function Device)精简功能设备</p><p><strong>RG</strong>(Research Group)研究组</p><p><strong><mark>RIP(Routing Information Protocol)路由信息协议</mark></strong></p><p><strong>RIPE</strong>(法文表示的European IP Network)欧洲的IP 网络</p><p><strong>RTP</strong>(Real-Time Transfer Protocol)实时传送协议</p><p><strong>RTSP</strong>(Real-Time Streaming Protocol)实时流式协议</p><p><mark><strong>RTT</strong>(Round-Trip Time)往返时间</mark></p><p><strong>SA</strong>(Security Association)安全关联</p><p><strong>SACK</strong>(Selective ACK)选择确认</p><p><strong>SAP</strong>(Service Access Point)服务访问点</p><p><strong>SCTP</strong>(Stream Control Transmission Protocol)流控制传输协议</p><p><strong>SDH</strong>(Synchronous Digital Hierarchy)同步数字系列</p><p><strong>SMI</strong>(Structure of Management Information)管理信息结构</p><p><strong><mark>SMTP(Simple Mail Transfer Protocol)简单邮件传送协议</mark></strong></p><p><strong>SNA</strong>(System Network Architecture)系统网络体系结构</p><p><strong>SNMP</strong>(Simple Network Management Protocol)简单网络管理协议</p><p><strong>SOH</strong>(Start Of Header)首部开始</p><p><strong>SONET</strong>(Synchronous Optical Network)同步光纤网</p><p><strong>STDM</strong>(Statistic TDM)统计时分复用</p><p><strong>STM</strong>(Synchronous Transfer Module)同步传递模块</p><p><strong>STS</strong>(Synchronous Transport Signal)同步传送信号</p><p><strong>TAG</strong>(TAG Switching)标记交换</p><p><strong>TCB</strong>(Transmission Control Block)传输控制程序块</p><p><strong><mark>TCP(Transmission Control Protocol)传输控制协议</mark></strong></p><p><mark><strong>TDM</strong>(Time Division Multiplexing)时分复用</mark></p><p><strong>TELNET</strong>(TELetype NETwork)电传机网络,一种因特网的应用程序</p><p><strong>TFTP</strong>(Trivial File Transfer Protocol)简单文件传送协议</p><p><strong>TIA</strong>(Telecommunications Industries Association)电信行业协会</p><p><strong>TLD</strong>(Top Level Domain)顶级域名</p><p><strong>TLI</strong>(Transport Layer Interface)运输层接口</p><p><strong>TLS</strong>(Transport Layer Security)运输层安全协议</p><p><strong>TLV</strong>(Type-Length-Value)类型-长度-值</p><p><strong>TPDU</strong>(Transport Protocol Data Unit)运输协议数据单元</p><p><strong><mark>UDP(User Datagram Protocol)用户数据报协议</mark></strong></p><p><mark><strong>URL</strong>(Uniform Resource Locator)统一资源定位符</mark></p><p><strong>UTP</strong>(Unshield Twisted Pair)无屏蔽双绞线</p><p><strong>UWB</strong>(Ultra-Wide Band)超宽带</p><p><strong>VC</strong>(Virtual Circuit)虚电路</p><p><strong>VDSL</strong>(Very high speed DSL)甚高数字用户线</p><p><mark><strong>VLAN</strong>(Virtual LAN)虚拟局域网</mark></p><p><mark><strong>VPN</strong>(Virtual Private Network) 虚拟专用网</mark></p><p><mark><strong>WAN</strong>(Wide Area Network) 广域网</mark></p><p><strong>WDM</strong>(Wavelength Division Multiplexing) 波分复用</p><p><strong>WG</strong>(Working Group) 工作组</p><p><strong>Wi-Fi</strong>(Wireless-Fidelity) 无线保真度(无限局域网的同义词)</p><p><strong>WLAN</strong>(Wireless Local Area Network) 无线局域网</p><p><strong>WMAN</strong>(Wireless Metrpolitan Area Network) 无线个城域网</p><p><strong>WPAN</strong>(wireless Personal Area Network) 无线个人区域网</p><p><strong>WSN</strong>(Wireless Sensor Network) 无线传感器网络</p><p><mark><strong>WWW</strong>(World Wide Web) 万维网</mark></p><h2 id="8-关于"><a href="#8-关于" class="headerlink" title="8.关于"></a>8.关于</h2><blockquote><p>[!tip]</p><p><a href="https://github.com/caolib/my-sites/blob/main/net/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C.md">markdown文件地址</a></p><p><a href="https://github.com/caolib/my-sites/issues">问题反馈</a></p></blockquote><p><img src="https://counter.seku.su/cmoe?name=clb&theme=r34"></p>]]></content>
<categories>
<category> 考研 </category>
</categories>
<tags>
<tag> 计算机网络 </tag>
</tags>
</entry>
<entry>
<title>mybatis</title>
<link href="/2023/10/14/mybatis/"/>
<url>/2023/10/14/mybatis/</url>
<content type="html"><![CDATA[<img src="https://mybatis.org/images/mybatis-logo.png"><h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><h3 id="什么是-MyBatis?"><a href="#什么是-MyBatis?" class="headerlink" title="什么是 MyBatis?"></a>什么是 MyBatis?</h3><blockquote><p>MyBatis 是一款优秀的==持久层框架==,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。</p></blockquote><hr><h3 id="准备工作"><a href="#准备工作" class="headerlink" title="准备工作"></a>准备工作</h3><ul><li><p>新建<code>maven</code>项目</p></li><li><p>在<code>pom.xml</code>文件中导入相关依赖,数据库依赖导入需要的就行</p></li></ul><figure class="highlight xml"><table><tr><td class="code"><pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">dependencies</span>></span><br> <span class="hljs-comment"><!--mybatis依赖--></span><br> <span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.mybatis<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>mybatis<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">version</span>></span>3.5.13<span class="hljs-tag"></<span class="hljs-name">version</span>></span><br> <span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br> <span class="hljs-comment"><!--mysql--></span><br> <span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>mysql<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>mysql-connector-java<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">version</span>></span>8.0.33<span class="hljs-tag"></<span class="hljs-name">version</span>></span><br> <span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br> <span class="hljs-comment"><!--sqlserver--></span><br> <span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>com.microsoft.sqlserver<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>mssql-jdbc<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">version</span>></span>12.2.0.jre8<span class="hljs-tag"></<span class="hljs-name">version</span>></span><br> <span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br> <span class="hljs-comment"><!--测试--></span><br> <span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.junit.jupiter<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>junit-jupiter<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">version</span>></span>5.9.3<span class="hljs-tag"></<span class="hljs-name">version</span>></span><br> <span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependencies</span>></span><br></code></pre></td></tr></table></figure><hr><h2 id="核心配置"><a href="#核心配置" class="headerlink" title="核心配置"></a><a href="https://mybatis.org/mybatis-3/zh/configuration.html">核心配置</a></h2><h3 id="核心配置文件"><a href="#核心配置文件" class="headerlink" title="核心配置文件"></a>核心配置文件</h3><blockquote><p>配置标签顺序(核心配置文件中的==标签顺序必须符合下面顺序==)</p></blockquote><img src="https://gitee.com/clibin/image-bed/raw/master/image-20231013140702442.png" alt="图片没了" style="float: left;"><blockquote><p>核心配置文件<code>mybatis-config.xml</code>示例</p></blockquote><figure class="highlight xml"><table><tr><td class="code"><pre><code class="hljs xml"><span class="hljs-meta"><?xml version=<span class="hljs-string">"1.0"</span> encoding=<span class="hljs-string">"UTF-8"</span> ?></span><br><span class="hljs-meta"><!DOCTYPE <span class="hljs-keyword">configuration</span> <span class="hljs-keyword">PUBLIC</span> <span class="hljs-string">"-//mybatis.org//DTD Config 3.0//EN"</span></span><br><span class="hljs-meta"> <span class="hljs-string">"https://mybatis.org/dtd/mybatis-3-config.dtd"</span>></span><br><span class="hljs-comment"><!--configuration 核心配置文件--></span><br><span class="hljs-tag"><<span class="hljs-name">configuration</span>></span><br> <span class="hljs-comment"><!--1.加载配置文件,从配置文件中读取数据使用${},见下面的数据源配置--></span><br> <span class="hljs-tag"><<span class="hljs-name">properties</span> <span class="hljs-attr">resource</span>=<span class="hljs-string">"db.properties"</span>/></span><br> <br> <span class="hljs-comment"><!--2.配置日志--></span><br> <span class="hljs-tag"><<span class="hljs-name">settings</span>></span><br> <span class="hljs-comment"><!--标准日志工厂配置--></span><br> <span class="hljs-tag"><<span class="hljs-name">setting</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"logImpl"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"STDOUT_LOGGING"</span>/></span><br> <span class="hljs-comment"><!--配置log4j日志--></span><br> <span class="hljs-comment"><!--<setting name="logImpl" value="LOG4J"/>--></span><br> <span class="hljs-tag"></<span class="hljs-name">settings</span>></span><br><br> <span class="hljs-comment"><!--</span><br><span class="hljs-comment"> 3.可以给实体类取别名,之后使用类名不需要写全限定类名</span><br><span class="hljs-comment"> 3.1 typeAlias标签页实现</span><br><span class="hljs-comment"> 3.2 package 扫描包下所有类并起一个默认别名为类名(小写也可以)或</span><br><span class="hljs-comment"> 如果想自定义别名,需要在类名上面加@Alias注解</span><br><span class="hljs-comment"> --></span><br> <span class="hljs-tag"><<span class="hljs-name">typeAliases</span>></span><br> <span class="hljs-comment"><!--<typeAlias type="com.clb.pojo.User" alias="User"/>--></span><br> <span class="hljs-tag"><<span class="hljs-name">package</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"com.clb.pojo"</span>/></span><br> <span class="hljs-tag"></<span class="hljs-name">typeAliases</span>></span><br><br> <span class="hljs-comment"><!--4.配置多环境,default指定使用哪个环境--></span><br> <span class="hljs-comment"><!--<environments default="mysql">--></span><br> <span class="hljs-tag"><<span class="hljs-name">environments</span> <span class="hljs-attr">default</span>=<span class="hljs-string">"sqlserver"</span>></span><br> <span class="hljs-comment"><!--mysql数据源--></span><br> <span class="hljs-tag"><<span class="hljs-name">environment</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"mysql"</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">transactionManager</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"JDBC"</span>/></span><br> <span class="hljs-tag"><<span class="hljs-name">dataSource</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"POOLED"</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">property</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"driver"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"${mysql.driver}"</span>/></span><br> <span class="hljs-tag"><<span class="hljs-name">property</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"url"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"${mysql.url}"</span>/></span><br> <span class="hljs-tag"><<span class="hljs-name">property</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"username"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"${mysql.username}"</span>/></span><br> <span class="hljs-tag"><<span class="hljs-name">property</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"${mysql.password}"</span>/></span><br> <span class="hljs-tag"></<span class="hljs-name">dataSource</span>></span><br> <span class="hljs-tag"></<span class="hljs-name">environment</span>></span><br> <span class="hljs-comment"><!--sql server数据源--></span><br> <span class="hljs-tag"><<span class="hljs-name">environment</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"sqlserver"</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">transactionManager</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"JDBC"</span>/></span><br> <span class="hljs-tag"><<span class="hljs-name">dataSource</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"POOLED"</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">property</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"driver"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"${ms.driver}"</span>/></span><br> <span class="hljs-tag"><<span class="hljs-name">property</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"url"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"${ms.url}"</span>/></span><br> <span class="hljs-tag"><<span class="hljs-name">property</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"username"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"${ms.username}"</span>/></span><br> <span class="hljs-tag"><<span class="hljs-name">property</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"password"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"${ms.password}"</span>/></span><br> <span class="hljs-tag"></<span class="hljs-name">dataSource</span>></span><br> <span class="hljs-tag"></<span class="hljs-name">environment</span>></span><br> <span class="hljs-tag"></<span class="hljs-name">environments</span>></span><br> <br> <span class="hljs-comment"><!--</span><br><span class="hljs-comment"> 每一个Mapper.xml配置文件都需要在mybatis核心配置文件中注册</span><br><span class="hljs-comment"> 1.mapper标签 使用resource指定mapper配置文件注册(无条件!)</span><br><span class="hljs-comment"> 2.mapper标签 使用class指定mapper类注册</span><br><span class="hljs-comment"> 3.package标签扫描包</span><br><span class="hljs-comment"> 总结:第 2,3中方法都需要满足:</span><br><span class="hljs-comment"> 条件:1.接口名和它的mapper配置文件名相同</span><br><span class="hljs-comment"> 2.接口文件和它的mapper配置文件在同一个包下</span><br><span class="hljs-comment"> 1.所以建议使用第一种方法,指定配置文件路径(使用配置文件时)</span><br><span class="hljs-comment"> 2.使用注解开发时,使用方法2,3</span><br><span class="hljs-comment"> --></span><br> <span class="hljs-tag"><<span class="hljs-name">mappers</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">mapper</span> <span class="hljs-attr">resource</span>=<span class="hljs-string">"com/clb/dao/EmpMapper.xml"</span>/></span><br> <span class="hljs-comment"><!--<mapper class="com.clb.dao.EmpMapper"/>--></span><br> <span class="hljs-comment"><!--<package name="com.clb.dao"/>--></span><br> <span class="hljs-tag"></<span class="hljs-name">mappers</span>></span><br><br><span class="hljs-tag"></<span class="hljs-name">configuration</span>></span><br></code></pre></td></tr></table></figure><blockquote><p>数据源配置文件示例</p></blockquote><figure class="highlight properties"><table><tr><td class="code"><pre><code class="hljs properties"><span class="hljs-comment"># mysql</span><br><span class="hljs-attr">mysql.driver</span>=<span class="hljs-string">com.mysql.cj.jdbc.Driver</span><br><span class="hljs-attr">mysql.url</span>=<span class="hljs-string">jdbc:mysql://localhost:3306/clb</span><br><span class="hljs-attr">mysql.username</span>=<span class="hljs-string">root</span><br><span class="hljs-attr">mysql.password</span>=<span class="hljs-string">123456</span><br><span class="hljs-comment"># sql server</span><br><span class="hljs-attr">ms.driver</span>=<span class="hljs-string">com.microsoft.sqlserver.jdbc.SQLServerDriver</span><br><span class="hljs-comment">#ms.url=jdbc:sqlserver://localhost:1433;trustServerCertificate=true</span><br><span class="hljs-attr">ms.url</span>=<span class="hljs-string">jdbc:sqlserver://192.168.0.88:1433;trustServerCertificate=true</span><br><span class="hljs-attr">ms.username</span>=<span class="hljs-string">sa</span><br><span class="hljs-attr">ms.password</span>=<span class="hljs-string">123456</span><br></code></pre></td></tr></table></figure><h3 id="Mapper-xml映射文件"><a href="#Mapper-xml映射文件" class="headerlink" title="Mapper.xml映射文件"></a>Mapper.xml映射文件</h3><figure class="highlight xml"><table><tr><td class="code"><pre><code class="hljs xml"><span class="hljs-meta"><?xml version=<span class="hljs-string">"1.0"</span> encoding=<span class="hljs-string">"UTF-8"</span> ?></span><br><span class="hljs-meta"><!DOCTYPE <span class="hljs-keyword">mapper</span></span><br><span class="hljs-meta"> <span class="hljs-keyword">PUBLIC</span> <span class="hljs-string">"-//mybatis.org//DTD Mapper 3.0//EN"</span></span><br><span class="hljs-meta"> <span class="hljs-string">"http://mybatis.org/dtd/mybatis-3-mapper.dtd"</span>></span><br><br><span class="hljs-comment"><!--把原来的UserDaoImpl类转换成一个xml配置文件--></span><br><br><span class="hljs-comment"><!--namespace=绑定一个对应的Dao/Mapper接口--></span><br><span class="hljs-tag"><<span class="hljs-name">mapper</span> <span class="hljs-attr">namespace</span>=<span class="hljs-string">"com.clb.dao.UserMapper"</span>></span><br> <span class="hljs-comment"><!--结果集映射 type -> 需要将数据库数据映射成user类型 数据库字段名和属性名一致则会自动映射,无需额外配置--></span><br> <span class="hljs-tag"><<span class="hljs-name">resultMap</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"UserMap"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"user"</span>></span><br> <span class="hljs-comment"><!--colum 数据库中的列 -> property 实体类中的属性--></span><br> <span class="hljs-comment"><!--<result column="id" property="id"/>--></span><br> <span class="hljs-comment"><!--<result column="name" property="name"/>--></span><br> <span class="hljs-tag"><<span class="hljs-name">result</span> <span class="hljs-attr">column</span>=<span class="hljs-string">"pwd"</span> <span class="hljs-attr">property</span>=<span class="hljs-string">"password"</span>/></span><br> <span class="hljs-tag"></<span class="hljs-name">resultMap</span>></span><br> <br> <span class="hljs-comment"><!--</span><br><span class="hljs-comment"> 查询语句</span><br><span class="hljs-comment"> id: 对应namespace中的接口的方法名</span><br><span class="hljs-comment"> resultType: sql语句执行的返回类型</span><br><span class="hljs-comment"> --></span><br> <span class="hljs-tag"><<span class="hljs-name">select</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"getUserList"</span> <span class="hljs-attr">resultType</span>=<span class="hljs-string">"com.clb.pojo.User"</span>></span><br> select * from mybatis.user<br> <span class="hljs-tag"></<span class="hljs-name">select</span>></span><br> <br> <br> <span class="hljs-comment"><!--根据id查询用户,这里#{id}中的id就是方法中的id变量--></span><br> <span class="hljs-tag"><<span class="hljs-name">select</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"selectById"</span> <span class="hljs-attr">resultType</span>=<span class="hljs-string">"com.clb.pojo.User"</span> <span class="hljs-attr">parameterType</span>=<span class="hljs-string">"int"</span>></span><br> select * from user where id = #{id}<br> <span class="hljs-tag"></<span class="hljs-name">select</span>></span><br><br> <span class="hljs-comment"><!--模糊查询,concat用于拼接字符串--></span><br> <span class="hljs-tag"><<span class="hljs-name">select</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"getUserLike"</span> <span class="hljs-attr">resultType</span>=<span class="hljs-string">"com.clb.pojo.User"</span>></span><br> select * from user where name like concat('', #{name}, '');<br> <span class="hljs-tag"></<span class="hljs-name">select</span>></span><br><br> <span class="hljs-comment"><!--添加一个用户,对象中的属性可以直接取出来,不需要加类名user.--></span><br> <span class="hljs-tag"><<span class="hljs-name">insert</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"addUser"</span> <span class="hljs-attr">parameterType</span>=<span class="hljs-string">"com.clb.pojo.User"</span>></span><br> insert into user(id, name, pwd) value (#{id}, #{name}, #{pwd})<br> <span class="hljs-tag"></<span class="hljs-name">insert</span>></span> <br><span class="hljs-tag"></<span class="hljs-name">mapper</span>></span><br></code></pre></td></tr></table></figure><hr><h3 id="MybatisUtils"><a href="#MybatisUtils" class="headerlink" title="MybatisUtils"></a>MybatisUtils</h3><blockquote><p>要使用mybatis执行sql语句,要使用<code>SqlSession</code>对象,可以直接在测试类中获取</p></blockquote><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Test</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">test</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> IOException {<br> <span class="hljs-comment">//加载核心配置文件</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">resource</span> <span class="hljs-operator">=</span> <span class="hljs-string">"mybatis-config.xml"</span>;<br> <span class="hljs-type">InputStream</span> <span class="hljs-variable">inputStream</span> <span class="hljs-operator">=</span> Resources.getResourceAsStream(resource);<br> <span class="hljs-comment">//获取SqlSessionFactory对象</span><br> <span class="hljs-type">SqlSessionFactory</span> <span class="hljs-variable">sqlSessionFactory</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">SqlSessionFactoryBuilder</span>().build(inputStream);<br> <span class="hljs-comment">//获取SqlSession对象</span><br> <span class="hljs-keyword">try</span> (<span class="hljs-type">SqlSession</span> <span class="hljs-variable">session</span> <span class="hljs-operator">=</span> sqlSessionFactory.openSession()) {<br> <span class="hljs-comment">//获取Mapper对象,执行xml映射文件中的方法</span><br> <span class="hljs-type">EmpMapper</span> <span class="hljs-variable">mapper</span> <span class="hljs-operator">=</span> session.getMapper(EmpMapper.class);<br> List<Emp> emps = mapper.selectBySalary(<span class="hljs-number">5000</span>);<br> emps.forEach(System.out::println);<br> }<br>}<br></code></pre></td></tr></table></figure><blockquote><p>但是每次使用都要写这一串代码,<strong>有点麻烦</strong>,前面获取SqlSession对象的代码是固定代码,将这段固定代码提取到工具类<code>MybatisUtils</code>,使用工具类的静态方法获取SqlSession对象</p></blockquote><blockquote><p><strong>MybatisUtils工具类示例</strong></p></blockquote><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//sqlSessionFactory --> sqlSession</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">MybatisUtils</span> {<br> <span class="hljs-keyword">private</span> <span class="hljs-title function_">MybatisUtils</span><span class="hljs-params">()</span> {}<br> <span class="hljs-comment">//定义一个SqlSessionFactory对象并使用静态初始化块初始化对象</span><br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> SqlSessionFactory sqlSessionFactory;<br> <span class="hljs-keyword">static</span> {<br> <span class="hljs-keyword">try</span> {<br> <span class="hljs-comment">//使用mybatis第一步,获取sqlSessionFactory对象</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">resource</span> <span class="hljs-operator">=</span> <span class="hljs-string">"mybatis-config.xml"</span>;<br> <span class="hljs-type">InputStream</span> <span class="hljs-variable">inputStream</span> <span class="hljs-operator">=</span> Resources.getResourceAsStream(resource);<br> sqlSessionFactory = <span class="hljs-keyword">new</span> <span class="hljs-title class_">SqlSessionFactoryBuilder</span>().build(inputStream);<br> } <span class="hljs-keyword">catch</span> (IOException e) {<br> <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">RuntimeException</span>(e);<br> }<br> }<br><br> <span class="hljs-comment">//既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。SqlSession 提供了在数据库执行 SQL 命令所需的所有方法</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> SqlSession <span class="hljs-title function_">getSqlSession</span><span class="hljs-params">()</span> {<br> <span class="hljs-keyword">return</span> sqlSessionFactory.openSession();<br> }<br>}<br></code></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Test</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">testMybatisUtils</span><span class="hljs-params">()</span> {<br> <span class="hljs-comment">//使用工具类直接获取SqlSession对象</span><br> <span class="hljs-type">SqlSession</span> <span class="hljs-variable">sqlSession</span> <span class="hljs-operator">=</span> MybatisUtils.getSqlSession();<br><br> <span class="hljs-type">EmpMapper</span> <span class="hljs-variable">mapper</span> <span class="hljs-operator">=</span> sqlSession.getMapper(EmpMapper.class);<br> List<Emp> emps = mapper.selectBySalary(<span class="hljs-number">1000</span>);<br> emps.forEach(System.out::println);<br><br> sqlSession.close();<br>}<br></code></pre></td></tr></table></figure><h2 id="注解开发"><a href="#注解开发" class="headerlink" title="注解开发"></a>注解开发</h2><blockquote><p>使用注解直接写sql语句代替xml映射文件</p></blockquote><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">UserMapper</span> {<br> <span class="hljs-meta">@Select("select id, name, pwd password from user")</span><br> List<User> <span class="hljs-title function_">selectAll</span><span class="hljs-params">()</span>;<br> <span class="hljs-comment">/*</span><br><span class="hljs-comment"> * @Param()注解:</span><br><span class="hljs-comment"> * 1.基本类型或者String类型需要加上</span><br><span class="hljs-comment"> * 2.引用类型可以不加</span><br><span class="hljs-comment"> * 3.如果只有一个基本类型的话,可以不加,但是建议加上</span><br><span class="hljs-comment"> * 4.在注解中的sql语句中{}中的参数就是@Param注解中的属性名</span><br><span class="hljs-comment"> * */</span><br> <span class="hljs-meta">@Select("select id, name, pwd password " +</span><br><span class="hljs-meta"> "from user " +</span><br><span class="hljs-meta"> "where id = #{id} and name=#{name} and pwd=#{password}")</span><br> User <span class="hljs-title function_">selectById</span><span class="hljs-params">(<span class="hljs-meta">@Param("id")</span> <span class="hljs-type">int</span> i, <span class="hljs-meta">@Param("name")</span>String n, <span class="hljs-meta">@Param("password")</span> String p)</span>;<br><br> <span class="hljs-comment">//#{id},#{name},#{password}中的三个属性均来自参数中对象的属性</span><br> <span class="hljs-meta">@Insert("insert into user(id, name, pwd) VALUE (#{id},#{name},#{password})")</span><br> <span class="hljs-type">int</span> <span class="hljs-title function_">addUser</span><span class="hljs-params">(User user)</span>;<br><br> <span class="hljs-meta">@Update("update user set name=#{name},pwd=#{password} where id=#{id}")</span><br> <span class="hljs-type">int</span> <span class="hljs-title function_">updateUser</span><span class="hljs-params">(User user)</span>;<br><br> <span class="hljs-meta">@Delete("delete from user where id = #{id}")</span><br> <span class="hljs-type">int</span> <span class="hljs-title function_">deleteUserById</span><span class="hljs-params">(<span class="hljs-meta">@Param("id")</span> <span class="hljs-type">int</span> id)</span>;<br>}<br></code></pre></td></tr></table></figure><blockquote><p>使用<code>@Result</code>注解可以定义结果映射,使用<code>@ResultMap</code>可以引用xml文件中定义的ResultMap结果映射</p></blockquote><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Select("select * from emp where first_name = #{name};")</span><br><span class="hljs-meta">@Results({</span><br><span class="hljs-meta"> @Result(column = "first_name", property = "firstName")</span><br><span class="hljs-meta">})</span><br>Emp <span class="hljs-title function_">getByFirstNameEmp</span><span class="hljs-params">(String name)</span>;<br></code></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//更推荐使用</span><br><span class="hljs-meta">@Select("select * from emp where first_name = #{name};")</span><br><span class="hljs-meta">@ResultMap("EmpMap")</span><br>Emp <span class="hljs-title function_">getByFirstNameEmp</span><span class="hljs-params">(String name)</span>;<br></code></pre></td></tr></table></figure><blockquote><p>总结:简单的sql语句使用注解比配置文件来的更方便且更快,但是复杂的sql语句使用注解反而不方便或难以实现,所以==简单的sql使用注解,复杂的使用xml,搭配使用==</p></blockquote><hr><h2 id="动态-SQL"><a href="#动态-SQL" class="headerlink" title="动态 SQL"></a><a href="https://mybatis.org/mybatis-3/zh/dynamic-sql.html">动态 SQL</a></h2><blockquote><p>==动态查询==</p></blockquote><figure class="highlight xml"><table><tr><td class="code"><pre><code class="hljs xml"><span class="hljs-comment"><!--where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。</span><br><span class="hljs-comment"> 而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。--></span><br><br><span class="hljs-comment"><!--动态sql if标签 如果参数不为空就加上条件,否则不加--></span><br><span class="hljs-tag"><<span class="hljs-name">select</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"pageQuery"</span> <span class="hljs-attr">resultType</span>=<span class="hljs-string">"com.sky.entity.Category"</span>></span><br> select * from category<br> <span class="hljs-tag"><<span class="hljs-name">where</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">if</span> <span class="hljs-attr">test</span>=<span class="hljs-string">"name != null and name != ''"</span>></span><br> and name like concat('%',#{name},'%')<br> <span class="hljs-tag"></<span class="hljs-name">if</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">if</span> <span class="hljs-attr">test</span>=<span class="hljs-string">"type != null"</span>></span><br> and type = #{type}<br> <span class="hljs-tag"></<span class="hljs-name">if</span>></span><br> <span class="hljs-tag"></<span class="hljs-name">where</span>></span><br> order by sort asc , create_time desc<br><span class="hljs-tag"></<span class="hljs-name">select</span>></span><br></code></pre></td></tr></table></figure><blockquote><p>==动态更新==</p></blockquote><figure class="highlight xml"><table><tr><td class="code"><pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">update</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"update"</span> <span class="hljs-attr">parameterType</span>=<span class="hljs-string">"Category"</span>></span><br> update category<br> <span class="hljs-tag"><<span class="hljs-name">set</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">if</span> <span class="hljs-attr">test</span>=<span class="hljs-string">"type != null"</span>></span><br> type = #{type},<br> <span class="hljs-tag"></<span class="hljs-name">if</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">if</span> <span class="hljs-attr">test</span>=<span class="hljs-string">"name != null"</span>></span><br> name = #{name},<br> <span class="hljs-tag"></<span class="hljs-name">if</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">if</span> <span class="hljs-attr">test</span>=<span class="hljs-string">"sort != null"</span>></span><br> sort = #{sort},<br> <span class="hljs-tag"></<span class="hljs-name">if</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">if</span> <span class="hljs-attr">test</span>=<span class="hljs-string">"status != null"</span>></span><br> status = #{status},<br> <span class="hljs-tag"></<span class="hljs-name">if</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">if</span> <span class="hljs-attr">test</span>=<span class="hljs-string">"updateTime != null"</span>></span><br> update_time = #{updateTime},<br> <span class="hljs-tag"></<span class="hljs-name">if</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">if</span> <span class="hljs-attr">test</span>=<span class="hljs-string">"updateUser != null"</span>></span><br> update_user = #{updateUser}<br> <span class="hljs-tag"></<span class="hljs-name">if</span>></span><br> <span class="hljs-tag"></<span class="hljs-name">set</span>></span><br> where id = #{id}<br><span class="hljs-tag"></<span class="hljs-name">update</span>></span><br></code></pre></td></tr></table></figure><blockquote><p>==批量插入,传入参数是一个集合类型,删除同理==</p></blockquote><figure class="highlight xml"><table><tr><td class="code"><pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">insert</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"insertBatch"</span>></span><br> insert into dish_flavor (dish_id, name, value)values<br> <span class="hljs-tag"><<span class="hljs-name">foreach</span> <span class="hljs-attr">collection</span>=<span class="hljs-string">"flavors"</span> <span class="hljs-attr">item</span>=<span class="hljs-string">"f"</span> <span class="hljs-attr">separator</span>=<span class="hljs-string">","</span>></span><br> (#{f.dishId},#{f.name},#{f.value})<br> <span class="hljs-tag"></<span class="hljs-name">foreach</span>></span><br><span class="hljs-tag"></<span class="hljs-name">insert</span>></span><br></code></pre></td></tr></table></figure><blockquote><p>更多动态SQL用法见<a href="https://mybatis.org/mybatis-3/zh/dynamic-sql.html">官网</a></p><p>接下来,了解进阶框架==mybatis-plus==</p></blockquote>]]></content>
<categories>
<category> 后端 </category>
</categories>
<tags>
<tag> mysql </tag>
<tag> mybatis </tag>
<tag> java </tag>
</tags>
</entry>
<entry>
<title>Redis</title>
<link href="/2023/08/31/Redis/"/>
<url>/2023/08/31/Redis/</url>
<content type="html"><![CDATA[<h1 id="Redis"><a href="#Redis" class="headerlink" title="Redis"></a><a href="https://redis.io/">Redis</a></h1><h2 id="什么是Nosql"><a href="#什么是Nosql" class="headerlink" title="什么是Nosql"></a>什么是Nosql</h2><blockquote><p>==NoSQL==-> ==not only sql==,不仅仅是sql(不是没有sql)</p></blockquote><p>Nosql特点</p><ol><li>方便拓展(数据之间没有关系,很好拓展!)</li><li>大数据量高性能(redis一秒写8万次,读取11万次,NoSQL的缓存记录级,是一种细粒度的缓存,性能会比较高!)</li><li>数据类型是多样的!(不需要事先设计数据库!随取随用!如果是数据量非常大的表,关系型数据库就很难设计了!)</li><li>传统==RDBMS==(关系型数据库管理系统)和==NoSQL==</li></ol><p>RDBMS的特点:</p><ul><li>基于关系模型,使用表格的存储方式,数据按照行和列进行组织。</li><li>使用SQL语言进行数据的查询和操作,SQL语言是一种通用的、标准化的、结构化的语言,可以进行复杂的查询和分析。</li><li>强调ACID规则(原子性、一致性、隔离性、持久性),可以保证数据的完整性和一致性,适合处理高要求的事务操作。</li><li>通常只能进行纵向扩展,即增加单个服务器的硬件资源来提高性能,这种方式成本高昂且有上限。</li><li>适合处理结构化或半结构化的数据,需要进行复杂查询或分析的场景,例如金融、电商、教育等领域。</li></ul><p>NoSQL的特点:</p><ul><li>不基于关系模型,使用键值对、文档、图形等多种存储方式,数据的结构和格式可以灵活变化,不需要预先定义。</li><li>使用非结构化查询语言(UnQL)或者特定的API进行数据的访问和操作,UnQL没有统一的标准,每种NoSQL数据库都有自己的语法和规则。</li><li>通常只提供弱一致性或最终一致性的保证,不能支持复杂的事务操作,但可以提高数据的可用性和并发性。</li><li>通常可以进行横向扩展,即增加多个服务器来分担数据和负载,这种方式成本低廉且可以无限扩展。</li><li>适合处理非结构化或多变的数据,需要高并发或海量数据存储的场景,例如社交网络、游戏、物联网等领域。</li></ul><p>大数据时代的3V:主要是描述问题的</p><ol><li>海量Volume</li><li>多样Variety</li><li>实时Velocity</li></ol><p>大数据时代的3高:主要是对程序的要求</p><ol><li>高并发</li><li>高可扩</li><li>高性能</li></ol><p>实际开发项目一般都是 NoSQL+RDBMS 搭配使用</p><hr><h2 id="redis入门"><a href="#redis入门" class="headerlink" title="redis入门"></a>redis入门</h2><h3 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h3><blockquote><p>redis是什么?</p></blockquote><ol><li>Redis(==Re==mote ==Di==ctionary ==S==erver ),即远程字典服务</li><li>是一个开源的使用ANSI ==C语言==编写、支持网络、可基于内存亦可持久化的日志型、==Key-Value==数据库,并提供多种语言的API</li><li>redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave==(主从)同步==</li><li>免费和开源,是当下最热门的NoSQL技术之一</li></ol><blockquote><p>redis能做什么?</p></blockquote><ol><li>内存存储、==持久化==,内存是断电即失的,所以说持久化很重要!</li><li>效率高,可以用于高速缓存</li><li>发布订阅系统</li><li>地图信息分析</li><li>计时器、计数器(浏览量)</li></ol><blockquote><p>特性</p></blockquote><ol><li>多样的数据类型</li><li>持久化</li><li>事务</li><li>集群</li></ol><h3 id="基本知识"><a href="#基本知识" class="headerlink" title="基本知识"></a>基本知识</h3><blockquote><p>登录redis客户端redis-cli:computer:</p></blockquote><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># 直接登录 -h ip地址 -p 端口号 -a 密码(不用密码也能登陆,但是没有权限)</span><br>redis-cli -h localhost -p 6379 -a 123456<br><br><span class="hljs-comment"># 推荐登录后再用密码进行认证</span><br><span class="hljs-comment"># 1.登录</span><br>redis-cli (-h localhost -p 6379 如果是本地登陆可以省略)<br><br><span class="hljs-comment"># 2.身份认证 OK >>> 成功</span><br>> auth 123456<br><br><span class="hljs-comment"># 查看命令的帮助信息</span><br><span class="hljs-built_in">help</span> [<span class="hljs-built_in">command</span>]<br></code></pre></td></tr></table></figure><img src="https://github.com/TankingCao/picx-images-hosting/raw/master/CopyQ.PGpyFa.13e3fgmo8pb4.webp" alt="CopyQ" /><blockquote><p><strong>redis有==16==个数据库(0-15),默认使用的是==0==,可以使用select切换数据库</strong></p></blockquote><p>切换数据库 <code>select [index]</code></p><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># 切换数据库为1号</span><br><span class="hljs-keyword">select</span> 1<br></code></pre></td></tr></table></figure><p>查看当前数据库大小<code>dbsize</code></p><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash">dbsize<br>> (<span class="hljs-built_in">integer</span>) 1<br></code></pre></td></tr></table></figure><p>设置键值对<code>set [key] [value]</code></p><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">set</span> name clb<br>> OK<br></code></pre></td></tr></table></figure><p>根据键获取值<code>get [key]</code></p><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash">get name<br>> <span class="hljs-string">"clb"</span><br></code></pre></td></tr></table></figure><p>查找所有适配的key <code>keys [pattern] </code></p><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># 查询所有</span><br>keys *<br>> 1) <span class="hljs-string">"name"</span><br>> 2) <span class="hljs-string">"age"</span><br></code></pre></td></tr></table></figure><p>清空当前数据库<code>flushdb</code></p><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash">flushdb<br></code></pre></td></tr></table></figure><p>清空所有数据库<code>flushall</code></p><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash">flushall<br></code></pre></td></tr></table></figure><hr><blockquote><p><strong>==redis是单线程的!==</strong></p></blockquote><p>官方表示,redis是基于内存操作,CPU不是redis的性能瓶颈,redis的性能瓶颈是机器的内存大小和网络带宽,既然单线程更容易实现,那就顺理成章的使用单线程了</p><blockquote><p>==<strong>redis为什么单线程还这么快?</strong>==</p></blockquote><p>误区:</p><ul><li>高性能的服务器一定是多线程的?</li><li>多线程(CPU上下文会切换)一定比单线程效率高?</li></ul><p>==核心==:redis是将所有数据全部放在内存中的,所以说使用单线程去操作效率就是最高的,多线程(CPU上下文会切换:耗时的操作!!!),对于内存系统来说,如果没有上下文切换效率就是最高的!多次读写操作都是在一个CPU上的,在内存情况下,这个就是最佳的方案!</p><hr><h3 id="Redis-Key"><a href="#Redis-Key" class="headerlink" title="Redis-Key"></a>Redis-Key</h3><p>查看一个或者多个key是否存在,返回个数<code>exists [key...]</code></p><img src="https://s2.loli.net/2024/09/27/R2JGgMxC94EhyTk.webp" alt="CopyQ" /><p>设置过期时间(多长时间后过期自动从数据库删除)<code>expire [key] [time] 单位默认是秒</code></p><blockquote><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash">expire name 3<span class="hljs-comment"># 3秒后过期,过期了数据就没了</span><br></code></pre></td></tr></table></figure></blockquote><p>查看key的剩余时间<code>ttl [key]</code></p><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash">ttl name<br>> 2<br></code></pre></td></tr></table></figure><p>移动key到其他数据库<code>move [key] [db]</code></p><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash">move name 15<br></code></pre></td></tr></table></figure><p>删除一个或多个key<code>del [key...]</code></p><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash">del name<br>del name age<br></code></pre></td></tr></table></figure><p>查看key的类型<code>type [key]</code>,不存在返回none</p><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">type</span> name <br>> string<br></code></pre></td></tr></table></figure><hr><h2 id="五大数据类型"><a href="#五大数据类型" class="headerlink" title="五大数据类型"></a>五大数据类型</h2><p>Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作==数据库==、==缓存==和==消息中间件==,它支持多种类型的数据结构,如 <a href="http://www.redis.cn/topics/data-types-intro.html#strings">字符串(strings)</a>, <a href="http://www.redis.cn/topics/data-types-intro.html#hashes">散列(hashes)</a>, <a href="http://www.redis.cn/topics/data-types-intro.html#lists">列表(lists)</a>, <a href="http://www.redis.cn/topics/data-types-intro.html#sets">集合(sets)</a>, <a href="http://www.redis.cn/topics/data-types-intro.html#sorted-sets">有序集合(sorted sets)</a> 与范围查询, <a href="http://www.redis.cn/topics/data-types-intro.html#bitmaps">bitmaps</a>, <a href="http://www.redis.cn/topics/data-types-intro.html#hyperloglogs">hyperloglogs</a> 和 <a href="http://www.redis.cn/commands/geoadd.html">地理空间(geospatial)</a> 索引半径查询。 Redis 内置了 <a href="http://www.redis.cn/topics/replication.html">复制(replication)</a>,<a href="http://www.redis.cn/commands/eval.html">LUA脚本(Lua scripting)</a>, <a href="http://www.redis.cn/topics/lru-cache.html">LRU驱动事件(LRU eviction)</a>,<a href="http://www.redis.cn/topics/transactions.html">事务(transactions)</a> 和不同级别的 <a href="http://www.redis.cn/topics/persistence.html">磁盘持久化(persistence)</a>, 并通过 <a href="http://www.redis.cn/topics/sentinel.html">Redis哨兵(Sentinel)</a>和自动 <a href="http://www.redis.cn/topics/cluster-tutorial.html">分区(Cluster)</a>提供高可用性(high availability)</p><hr><h3 id="String"><a href="#String" class="headerlink" title="String"></a>String</h3><p>==字符串==</p><p>set、get、keys、exists同上</p><p>追加字符串<code>append [key] [value]</code> 如果name不存在,就等同于set方法</p><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash">append name nb<br>> (<span class="hljs-built_in">integer</span>) 5<br></code></pre></td></tr></table></figure><p>查询字符串长度<code>strlen [key]</code></p><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash">strlen name<br>> (<span class="hljs-built_in">integer</span>) 5<br></code></pre></td></tr></table></figure><hr><p>增加和减少操作,前提是key对应的值可以转化成integer,否则报错</p><p>自增(++) <code>incr [key]</code>,自减(–)<code>decr [key]</code></p><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash">get views<br>> <span class="hljs-string">"3"</span><br><br>incr views<br>> (<span class="hljs-built_in">integer</span>) 4<br><br>decr views<br>> (<span class="hljs-built_in">integer</span>) 3<br></code></pre></td></tr></table></figure><p>增加(+=)<code>incrby [key] [步长]</code>,减少(-=)<code>decrby [key] [步长]</code></p><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash">get views<br>> <span class="hljs-string">"10"</span><br><br>INCRBY views 5<br>> (<span class="hljs-built_in">integer</span>) 15<br><br>DECRBY views 3<br>> (<span class="hljs-built_in">integer</span>) 12<br></code></pre></td></tr></table></figure><blockquote><p> 获取字符串的子串<code>getrange [key] [start] [end]</code></p></blockquote><p>注意:==闭区间==(开始索引和结束索引都包含),==逆序索引从-1开始(倒数第1个索引为-1,倒数第2索引为-2…)==</p><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash">get k<br>> <span class="hljs-string">"hello world!"</span><br><br>getrange k 0 4<span class="hljs-comment">#[0,4]</span><br>> <span class="hljs-string">"hello"</span><br><br>getrange k 0 -1<span class="hljs-comment">#获取全部,0表示头部,-1表示尾部</span><br><span class="hljs-string">"hello world!"</span><br></code></pre></td></tr></table></figure><blockquote><p>替换字符串<code>setrange [key] [start] [value]</code>,从start索引开始,用value直接覆盖后面的值,如果原来字符串长度不够,那么补0增加长度后再进行覆盖</p></blockquote><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash">127.0.0.1:6379> get str<br><span class="hljs-string">"12345"</span><br><br>127.0.0.1:6379> setrange str 1 abcdefg<br>(<span class="hljs-built_in">integer</span>) 8<br><br>127.0.0.1:6379> get str<br><span class="hljs-string">"1abcdefg"</span><br></code></pre></td></tr></table></figure><p>设置key-value并指定过期时间<code>setex [key] [seconds] [value]</code></p><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash">127.0.0.1:6379> setex name 10 clb<br>OK<br>127.0.0.1:6379> ttl name<br>(<span class="hljs-built_in">integer</span>) 7<br></code></pre></td></tr></table></figure><blockquote><p>==设置key-value==<code>setnx [key] [value]</code></p></blockquote><p>和set的区别:==如果key已经存在,set方法直接覆盖原来的值,而setnx不会==</p><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash">127.0.0.1:6379> setnx name cxk<br>(<span class="hljs-built_in">integer</span>) 1<br>127.0.0.1:6379> setnx name ikun<br>(<span class="hljs-built_in">integer</span>) 0<span class="hljs-comment">#key已经存在,不覆盖</span><br>127.0.0.1:6379> get name<br><span class="hljs-string">"cxk"</span><br></code></pre></td></tr></table></figure><hr><p>批量设置<code>mset [key value ...]</code></p><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash">127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3<br>OK<br>127.0.0.1:6379> keys *<br>1) <span class="hljs-string">"k3"</span><br>2) <span class="hljs-string">"k1"</span><br>3) <span class="hljs-string">"k2"</span><br></code></pre></td></tr></table></figure><p>批量获取<code>mget [key ...]</code></p><blockquote><p>批量设置<code>msetnx [key value ...]</code>msetnx是一个==<strong>原子性</strong>==操作,要么全部成功,要么全部失败,只要有一个key存在,整个操作全部失败</p></blockquote><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash">127.0.0.1:6379> mget k1 k2 k3<br>1) <span class="hljs-string">"v1"</span><br>2) <span class="hljs-string">"v2"</span><br>3) <span class="hljs-string">"v3"</span><br>127.0.0.1:6379> msetnx k1 666 k4 999<br>(<span class="hljs-built_in">integer</span>) 0<span class="hljs-comment"># k1存在,整个操作失败,k4也未赋值</span><br>127.0.0.1:6379> mget k1 k2 k3 k4<br>1) <span class="hljs-string">"v1"</span><br>2) <span class="hljs-string">"v2"</span><br>3) <span class="hljs-string">"v3"</span><br>4) (nil)<br></code></pre></td></tr></table></figure><hr><p>设置一个对象user:1 值用一个json字符串来表示,但是这样不能直接获取到name属性,所以要单独赋值</p><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">set</span> <span class="hljs-keyword">user</span>:<span class="hljs-number">1</span> {name:zhangsan,age:<span class="hljs-number">19</span>}<br></code></pre></td></tr></table></figure><blockquote><p>这里的key的设计:<code>user:{id}:{filed}</code>非常巧妙(这样就能直接获取一个user对象中的各种属性,类似一种层次结构)</p></blockquote><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> mset <span class="hljs-keyword">user</span>:<span class="hljs-number">1</span>:name zhangsan <span class="hljs-keyword">user</span>:<span class="hljs-number">1</span>:age <span class="hljs-number">19</span><br>OK<br><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> mget <span class="hljs-keyword">user</span>:<span class="hljs-number">1</span>:name <span class="hljs-keyword">user</span>:<span class="hljs-number">1</span>:age<br><span class="hljs-number">1</span>) "zhangsan"<br><span class="hljs-number">2</span>) "19"<br></code></pre></td></tr></table></figure><hr><blockquote><p>getset方法<code>getset [key] [value]</code>获取key原来对应的value,设置一个新的值,返回原来被替换的值,如果本来就不存在,返回nil</p></blockquote><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> getset name ikun<br>(nil)<br><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> getset name kunkun<br>"ikun"<br><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> <span class="hljs-keyword">get</span> name<br>"kunkun"<br></code></pre></td></tr></table></figure><hr><h3 id="List"><a href="#List" class="headerlink" title="List"></a>List</h3><p>==列表==</p><p>插入值</p><ol><li>从左边插入(头插法)<code>lpush [key] [value ...]</code></li><li>从右边插入(尾插法)<code>rpush [key] [value ...]</code></li><li>从左边开始遍历<code>lrange [start] [end]</code></li></ol><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> lpush list <span class="hljs-number">1</span> <span class="hljs-number">2</span> <span class="hljs-number">3</span> <br>(<span class="hljs-type">integer</span>) <span class="hljs-number">3</span><br><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> lrange list <span class="hljs-number">0</span> <span class="hljs-number">-1</span> # <span class="hljs-number">-1</span>代表尾部,所以是遍历全部<br><span class="hljs-number">1</span>) "3"<br><span class="hljs-number">2</span>) "2"<br><span class="hljs-number">3</span>) "1"<br><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> rpush list <span class="hljs-number">4</span> <span class="hljs-number">5</span> <span class="hljs-number">6</span><br>(<span class="hljs-type">integer</span>) <span class="hljs-number">6</span><br><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> lrange list <span class="hljs-number">0</span> <span class="hljs-number">-1</span><br><span class="hljs-number">1</span>) "3"<br><span class="hljs-number">2</span>) "2"<br><span class="hljs-number">3</span>) "1"<br><span class="hljs-number">4</span>) "4"<br><span class="hljs-number">5</span>) "5"<br><span class="hljs-number">6</span>) "6"<br></code></pre></td></tr></table></figure><p>移除元素</p><ol><li>从左边开始移除<code>lpop [key] [count个数] </code></li><li>从右边开始移除<code>rpop [key] [count个数] </code></li></ol><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> lrange list <span class="hljs-number">0</span> <span class="hljs-number">-1</span><br><span class="hljs-number">1</span>) "3"<br><span class="hljs-number">2</span>) "2"<br><span class="hljs-number">3</span>) "1"<br><span class="hljs-number">4</span>) "4"<br><span class="hljs-number">5</span>) "5"<br><span class="hljs-number">6</span>) "6"<br><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> lpop list <span class="hljs-number">2</span><br><span class="hljs-number">1</span>) "3"<br><span class="hljs-number">2</span>) "2"<br><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> rpop list <span class="hljs-number">3</span><br><span class="hljs-number">1</span>) "6"<br><span class="hljs-number">2</span>) "5"<br><span class="hljs-number">3</span>) "4"<br><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> lrange list <span class="hljs-number">0</span> <span class="hljs-number">-1</span><br><span class="hljs-number">1</span>) "1"<br></code></pre></td></tr></table></figure><p>获取列表中指定索引的值<code>lindex [key] [index]</code></p><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> lrange lst <span class="hljs-number">0</span> <span class="hljs-number">-1</span><br><span class="hljs-number">1</span>) "5"<br><span class="hljs-number">2</span>) "4"<br><span class="hljs-number">3</span>) "3"<br><span class="hljs-number">4</span>) "2"<br><span class="hljs-number">5</span>) "1"<br><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> lindex lst <span class="hljs-number">1</span><br>"4"<br></code></pre></td></tr></table></figure><p>获取列表长度<code>llen [key]</code></p><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> lrange lst <span class="hljs-number">0</span> <span class="hljs-number">-1</span><br><span class="hljs-number">1</span>) "5"<br><span class="hljs-number">2</span>) "4"<br><span class="hljs-number">3</span>) "3"<br><span class="hljs-number">4</span>) "2"<br><span class="hljs-number">5</span>) "1"<br><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> llen lst<br>(<span class="hljs-type">integer</span>) <span class="hljs-number">5</span><br></code></pre></td></tr></table></figure><blockquote><p>删除列表中n个指定元素(精确匹配)<code>lrem [key] 3 2</code> >>> 删除list列表中从左往右数的前3个2,返回值为成功删除的个数</p></blockquote><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> lrange list <span class="hljs-number">0</span> <span class="hljs-number">-1</span><br><span class="hljs-number">1</span>) "2" <br><span class="hljs-number">2</span>) "10"<br><span class="hljs-number">3</span>) "9"<br><span class="hljs-number">4</span>) "6"<br><span class="hljs-number">5</span>) "2"<br><span class="hljs-number">6</span>) "2"<br><span class="hljs-number">7</span>) "2"<br><span class="hljs-number">8</span>) "1"<br><span class="hljs-number">9</span>) "2"<br><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> lrem list <span class="hljs-number">3</span> <span class="hljs-number">2</span><br>(<span class="hljs-type">integer</span>) <span class="hljs-number">3</span><br><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> lrange list <span class="hljs-number">0</span> <span class="hljs-number">-1</span><br><span class="hljs-number">1</span>) "10"<br><span class="hljs-number">2</span>) "9"<br><span class="hljs-number">3</span>) "6"<br><span class="hljs-number">4</span>) "2"<br><span class="hljs-number">5</span>) "1"<br><span class="hljs-number">6</span>) "2"<br></code></pre></td></tr></table></figure><p>截取列表指定索引之间的元素<code>ltrim [key] [start] [stop]</code> 截取[start,stop],==这是在原数组上进行修改!==</p><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> lrange list <span class="hljs-number">0</span> <span class="hljs-number">-1</span><br><span class="hljs-number">1</span>) "a"<br><span class="hljs-number">2</span>) "b"<br><span class="hljs-number">3</span>) "c"<br><span class="hljs-number">4</span>) "d"<br><span class="hljs-number">5</span>) "e"<br><span class="hljs-number">6</span>) "ok"<br><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> ltrim list <span class="hljs-number">0</span> <span class="hljs-number">2</span><br>OK<br><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> lrange list <span class="hljs-number">0</span> <span class="hljs-number">-1</span><br><span class="hljs-number">1</span>) "a"<br><span class="hljs-number">2</span>) "b"<br><span class="hljs-number">3</span>) "c"<br></code></pre></td></tr></table></figure><blockquote><p>移除arr1中最右边的元素并从左边插入arr2<code>rpoplpush [source] [destination]</code></p></blockquote><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> lrange arr1 <span class="hljs-number">0</span> <span class="hljs-number">-1</span><br><span class="hljs-number">1</span>) "1"<br><span class="hljs-number">2</span>) "2"<br><span class="hljs-number">3</span>) "3"<br><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> lrange arr2 <span class="hljs-number">0</span> <span class="hljs-number">-1</span><br><span class="hljs-number">1</span>) "a"<br><span class="hljs-number">2</span>) "b"<br><span class="hljs-number">3</span>) "c"<br><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> rpoplpush arr1 arr2<br>"3"<br><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> lrange arr1 <span class="hljs-number">0</span> <span class="hljs-number">-1</span><br><span class="hljs-number">1</span>) "1"<br><span class="hljs-number">2</span>) "2"<br><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> lrange arr2 <span class="hljs-number">0</span> <span class="hljs-number">-1</span><br><span class="hljs-number">1</span>) "3"<br><span class="hljs-number">2</span>) "a"<br><span class="hljs-number">3</span>) "b"<br><span class="hljs-number">4</span>) "c"<br></code></pre></td></tr></table></figure><p>根据索引设置列表中的元素<code>lset [key] [index] [value]</code>,相当于更新操作</p><p>列表不存在或索引超出范围(可以使用负数表示逆序索引)都会报错</p><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> lrange list <span class="hljs-number">0</span> <span class="hljs-number">-1</span><br><span class="hljs-number">1</span>) "v1"<br><span class="hljs-number">2</span>) "v2"<br><span class="hljs-number">3</span>) "v3"<br><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> lset list <span class="hljs-number">1</span> <span class="hljs-number">666</span><br>OK<br><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> lrange list <span class="hljs-number">0</span> <span class="hljs-number">-1</span><br><span class="hljs-number">1</span>) "v1"<br><span class="hljs-number">2</span>) "666"<br><span class="hljs-number">3</span>) "v3"<br></code></pre></td></tr></table></figure><p>在列表中从左往右数第1个pivot前面/后面插入指定元素</p><p><code>linsert [key] before|after [pivot] [element]</code></p><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> lrange list <span class="hljs-number">0</span> <span class="hljs-number">-1</span><br><span class="hljs-number">1</span>) "kun"<br><span class="hljs-number">2</span>) "cxk"<br><span class="hljs-number">3</span>) "clb"<br><span class="hljs-number">4</span>) "kun"<br><span class="hljs-number">5</span>) "nb"<br><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> linsert list before kun <span class="hljs-number">666</span> # 从左往右第一个kun前插入<span class="hljs-number">666</span><br>(<span class="hljs-type">integer</span>) <span class="hljs-number">6</span><br><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> lrange list <span class="hljs-number">0</span> <span class="hljs-number">-1</span><br><span class="hljs-number">1</span>) "666"<br><span class="hljs-number">2</span>) "kun"<br><span class="hljs-number">3</span>) "cxk"<br><span class="hljs-number">4</span>) "clb"<br><span class="hljs-number">5</span>) "kun"<br><span class="hljs-number">6</span>) "nb"<br><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> linsert list after nb ctrl # 从左往右第一个nb后面插入ctrl<br>(<span class="hljs-type">integer</span>) <span class="hljs-number">7</span><br><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> lrange list <span class="hljs-number">0</span> <span class="hljs-number">-1</span><br><span class="hljs-number">1</span>) "666"<br><span class="hljs-number">2</span>) "kun"<br><span class="hljs-number">3</span>) "cxk"<br><span class="hljs-number">4</span>) "clb"<br><span class="hljs-number">5</span>) "kun"<br><span class="hljs-number">6</span>) "nb"<br><span class="hljs-number">7</span>) "ctrl"<br></code></pre></td></tr></table></figure><blockquote><p>小结:List实际上是一个==双向链表==结构,在两边crud快,中间元素crud相对较慢</p></blockquote><hr><h3 id="Set"><a href="#Set" class="headerlink" title="Set"></a>Set</h3><p>==集合,不能有重复元素==</p><p>set集合添加元素<code>sadd [key] [member ...]</code>,添加重复元素无效</p><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> sadd <span class="hljs-keyword">set</span> hello ikun cxk ctrl hello<br>(<span class="hljs-type">integer</span>) <span class="hljs-number">4</span> # 因为<span class="hljs-keyword">set</span>不允许有重复元素,所以最后一个hello未被添加<br></code></pre></td></tr></table></figure><p>set查看所有元素<code>smembers [key]</code></p><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> smembers <span class="hljs-keyword">set</span><br><span class="hljs-number">1</span>) "ctrl"<br><span class="hljs-number">2</span>) "ikun"<br><span class="hljs-number">3</span>) "hello"<br><span class="hljs-number">4</span>) "cxk"<br></code></pre></td></tr></table></figure><p>set是否包含元素<code>sismember [key] [member]</code></p><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> sismember <span class="hljs-keyword">set</span> ikun<br>(<span class="hljs-type">integer</span>) <span class="hljs-number">1</span><br></code></pre></td></tr></table></figure><p>获取set中元素个数<code>scarg set</code></p><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> scard <span class="hljs-keyword">set</span><br>(<span class="hljs-type">integer</span>) <span class="hljs-number">4</span><br></code></pre></td></tr></table></figure><p>移除set中指定元素<code>srem [key] [member]</code></p><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> srem <span class="hljs-keyword">set</span> ikun<br>(<span class="hljs-type">integer</span>) <span class="hljs-number">1</span><br></code></pre></td></tr></table></figure><p>从set中随机移除几个元素<code>spop [key] [count:个数超出上限则全部移除]</code></p><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> spop <span class="hljs-keyword">set</span> <span class="hljs-number">1</span><br><span class="hljs-number">1</span>) "5"<br><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> spop <span class="hljs-keyword">set</span> <span class="hljs-number">2</span><br><span class="hljs-number">1</span>) "4"<br><span class="hljs-number">2</span>) "1"<br></code></pre></td></tr></table></figure><p>从set中随机取出几个元素<code>srandmember [key] [count:个数超出上限则取出全部]</code></p><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> srandmember <span class="hljs-keyword">set</span> <span class="hljs-number">1</span><br><span class="hljs-number">1</span>) "1"<br><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> srandmember <span class="hljs-keyword">set</span> <span class="hljs-number">1</span><br><span class="hljs-number">1</span>) "4"<br><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> srandmember <span class="hljs-keyword">set</span> <span class="hljs-number">2</span><br><span class="hljs-number">1</span>) "1"<br><span class="hljs-number">2</span>) "4"<br><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> srandmember <span class="hljs-keyword">set</span> <span class="hljs-number">3</span><br><span class="hljs-number">1</span>) "2"<br><span class="hljs-number">2</span>) "1"<br><span class="hljs-number">3</span>) "5"<br></code></pre></td></tr></table></figure><p>从一个集合中移动指定元素到另外一个集合<code>smove [source] [destination] member</code></p><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> sadd s1 ikun cxk<br>(<span class="hljs-type">integer</span>) <span class="hljs-number">2</span><br><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> sadd s2 ctrl rap<br>(<span class="hljs-type">integer</span>) <span class="hljs-number">2</span><br><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> smove s1 s2 ikun<br>(<span class="hljs-type">integer</span>) <span class="hljs-number">1</span><br><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> SMEMBERS s2<br><span class="hljs-number">1</span>) "ctrl"<br><span class="hljs-number">2</span>) "ikun"<br><span class="hljs-number">3</span>) "rap"<br></code></pre></td></tr></table></figure><p>差集、交集、并集<code>sdiff|sinter|sunion [set1] [set2...]</code></p><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> sadd s1 a b c d<br>(<span class="hljs-type">integer</span>) <span class="hljs-number">4</span><br><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> sadd s2 c d e f<br>(<span class="hljs-type">integer</span>) <span class="hljs-number">4</span><br><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> sdiff s1 s2 # 差集<br><span class="hljs-number">1</span>) "a"<br><span class="hljs-number">2</span>) "b"<br><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> sdiff s2 s1 # 差集<br><span class="hljs-number">1</span>) "e"<br><span class="hljs-number">2</span>) "f"<br><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> sinter s1 s2 # 交集<br><span class="hljs-number">1</span>) "d"<br><span class="hljs-number">2</span>) "c"<br><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> sunion s1 s2 # 并集<br><span class="hljs-number">1</span>) "f"<br><span class="hljs-number">2</span>) "b"<br><span class="hljs-number">3</span>) "a"<br><span class="hljs-number">4</span>) "e"<br><span class="hljs-number">5</span>) "d"<br><span class="hljs-number">6</span>) "c"<br></code></pre></td></tr></table></figure><hr><h3 id="Hash"><a href="#Hash" class="headerlink" title="Hash"></a>Hash</h3><p>==键值对,相当于map==</p><p>设置<code>hset|hmset [hash] [key value ...]</code>hset从redis4开始支持批量设置</p><p>获取</p><ol><li>根据key获取一个 值<code>hget [hash] [key]</code></li><li>根据多个key获取多个值<code>hmget [hash] [key ...]</code></li><li>获取所有键和值<code>hgetall [hash]</code></li></ol><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> hset hash k1 v1 k2 v2 k3 v3<br>(<span class="hljs-type">integer</span>) <span class="hljs-number">3</span><br><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> hget hash k1<br>"v1"<br><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> hgetall hash<br><span class="hljs-number">1</span>) "k1"<br><span class="hljs-number">2</span>) "v1"<br><span class="hljs-number">3</span>) "k2"<br><span class="hljs-number">4</span>) "v2"<br><span class="hljs-number">5</span>) "k3"<br><span class="hljs-number">6</span>) "v3"<br></code></pre></td></tr></table></figure><p>根据键删除k-v<code>hdel [hash] [key ...]</code></p><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> hdel hash k1<br>(<span class="hljs-type">integer</span>) <span class="hljs-number">1</span><br></code></pre></td></tr></table></figure><p>获取键值对个数<code>hlen [hash]</code></p><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> hlen hash<br>(<span class="hljs-type">integer</span>) <span class="hljs-number">2</span><br></code></pre></td></tr></table></figure><p>获取所有的key<code>hkeys [hash]</code></p><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> hkeys hash<br><span class="hljs-number">1</span>) "k2"<br><span class="hljs-number">2</span>) "k3"<br></code></pre></td></tr></table></figure><p>获取所有的value<code>hvals [hash]</code></p><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> hvals hash<br><span class="hljs-number">1</span>) "v2"<br><span class="hljs-number">2</span>) "v3"<br></code></pre></td></tr></table></figure><p><code>hsetnx</code> <code>hincrby</code>用法同string</p><blockquote><p>hash更多应用于存储用户信息!</p></blockquote><hr><h3 id="Zset"><a href="#Zset" class="headerlink" title="Zset"></a>Zset</h3><p>==有序集合==,在set基础上增加了一个score,默认根据score升序排序</p><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> zadd <span class="hljs-keyword">set</span> <span class="hljs-number">-6</span> ikun <span class="hljs-number">66</span> kun <span class="hljs-number">6</span> kunkun <span class="hljs-number">0</span> ctrl<br>(<span class="hljs-type">integer</span>) <span class="hljs-number">4</span><br><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> zrange <span class="hljs-keyword">set</span> <span class="hljs-number">0</span> <span class="hljs-number">-1</span><br><span class="hljs-number">1</span>) "ikun"<br><span class="hljs-number">2</span>) "ctrl"<br><span class="hljs-number">3</span>) "kunkun"<br><span class="hljs-number">4</span>) "kun"<br></code></pre></td></tr></table></figure><p>获取score在-inf(负无穷,可以改为具体数值)到+inf之间元素并带有score(升序)</p><p><code>zrangebyscore [set] [-inf] [+inf] (withscores可以不用)</code></p><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> zrangebyscore <span class="hljs-keyword">set</span> <span class="hljs-operator">-</span>inf <span class="hljs-operator">+</span>inf withscores<br><span class="hljs-number">1</span>) "ikun"<br><span class="hljs-number">2</span>) "-6"<br><span class="hljs-number">3</span>) "ctrl"<br><span class="hljs-number">4</span>) "0"<br><span class="hljs-number">5</span>) "kunkun"<br><span class="hljs-number">6</span>) "6"<br><span class="hljs-number">7</span>) "kun"<br><span class="hljs-number">8</span>) "66"<br></code></pre></td></tr></table></figure><p>根据score逆序获取,range前面加<code>rev</code></p><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> zrevrange <span class="hljs-keyword">set</span> <span class="hljs-number">0</span> <span class="hljs-number">-1</span><br><span class="hljs-number">1</span>) "kun"<br><span class="hljs-number">2</span>) "kunkun"<br><span class="hljs-number">3</span>) "ctrl"<br><span class="hljs-number">4</span>) "ikun"<br><span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>:<span class="hljs-number">6379</span><span class="hljs-operator">></span> ZREVRANGEBYSCORE <span class="hljs-keyword">set</span> <span class="hljs-operator">+</span>inf <span class="hljs-operator">-</span>inf withscores<br><span class="hljs-number">1</span>) "kun"<br><span class="hljs-number">2</span>) "66"<br><span class="hljs-number">3</span>) "kunkun"<br><span class="hljs-number">4</span>) "6"<br><span class="hljs-number">5</span>) "ctrl"<br><span class="hljs-number">6</span>) "0"<br><span class="hljs-number">7</span>) "ikun"<br><span class="hljs-number">8</span>) "-6"<br></code></pre></td></tr></table></figure><blockquote><p>获取有序集合元素个数<code>zcard [set]</code></p></blockquote><blockquote><p>统计score在区间内的元素个数<code>zcount [set] [min] [max]</code>包含两端</p></blockquote><hr><h2 id="三种特殊数据类型"><a href="#三种特殊数据类型" class="headerlink" title="三种特殊数据类型"></a>三种特殊数据类型</h2><h3 id="Geospatial-地理位置"><a href="#Geospatial-地理位置" class="headerlink" title="Geospatial(地理位置)"></a><a href="http://www.redis.cn/commands/geoadd.html">Geospatial(地理位置)</a></h3><blockquote><p>==<strong>geoadd</strong>== 添加一个或多个城市信息<code>geoadd [key] [经度 纬度 member ...]</code></p><p>:rocket: ==<strong>这里的key实际上是zset(有序集合),zset方法可以操作这里的key</strong>==</p></blockquote><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash">127.0.0.1:6379> geoadd china:city 121.47 31.23 shanghai<br>(<span class="hljs-built_in">integer</span>) 1<br>127.0.0.1:6379> geoadd china:city 106.50 29.53 chongqing<br>(<span class="hljs-built_in">integer</span>) 1<br></code></pre></td></tr></table></figure><blockquote><p> 查询所有位置信息</p></blockquote><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash">127.0.0.1:6379> zrange china:city 0 -1<br>1) <span class="hljs-string">"chongqing"</span><br>2) <span class="hljs-string">"xian"</span><br>3) <span class="hljs-string">"shenzhen"</span><br>4) <span class="hljs-string">"hangzhou"</span><br>5) <span class="hljs-string">"shanghai"</span><br>6) <span class="hljs-string">"beijing"</span><br></code></pre></td></tr></table></figure><blockquote><p>==<strong>geopos</strong>== 查询一个或多个城市经度和纬度<code>geopos [key] [member ...]</code></p></blockquote><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash">127.0.0.1:6379> geopos china:city beijing shanghai<br>1) 1) <span class="hljs-string">"116.39999896287918091"</span><br> 2) <span class="hljs-string">"39.90000009167092543"</span><br>2) 1) <span class="hljs-string">"121.47000163793563843"</span><br> 2) <span class="hljs-string">"31.22999903975783553"</span><br></code></pre></td></tr></table></figure><blockquote><p>==<strong>geodist</strong>==计算两地之间的距离<code>geodist [key] [member1] [member2] [unit:单位,默认为米]</code></p></blockquote><ul><li><strong>m</strong> 表示单位为米(默认使用)</li><li><strong>km</strong> 表示单位为千米</li><li><strong>mi</strong> 表示单位为英里</li><li><strong>ft</strong> 表示单位为英尺</li></ul><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash">127.0.0.1:6379> geodist china:city beijing shanghai<br><span class="hljs-string">"1067378.7564"</span><br>127.0.0.1:6379> geodist china:city beijing shanghai km<br><span class="hljs-string">"1067.3788"</span><br></code></pre></td></tr></table></figure><blockquote><p>==<strong>georadius</strong>== 以指定点为半径,查找位置在方圆半径内的元素(元素来源于key),==返回结果按照距离升序排序==</p><p><code>georadius [key] [经度 纬度 距离 单位] [withcoord:输出经纬度] [withdist:输出距离] [count 个数:指定查找的最多个数] </code></p></blockquote><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># 查找以经纬度坐标(110,30)为中心,方圆500km内的城市信息,只找最近的两个(只找出在china:key中录入的城市)</span><br>127.0.0.1:6379> georadius china:city 110 30 500 km withcoord withdist count 2<br>1) 1) <span class="hljs-string">"chongqing"</span><br> 2) <span class="hljs-string">"341.9374"</span><br> 3) 1) <span class="hljs-string">"106.49999767541885376"</span><br> 2) <span class="hljs-string">"29.52999957900659211"</span><br>2) 1) <span class="hljs-string">"xian"</span><br> 2) <span class="hljs-string">"483.8340"</span><br> 3) 1) <span class="hljs-string">"108.96000176668167114"</span><br> 2) <span class="hljs-string">"34.25999964418929977"</span><br></code></pre></td></tr></table></figure><blockquote><p><strong>==georadiusmember==</strong> 根据已存在点作为中心查找范围内的元素,用法同上</p><p><code>georadiusbymember [key] [member] 200 km</code></p></blockquote><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash">127.0.0.1:6379> georadiusbymember china:city shanghai 200 km<br>1) <span class="hljs-string">"hangzhou"</span><br>2) <span class="hljs-string">"shanghai"</span><br></code></pre></td></tr></table></figure><blockquote><p>geohash返回一个位置的geohash字符串(11位字符) <code>geohash [key] [mamber ...]</code></p></blockquote><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash">127.0.0.1:6379> geohash china:city beijing shanghai<br>1) <span class="hljs-string">"wx4fbxxfke0"</span><br>2) <span class="hljs-string">"wtw3sj5zbj0"</span><br></code></pre></td></tr></table></figure><hr><h3 id="Hyperloglog"><a href="#Hyperloglog" class="headerlink" title="Hyperloglog"></a>Hyperloglog</h3><p>==元素不重复,类似于set集合,但是占用内存更小,不过数据量大的时候有一定误差==</p><blockquote><p>添加元素 <code>pfadd [key] [value ...]</code></p><p>统计不重复元素个数 <code>pfcount [key]</code></p><p>合并多个集合 <code>pfmege [destination] [source ...]</code></p></blockquote><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash">127.0.0.1:6379> pfadd set1 1 2 3 4 5<br>(<span class="hljs-built_in">integer</span>) 1<br>127.0.0.1:6379> pfadd set2 4 5 6 7 8<br>(<span class="hljs-built_in">integer</span>) 1<br>127.0.0.1:6379> pfmerge set3 set1 set2<br>OK<br>127.0.0.1:6379> pfcount set3<br>(<span class="hljs-built_in">integer</span>) 8<br></code></pre></td></tr></table></figure><hr><h3 id="Bitmap-位图"><a href="#Bitmap-位图" class="headerlink" title="Bitmap(位图)"></a>Bitmap(位图)</h3><p>设置一周打卡信息(1 打卡,0 未打卡)</p><blockquote><p><code>setbit [key] [offset:int数字,此处表示周几] [status 0/1]</code></p></blockquote><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash">127.0.0.1:6379> setbit sign 1 1<br>(<span class="hljs-built_in">integer</span>) 0<br>127.0.0.1:6379> setbit sign 2 0<br>(<span class="hljs-built_in">integer</span>) 0<br>127.0.0.1:6379> setbit sign 3 1<br>(<span class="hljs-built_in">integer</span>) 0<br>127.0.0.1:6379> setbit sign 4 1<br>(<span class="hljs-built_in">integer</span>) 0<br>127.0.0.1:6379> setbit sign 5 0<br>(<span class="hljs-built_in">integer</span>) 0<br>127.0.0.1:6379> setbit sign 6 1<br>(<span class="hljs-built_in">integer</span>) 0<br>127.0.0.1:6379> setbit sign 7 0<br>(<span class="hljs-built_in">integer</span>) 0<br></code></pre></td></tr></table></figure><blockquote><p>获取某天状态 <code>getbit [key] [offset]</code></p></blockquote><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash">127.0.0.1:6379> getbit sign 4<br>(<span class="hljs-built_in">integer</span>) 1<br></code></pre></td></tr></table></figure><blockquote><p>统计状态为1的个数 <code>bitcount [key]</code></p></blockquote><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash">127.0.0.1:6379> bitcount sign<br>(<span class="hljs-built_in">integer</span>) 4<br></code></pre></td></tr></table></figure><hr><h2 id="事务"><a href="#事务" class="headerlink" title="事务"></a>事务</h2><p>redis事务本质:一组命令的集合,有一致性、顺序性、排他性,</p><p>==redis事务没有隔离级别的概念==,所有命令在事务中并没有直接被执行,只有发起执行命令的时候才会执行</p><p><strong>==redis单条命令保证原子性,但是事务不保证原子性==</strong></p><blockquote><p>redis的事务</p></blockquote><ul><li>开始事务(<code>multi</code> 开启事务之后输入的命令不会直接执行,而是进入命令队列,直到事务被执行,所有的命令才按顺序执行)</li><li>命令入队(<code>...</code>)</li><li>执行事务(<code>exec</code>)</li></ul><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash">127.0.0.1:6379> multi<span class="hljs-comment"># 开启事务</span><br>OK<br><span class="hljs-comment"># 命令入队</span><br>127.0.0.1:6379(TX)> mset k1 v1 k2 v2 k3 v3<br>QUEUED<br>127.0.0.1:6379(TX)> mget k1 k2<br>QUEUED<br>127.0.0.1:6379(TX)> <span class="hljs-built_in">exec</span><span class="hljs-comment"># 执行事务</span><br>1) OK<br>2) 1) <span class="hljs-string">"v1"</span><br> 2) <span class="hljs-string">"v2"</span><br></code></pre></td></tr></table></figure><blockquote><p>放弃事务(<code>discard</code>命令入队过程中使用可以放弃事务)</p></blockquote><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash">127.0.0.1:6379> multi<br>OK<br>127.0.0.1:6379(TX)> <span class="hljs-built_in">set</span> k1 1231<br>QUEUED<br>127.0.0.1:6379(TX)> discard<br>OK<br></code></pre></td></tr></table></figure><blockquote><p>==编译型异常==(命令有问题,过不了编译),事务中所有命令不执行!</p></blockquote><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash">127.0.0.1:6379> multi<br>OK<br>127.0.0.1:6379(TX)> <span class="hljs-built_in">set</span> k1 v1<br>QUEUED<br>127.0.0.1:6379(TX)> setttttt k2 v2<span class="hljs-comment"># 不存在的命令导致编译异常</span><br>(error) ERR unknown <span class="hljs-built_in">command</span> <span class="hljs-string">'setttttt'</span>, with args beginning with: <span class="hljs-string">'k2'</span> <span class="hljs-string">'v2'</span> <br>127.0.0.1:6379(TX)> <span class="hljs-built_in">set</span> k3 v3<br>QUEUED<br>127.0.0.1:6379(TX)> <span class="hljs-built_in">exec</span><br>(error) EXECABORT Transaction discarded because of previous errors.# 之前的编译异常导致整个事务被放弃<br></code></pre></td></tr></table></figure><blockquote><p>==运行时异常==(除0错误,索引越界…)错误命令不执行,其他命令正常执行,==不能保证原子性==</p></blockquote><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash">127.0.0.1:6379> multi<br>OK<br>127.0.0.1:6379(TX)> <span class="hljs-built_in">set</span> k1 v1<br>QUEUED<br>127.0.0.1:6379(TX)> incr k1<span class="hljs-comment"># v1不能转换为integer类型,运行时异常,编译没问题</span><br>QUEUED<br>127.0.0.1:6379(TX)> get k1<br>QUEUED<br>127.0.0.1:6379(TX)> <span class="hljs-built_in">exec</span><br>1) OK<br>2) (error) ERR value is not an <span class="hljs-built_in">integer</span> or out of range<br>3) <span class="hljs-string">"v1"</span><br></code></pre></td></tr></table></figure><blockquote><p>==<strong>悲观锁</strong>==:很悲观,认为什么时候都会出问题,无论做什么都会加锁</p></blockquote><blockquote><p>==<strong>乐观锁</strong>==:</p><ul><li>很乐观,认为什么时候都不会出现问题,所以不会上锁,更新数据的时候去判断一下,在此期间是否有人修改过这个数据</li><li>获取version</li><li>更新的时候比较version</li></ul></blockquote><p>使用watch当做redis的乐观锁操作 <code>watch [key]</code></p><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># 多线程并发修改</span><br><span class="hljs-comment"># --------------线程1--------------</span><br>127.0.0.1:6379> watch money <span class="hljs-comment"># 监视,想当于加乐观锁</span><br>OK<br>127.0.0.1:6379> multi<br>OK<br>127.0.0.1:6379(TX)> incrby money 666<br>QUEUED<br><span class="hljs-comment"># exec还未执行时,线程2开始执行</span><br>127.0.0.1:6379(TX)> <span class="hljs-built_in">exec</span><br>(nil)<span class="hljs-comment"># 执行失败</span><br><br><span class="hljs-comment"># --------------线程2--------------</span><br>127.0.0.1:6379> <span class="hljs-built_in">set</span> money -6<span class="hljs-comment"># 修改了线程1中加了乐观锁的money</span><br>OK<br></code></pre></td></tr></table></figure><p>执行失败后使用<code>unwatch</code>解锁后再重新监视并执行</p><hr><h2 id="Jedis"><a href="#Jedis" class="headerlink" title="Jedis"></a>Jedis</h2><p>==官方推荐==的java连接开发工具,使用java操作redis的中间件,如果要使用java操作redis,那么一定要对jedis十分的熟悉!</p><ol><li>导入jedis依赖</li></ol><figure class="highlight xml"><table><tr><td class="code"><pre><code class="hljs xml"><span class="hljs-comment"><!--导入jedis依赖--></span><br><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>redis.clients<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>jedis<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">version</span>></span>4.4.3<span class="hljs-tag"></<span class="hljs-name">version</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br></code></pre></td></tr></table></figure><ol start="2"><li>创建jedis对象</li></ol><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//创建一个Jedis对象并认证</span><br>jedis = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Jedis</span>(<span class="hljs-string">"192.168.66.6"</span>, <span class="hljs-number">6379</span>);<br>jedis.auth(<span class="hljs-string">"123456"</span>);<br></code></pre></td></tr></table></figure><ol start="3"><li>使用jedis对象调用API,jedis的API基本都是对应redis中的命令</li></ol><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java">jedis.flushDB();<br>System.out.println(jedis.set(<span class="hljs-string">"name"</span>, <span class="hljs-string">"clb"</span>));<br>System.out.println(jedis.set(<span class="hljs-string">"password"</span>, <span class="hljs-string">"123456"</span>));<br>System.out.println(jedis.exists(<span class="hljs-string">"name"</span>));<br></code></pre></td></tr></table></figure><blockquote><p>事务操作</p></blockquote><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java">#事务<br><span class="hljs-meta">@Test</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">testTX</span><span class="hljs-params">()</span> {<br> <span class="hljs-type">JSONObject</span> <span class="hljs-variable">json</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">JSONObject</span>();<br> json.put(<span class="hljs-string">"name"</span>, <span class="hljs-string">"cxk"</span>);<br> json.put(<span class="hljs-string">"age"</span>, <span class="hljs-string">"34"</span>);<br> <span class="hljs-type">String</span> <span class="hljs-variable">result</span> <span class="hljs-operator">=</span> json.toJSONString();<br> jedis.flushDB();<br><br> jedis.watch(result);<br> <span class="hljs-comment">//开启事务</span><br> <span class="hljs-type">Transaction</span> <span class="hljs-variable">multi</span> <span class="hljs-operator">=</span> jedis.multi();<br> <span class="hljs-keyword">try</span> {<br> multi.set(<span class="hljs-string">"user1"</span>, result);<br> <span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">1</span> / <span class="hljs-number">0</span>; <span class="hljs-comment">// 制造一个运行时异常</span><br> multi.set(<span class="hljs-string">"user2"</span>, result);<br> multi.exec(); <span class="hljs-comment">//执行事务</span><br> } <span class="hljs-keyword">catch</span> (Exception e) {<br> multi.discard();<span class="hljs-comment">//放弃事务</span><br> <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">RuntimeException</span>(e);<br> } <span class="hljs-keyword">finally</span> {<br> System.out.println(jedis.get(<span class="hljs-string">"user1"</span>));<br> System.out.println(jedis.get(<span class="hljs-string">"user2"</span>));<br> jedis.close(); <span class="hljs-comment">//关闭连接</span><br> }<br>}<br></code></pre></td></tr></table></figure><hr><h2 id="SpringBoot整合"><a href="#SpringBoot整合" class="headerlink" title="SpringBoot整合"></a>SpringBoot整合</h2><p>SpringBoot2.x之后,原来使用的jedis被替换成了lettuce</p><p>==jedis==:采用直连,多个线程池操作的话,是不安全的,如果想要避免,就要使用<code>jedis pool</code>连接池,更像<code>BIO</code>(阻塞IO)模式</p><p>==<strong>lettuce</strong>==:采用<code>netty</code>,实例可以在多个线程中进行共享,不存在线程不安全的情况!可以减少线程数据,更像<code>NIO</code>(非阻塞IO)模式</p><ol><li>导入redis依赖</li></ol><figure class="highlight xml"><table><tr><td class="code"><pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.springframework.boot<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>spring-boot-starter-data-redis<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br></code></pre></td></tr></table></figure><ol start="2"><li>配置文件配置redis</li></ol><figure class="highlight yaml"><table><tr><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">spring:</span><br> <span class="hljs-attr">data:</span><br> <span class="hljs-attr">redis:</span><br> <span class="hljs-attr">host:</span> <span class="hljs-number">192.168</span><span class="hljs-number">.66</span><span class="hljs-number">.6</span><br> <span class="hljs-attr">port:</span> <span class="hljs-number">6379</span><br> <span class="hljs-attr">password:</span> <span class="hljs-number">123456</span><br></code></pre></td></tr></table></figure><ol start="3"><li>测试</li></ol><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@SpringBootTest</span><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">Redis02SpringbootApplicationTests</span> {<br> <span class="hljs-meta">@Autowired</span><br> <span class="hljs-keyword">private</span> RedisTemplate redisTemplate;<br> <span class="hljs-meta">@Test</span><br> <span class="hljs-keyword">void</span> <span class="hljs-title function_">contextLoads</span><span class="hljs-params">()</span> {<br> <span class="hljs-comment">//方法和redis命令一一对应</span><br> <span class="hljs-comment">//opsForValue() 操作String</span><br> <span class="hljs-comment">//opsForList() 操作List</span><br> <span class="hljs-comment">//......</span><br> redisTemplate.opsForValue().set(<span class="hljs-string">"name"</span>, <span class="hljs-string">"坤坤"</span>);<br> System.out.println(redisTemplate.opsForValue().get(<span class="hljs-string">"name"</span>));<br> }<br>}<br></code></pre></td></tr></table></figure><p>redis中各种数据类型对应的操作方法</p><img src="https://s2.loli.net/2024/09/27/Iy2QGasZbTcLlYW.jpg" alt="1693648470356" /><ol start="4"><li>==序列化== :<ul><li>在Spring Boot中,序列化是指==将对象转换为可以存储或传输的字节序列的过程==。这可以通过将对象转换为==JSON==或==XML==格式来实现。序列化允许在存储或传输对象时==减少内存使用==,并确保在在不同系统之间交换数据时能够正确表示对象</li><li>实体类一般都要序列化,<code>implements Serializable</code>实现序列化接口就可以了,默认使用的是JDK序列化,也可以使用其他json工具实现,因为默认的jdk序列化存储中文到redis会出现乱码</li></ul></li></ol><hr>]]></content>
<categories>
<category> 数据库 </category>
</categories>
<tags>
<tag> redis </tag>
<tag> 数据库 </tag>
<tag> NOSQL </tag>
</tags>
</entry>
<entry>
<title>mybatis-plus</title>
<link href="/2023/08/31/mybatis-plus/"/>
<url>/2023/08/31/mybatis-plus/</url>
<content type="html"><![CDATA[<h1 id="mybatis-plus"><a href="#mybatis-plus" class="headerlink" title="mybatis-plus"></a>mybatis-plus</h1><h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><blockquote><p><a href="https://github.com/baomidou/mybatis-plus">MyBatis-Plus</a>(简称 MP)是一个 <a href="https://www.mybatis.org/mybatis-3/">MyBatis</a>的增强工具,在 MyBatis 的基础上==只做增强不做改变==,为简化开发、提高效率而生。(先了解[[mybatis]]框架)</p></blockquote><p><img src="https://www.baomidou.com/img/relationship-with-mybatis.png"></p><h2 id="1-快速开始"><a href="#1-快速开始" class="headerlink" title="1.快速开始"></a>1.快速开始</h2><h3 id="1-1-导入依赖"><a href="#1-1-导入依赖" class="headerlink" title="1.1 导入依赖"></a>1.1 导入依赖</h3><blockquote><p>导入mybatis-plus依赖,包含了mybatis,==不用额外再导入mybatis依赖==</p></blockquote><figure class="highlight xml"><table><tr><td class="code"><pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>com.baomidou<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>mybatis-plus-boot-starter<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">version</span>></span>3.5.3.1<span class="hljs-tag"></<span class="hljs-name">version</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br></code></pre></td></tr></table></figure><h3 id="1-2-创建Mapper"><a href="#1-2-创建Mapper" class="headerlink" title="1.2 创建Mapper"></a>1.2 创建Mapper</h3><blockquote><p>为了简化单表CRUD,mp已经提供了对于单表的CRUD操作的接口<code>BaseMapper</code>,直接继承BaseMapper接口即可直接使用</p></blockquote><p><img src="https://s2.loli.net/2024/09/27/4OLtKEZGCX8RBba.png"></p><h3 id="1-3-测试CRUD"><a href="#1-3-测试CRUD" class="headerlink" title="1.3 测试CRUD"></a>1.3 测试CRUD</h3><blockquote><p>测试BaseMapper中对单表CRUD操作</p></blockquote><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Test</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">testInsert</span><span class="hljs-params">()</span> {<br> <span class="hljs-type">User</span> <span class="hljs-variable">user</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">User</span>();<br> <span class="hljs-comment">//user.setId(5L);</span><br> user.setUsername(<span class="hljs-string">"ikun23"</span>);<br> user.setPassword(<span class="hljs-string">"123"</span>);<br> user.setPhone(<span class="hljs-string">"18688990011"</span>);<br> user.setBalance(<span class="hljs-number">200</span>);<br> user.setInfo(UserInfo.of(<span class="hljs-number">24</span>,<span class="hljs-string">"英语老师"</span>,<span class="hljs-string">"female"</span>));<br> user.setCreateTime(LocalDateTime.now());<br> user.setUpdateTime(LocalDateTime.now());<br> userMapper.insert(user);<br>}<br><br><span class="hljs-meta">@Test</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">testSelectById</span><span class="hljs-params">()</span> {<br> <span class="hljs-type">User</span> <span class="hljs-variable">user</span> <span class="hljs-operator">=</span> userMapper.selectById(<span class="hljs-number">4L</span>);<br> System.out.println(user);<br>}<br><br><span class="hljs-meta">@Test</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">testSelectByIds</span><span class="hljs-params">()</span> {<br> List<User> users = userMapper.selectBatchIds(List.of(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>));<br> users.forEach(System.out::println);<br>}<br><br><span class="hljs-meta">@Test</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">testUpdate</span><span class="hljs-params">()</span> {<br> <span class="hljs-type">User</span> <span class="hljs-variable">user</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">User</span>();<br> user.setId(<span class="hljs-number">5L</span>);<br> user.setBalance(<span class="hljs-number">3</span>);<br> user.setInfo(UserInfo.of(<span class="hljs-number">24</span>,<span class="hljs-string">"英语老师"</span>,<span class="hljs-string">"female"</span>));<br> user.setCreateTime(LocalDateTime.now());<br> user.setUpdateTime(LocalDateTime.now());<br><br> userMapper.updateById(user);<br>}<br><br><span class="hljs-meta">@Test</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">testDelete</span><span class="hljs-params">()</span> {<br> System.out.println(userMapper.deleteById(<span class="hljs-number">5L</span>));<br>}<br></code></pre></td></tr></table></figure><blockquote><p>总结:只要继承了<code>BaseMapper</code>,就能直接对单表进行CRUD操作!</p></blockquote><h2 id="2-常见注解"><a href="#2-常见注解" class="headerlink" title="2.常见注解"></a>2.常见注解</h2><blockquote><p><strong>问题</strong>:在刚刚的测试中,我们直接调用BaseMapper中的方法就能对表增删改查,在继承<code>BaseMapper</code>的时候我们只是指定了一个泛型<code><User></code>,并没有指定是哪张表,那么==mybatis-plus怎么知道我们要操作的是user表呢?它又是怎么知道这张表中的所有字段名呢?==</p><p>解答:其实mp遵从==约定大于配置==的思想,mp从<code>User</code>类推导出数据库中表名为<code>user</code>,然后根据User类中的所有变量名从==驼峰命名==转成==下划线==作为数据库的字段名,从而在调用方法时可以自动生成正确的sql语句。</p><p>如果我们在创建User类和user表的时候遵从驼峰命名和下划线命名,那么我们不需要做额外的配置,如果类名和表名、属性名和字段名直接不是简单的转换,那么我们就不得不使用一些相应的注解来声明表的信息</p></blockquote><h3 id="2-1-TableName"><a href="#2-1-TableName" class="headerlink" title="2.1 @TableName"></a>2.1 <a href="https://www.baomidou.com/pages/223848/#tablename">@TableName</a></h3><ul><li>描述:表名注解,标识实体类对应的表</li><li>使用位置:实体类</li></ul><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@TableName("sys_user")</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">User</span> {<br> <span class="hljs-keyword">private</span> Long id;<br> <span class="hljs-keyword">private</span> String name;<br> <span class="hljs-keyword">private</span> Integer age;<br> <span class="hljs-keyword">private</span> String email;<br>}<br></code></pre></td></tr></table></figure><h3 id="2-2-TableId"><a href="#2-2-TableId" class="headerlink" title="2.2 @TableId"></a>2.2 <a href="https://github.com/baomidou/mybatis-plus/blob/3.0/mybatis-plus-annotation/src/main/java/com/baomidou/mybatisplus/annotation/TableId.java">@TableId</a></h3><ul><li>描述:主键注解</li><li>使用位置:实体类主键字段</li></ul><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@TableName("sys_user")</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">User</span> {<br> <span class="hljs-meta">@TableId</span><br> <span class="hljs-keyword">private</span> Long id;<br> <span class="hljs-keyword">private</span> String name;<br> <span class="hljs-keyword">private</span> Integer age;<br> <span class="hljs-keyword">private</span> String email;<br>}<br></code></pre></td></tr></table></figure><table><thead><tr><th><strong>属性</strong></th><th><strong>类型</strong></th><th><strong>必须指定</strong></th><th><strong>默认值</strong></th><th><strong>描述</strong></th></tr></thead><tbody><tr><td>value</td><td>String</td><td>否</td><td>“”</td><td>主键字段名</td></tr><tr><td>type</td><td>Enum</td><td>否</td><td>==IdType.NONE==</td><td>指定主键类型</td></tr></tbody></table><p><code>IdType</code>支持的类型有:</p><table><thead><tr><th><strong>值</strong></th><th><strong>描述</strong></th></tr></thead><tbody><tr><td>AUTO</td><td>数据库 ID 自增</td></tr><tr><td>NONE</td><td>无状态,该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)</td></tr><tr><td>INPUT</td><td>insert 前自行 set 主键值</td></tr><tr><td>ASSIGN_ID</td><td>分配 ID(主键类型为 Number(Long 和 Integer)或 String)(since 3.3.0),使用接口IdentifierGenerator的方法nextId(默认实现类为DefaultIdentifierGenerator雪花算法)</td></tr><tr><td>ASSIGN_UUID</td><td>分配 UUID,主键类型为 String(since 3.3.0),使用接口IdentifierGenerator的方法nextUUID(默认 default 方法)</td></tr><tr><td><del>ID_WORKER</del></td><td>分布式全局唯一 ID 长整型类型(please use ASSIGN_ID)</td></tr><tr><td><del>UUID</del></td><td>32 位 UUID 字符串(please use ASSIGN_UUID)</td></tr><tr><td><del>ID_WORKER_STR</del></td><td>分布式全局唯一 ID 字符串类型(please use ASSIGN_ID)</td></tr></tbody></table><p>这里比较常见的有三种:</p><ul><li><code>AUTO</code>:利用数据库的id自增长</li><li><code>INPUT</code>:手动生成id</li><li><code>ASSIGN_ID</code>:雪花算法生成<code>Long</code>类型的全局唯一id,这是默认的ID策略</li></ul><h3 id="2-3-TableField"><a href="#2-3-TableField" class="headerlink" title="2.3 @TableField"></a>2.3 <a href="https://www.baomidou.com/pages/223848/#tablefield">@TableField</a></h3><ul><li>描述:字段注解(非主键)</li></ul><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@TableName("sys_user")</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">User</span> {<br> <span class="hljs-meta">@TableId</span><br> <span class="hljs-keyword">private</span> Long id;<br> <span class="hljs-meta">@TableField("nickname")</span><br> <span class="hljs-keyword">private</span> String name;<br> <span class="hljs-keyword">private</span> Integer age;<br> <span class="hljs-keyword">private</span> String email;<br>}<br></code></pre></td></tr></table></figure><h2 id="3-常见配置"><a href="#3-常见配置" class="headerlink" title="3.常见配置"></a>3.常见配置</h2><p>MybatisPlus也支持基于yaml文件的自定义配置,详见官方文档:<br><a href="https://www.baomidou.com/pages/56bac0/#%E5%9F%BA%E6%9C%AC%E9%85%8D%E7%BD%AE">使用配置 | MyBatis-Plus</a></p><p>大多数的配置都有默认值,因此我们都无需配置。但还有一些是没有默认值的,例如:</p><ul><li>实体类的别名扫描包</li><li>全局id类型</li></ul><figure class="highlight yaml"><table><tr><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">mybatis-plus:</span><br> <span class="hljs-attr">type-aliases-package:</span> <span class="hljs-string">com.itheima.mp.domain.po</span> <span class="hljs-comment">#别名扫描包</span><br> <span class="hljs-attr">global-config:</span><br> <span class="hljs-attr">db-config:</span><br> <span class="hljs-attr">id-type:</span> <span class="hljs-string">auto</span> <span class="hljs-comment"># 全局id类型为自增长</span><br></code></pre></td></tr></table></figure><p>需要注意的是,MyBatisPlus也支持手写SQL的,而mapper文件的读取地址可以自己配置:</p><figure class="highlight yaml"><table><tr><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">mybatis-plus:</span><br> <span class="hljs-attr">mapper-locations:</span> <span class="hljs-string">"classpath*:/mapper/**/*.xml"</span> <span class="hljs-comment"># Mapper.xml文件地址,当前这个是默认值。</span><br></code></pre></td></tr></table></figure><p>可以看到默认值是<code>classpath*:/mapper/**/*.xml</code>,也就是说我们只要把mapper.xml文件放置这个目录下就一定会被加载。</p><p>使用<code>@MapperScan</code>注解标识mapper类所在目录</p><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@SpringBootApplication</span><br><span class="hljs-meta">@MapperScan("com.clb.mapper")</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">App</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> SpringApplication.run(App.class, args);<br> }<br>}<br><br></code></pre></td></tr></table></figure><blockquote><p>配置了==@MapperScan==注解后,mapper类中无需添加==@Mapper==注解</p></blockquote><hr><h2 id="4-核心功能"><a href="#4-核心功能" class="headerlink" title="==4.核心功能=="></a><strong>==4.核心功能==</strong></h2><h3 id="4-1-条件构造器"><a href="#4-1-条件构造器" class="headerlink" title="4.1 条件构造器"></a>4.1 条件构造器</h3><p>除了新增以外,修改、删除、查询的SQL语句都需要指定where条件。因此BaseMapper中提供的相关方法除了以<code>id</code>作为<code>where</code>条件以外,还支持更加复杂的<code>where</code>条件。<br><img src="https://s2.loli.net/2024/09/27/RLOD7QvGzrIm6bX.png" alt="image.png"><br>参数中的<code>Wrapper</code>就是条件构造的抽象类,其下有很多默认实现,继承关系如图:<br><img src="https://s2.loli.net/2024/09/27/HZGgNMxK4R8PChQ.png" alt="image.png"></p><p><code>Wrapper</code>的子类<code>AbstractWrapper</code>提供了where中包含的所有条件构造方法:<br><img src="https://s2.loli.net/2024/09/27/tjv9SlikbZMu6GE.png" alt="image.png"><br>而QueryWrapper在AbstractWrapper的基础上拓展了一个select方法,允许指定查询字段:<br><img src="https://s2.loli.net/2024/09/27/wquGfEOJlzTtceP.png" alt="image.png"><br>而UpdateWrapper在AbstractWrapper的基础上拓展了一个set方法,允许指定SQL中的SET部分:<br><img src="https://s2.loli.net/2024/09/27/jQ6iLP7qJtka52N.png" alt="image.png"></p><p>接下来,我们就来看看如何利用<code>Wrapper</code>实现复杂查询。</p><h4 id="4-1-1-QueryWrapper"><a href="#4-1-1-QueryWrapper" class="headerlink" title="4.1.1 QueryWrapper"></a>4.1.1 QueryWrapper</h4><p>修改、删除、查询都可以使用QueryWrapper构建查询条件</p><p><strong>查询:</strong>查询名字带有o,且存款大于等于1000的人</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><code class="hljs mysql">select id,username,info,balance <br>from user<br>where username like %o% and balance >= 1000;<br></code></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Test</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">test02</span><span class="hljs-params">()</span> {<br> QueryWrapper<User> wrapper = <span class="hljs-keyword">new</span> <span class="hljs-title class_">QueryWrapper</span><>();<br> <span class="hljs-comment">//构建查询条件</span><br> wrapper.select(<span class="hljs-string">"id"</span>, <span class="hljs-string">"username"</span>, <span class="hljs-string">"info"</span>, <span class="hljs-string">"balance"</span>)<br> .like(<span class="hljs-string">"username"</span>, <span class="hljs-string">"o"</span>)<br> .ge(<span class="hljs-string">"balance"</span>, <span class="hljs-number">1000</span>);<br> <span class="hljs-comment">//查询数据</span><br> List<User> users = userMapper.selectList(wrapper);<br> users.forEach(System.out::println);<br>}<br></code></pre></td></tr></table></figure><h4 id="4-1-2-UpdateWrapper"><a href="#4-1-2-UpdateWrapper" class="headerlink" title="4.1.2 UpdateWrapper"></a>4.1.2 UpdateWrapper</h4><p>基于BaseMapper中的update方法更新时只能直接赋值,对于一些复杂的需求就难以实现。<br>例如:更新id为<code>1,2,4</code>的用户的余额,扣200,对于的SQL应该是:</p><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">UPDATE</span> <span class="hljs-keyword">user</span> <span class="hljs-keyword">SET</span> balance <span class="hljs-operator">=</span> balance <span class="hljs-operator">-</span> <span class="hljs-number">200</span> <span class="hljs-keyword">WHERE</span> id <span class="hljs-keyword">in</span> (<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">4</span>)<br></code></pre></td></tr></table></figure><p>SET的赋值结果是基于字段现有值的,这个时候就要利用UpdateWrapper中的setSql功能了:</p><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Test</span><br><span class="hljs-keyword">void</span> <span class="hljs-title function_">testUpdateWrapper</span><span class="hljs-params">()</span> {<br> List<Long> ids = List.of(<span class="hljs-number">1L</span>, <span class="hljs-number">2L</span>, <span class="hljs-number">4L</span>);<br> <span class="hljs-comment">// 1.生成SQL</span><br> UpdateWrapper<User> wrapper = <span class="hljs-keyword">new</span> <span class="hljs-title class_">UpdateWrapper</span><User>()<br> .setSql(<span class="hljs-string">"balance = balance - 200"</span>) <span class="hljs-comment">// SET balance = balance - 200</span><br> .in(<span class="hljs-string">"id"</span>, ids); <span class="hljs-comment">// WHERE id in (1, 2, 4)</span><br> <span class="hljs-comment">// 2.更新,注意第一个参数可以给null,也就是不填更新字段和数据,</span><br> <span class="hljs-comment">// 而是基于UpdateWrapper中的setSQL来更新</span><br> userMapper.update(<span class="hljs-literal">null</span>, wrapper);<br>}<br></code></pre></td></tr></table></figure><h4 id="4-1-3-LambdaQueryWrapper"><a href="#4-1-3-LambdaQueryWrapper" class="headerlink" title="4.1.3 LambdaQueryWrapper"></a>4.1.3 LambdaQueryWrapper</h4><p>无论是QueryWrapper还是UpdateWrapper在构造条件的时候都需要写死字段名称,可能会出现字符串写错的现象,因此MybatisPlus又提供了一套基于Lambda的Wrapper,包含两个:</p><ul><li><code>LambdaQueryWrapper</code></li><li><code>LambdaUpdateWrapper</code></li></ul><p>分别对应<code>QueryWrapper</code>和<code>UpdateWrapper</code></p><figure class="highlight plaintext"><table><tr><td class="code"><pre><code class="hljs mysql">select id,username,info,balance <br>from user<br>where username like %o% and balance >= 1000;<br></code></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Test</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">test02</span><span class="hljs-params">()</span> {<br> LambdaQueryWrapper<User> wrapper = <span class="hljs-keyword">new</span> <span class="hljs-title class_">LambdaQueryWrapper</span><>();<br> <span class="hljs-comment">//构建查询条件</span><br> <span class="hljs-comment">//由原来的字符串变成getter函数对象获取类属性</span><br> wrapper.select(User::getId, User::getUsername, User::getInfo, User::getBalance)<br> .like(User::getUsername, <span class="hljs-string">"o"</span>)<br> .ge(User::getBalance, <span class="hljs-number">1000</span>);<br> userMapper.selectList(wrapper);<br>}<br></code></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="code"><pre><code class="hljs mysql">UPDATE user SET balance = balance - 200 WHERE id in (1, 2, 4);<br></code></pre></td></tr></table></figure><h3 id="4-2-自定义SQL"><a href="#4-2-自定义SQL" class="headerlink" title="4.2 自定义SQL"></a>4.2 自定义SQL</h3><h4 id="4-2-1-基本使用"><a href="#4-2-1-基本使用" class="headerlink" title="4.2.1 基本使用"></a>4.2.1 基本使用</h4><p>在演示<code>UpdateWrapper</code>的案例中,我们在代码中编写了更新的SQL语句:<br><img src="https://s2.loli.net/2024/09/27/GuIBkSFgNjRfm3X.png" alt="image.png"><br>这种写法在某些企业也是不允许的,因为SQL语句最好都维护在持久层,而不是业务层。就当前案例来说,由于条件是in语句,只能将SQL写在Mapper.xml文件,利用foreach来生成动态SQL。<br>这实在是太麻烦了。假如查询条件更复杂,动态SQL的编写也会更加复杂。</p><p>所以,MybatisPlus提供了自定义SQL功能,可以让我们利用Wrapper生成查询条件,再结合Mapper.xml或注解编写SQL</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><code class="hljs mysql">UPDATE user SET balance = balance - 200 WHERE id in (1, 2, 4)<br></code></pre></td></tr></table></figure><p><code>update user set balance = balance - 200</code>使用注解完成</p><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//将wrapper作为ew,并使用ew.customSqlSegment取出条件是固定写法</span><br><span class="hljs-meta">@Update("update tb_user set balance = balance - #{amount} ${ew.customSqlSegment}")</span><br><span class="hljs-keyword">void</span> <span class="hljs-title function_">updateBalanceByWrapper</span><span class="hljs-params">(<span class="hljs-meta">@Param("amount")</span> <span class="hljs-type">int</span> amount, <span class="hljs-meta">@Param("ew")</span> LambdaQueryWrapper<User> wrapper)</span>;<br></code></pre></td></tr></table></figure><p><code>where id in(1,2,4)</code> 使用自定义sql完成,将wrapper作为参数传入自定义的方法中<code>${ew.customSqlSegment}</code> ==><code>where id in (1,2,4)</code></p><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Test</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">testCustomSql</span><span class="hljs-params">()</span> {<br> <span class="hljs-type">int</span> <span class="hljs-variable">amount</span> <span class="hljs-operator">=</span> <span class="hljs-number">200</span>;<br> LambdaQueryWrapper<User> wrapper = <span class="hljs-keyword">new</span> <span class="hljs-title class_">LambdaQueryWrapper</span><>();<br> wrapper.in(User::getId, <span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">4</span>);<br> userMapper.updateBalanceByWrapper(amount, wrapper);<br>}<br></code></pre></td></tr></table></figure><h4 id="4-2-2-多表联查"><a href="#4-2-2-多表联查" class="headerlink" title="4.2.2 多表联查"></a>4.2.2 多表联查</h4><p>理论上来将MyBatisPlus是不支持多表查询的,不过我们可以利用Wrapper中自定义条件结合自定义SQL来实现多表查询的效果。<br>例如,我们要查询出所有收货地址在北京的并且用户id在1、2、4之中的用户<br>要是自己基于mybatis实现SQL,大概是这样的:</p><figure class="highlight xml"><table><tr><td class="code"><pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">select</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"queryUserByIdAndAddr"</span> <span class="hljs-attr">resultType</span>=<span class="hljs-string">"com.itheima.mp.domain.po.User"</span>></span><br> SELECT *<br> FROM user u<br> INNER JOIN address a ON u.id = a.user_id<br> WHERE u.id<br> <span class="hljs-tag"><<span class="hljs-name">foreach</span> <span class="hljs-attr">collection</span>=<span class="hljs-string">"ids"</span> <span class="hljs-attr">separator</span>=<span class="hljs-string">","</span> <span class="hljs-attr">item</span>=<span class="hljs-string">"id"</span> <span class="hljs-attr">open</span>=<span class="hljs-string">"IN ("</span> <span class="hljs-attr">close</span>=<span class="hljs-string">")"</span>></span><br> #{id}<br> <span class="hljs-tag"></<span class="hljs-name">foreach</span>></span><br> AND a.city = #{city}<br><span class="hljs-tag"></<span class="hljs-name">select</span>></span><br></code></pre></td></tr></table></figure><p>可以看出其中最复杂的就是WHERE条件的编写,如果业务复杂一些,这里的SQL会更变态。但是基于自定义SQL结合Wrapper的玩法,我们就可以利用Wrapper来构建查询条件,然后手写SELECT及FROM部分,实现多表查询。<br>查询条件这样来构建:</p><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Test</span><br><span class="hljs-keyword">void</span> <span class="hljs-title function_">testCustomJoinWrapper</span><span class="hljs-params">()</span> {<br> <span class="hljs-comment">// 1.准备自定义查询条件</span><br> QueryWrapper<User> wrapper = <span class="hljs-keyword">new</span> <span class="hljs-title class_">QueryWrapper</span><User>()<br> .in(<span class="hljs-string">"u.id"</span>, List.of(<span class="hljs-number">1L</span>, <span class="hljs-number">2L</span>, <span class="hljs-number">4L</span>))<br> .eq(<span class="hljs-string">"a.city"</span>, <span class="hljs-string">"北京"</span>);<br><br> <span class="hljs-comment">// 2.调用mapper的自定义方法</span><br> List<User> users = userMapper.queryUserByWrapper(wrapper);<br><br> users.forEach(System.out::println);<br>}<br></code></pre></td></tr></table></figure><p>然后在UserMapper中自定义方法:</p><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Select("SELECT u.* FROM user u INNER JOIN address a ON u.id = a.user_id ${ew.customSqlSegment}")</span><br>List<User> <span class="hljs-title function_">queryUserByWrapper</span><span class="hljs-params">(<span class="hljs-meta">@Param("ew")</span>QueryWrapper<User> wrapper)</span>;<br></code></pre></td></tr></table></figure><p>当然,也可以在<code>UserMapper.xml</code>中写SQL:</p><figure class="highlight xml"><table><tr><td class="code"><pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">select</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"queryUserByIdAndAddr"</span> <span class="hljs-attr">resultType</span>=<span class="hljs-string">"com.itheima.mp.domain.po.User"</span>></span><br> SELECT * FROM user u INNER JOIN address a ON u.id = a.user_id ${ew.customSqlSegment}<br><span class="hljs-tag"></<span class="hljs-name">select</span>></span><br></code></pre></td></tr></table></figure><blockquote><p>==总结:where条件可以使用wrapper构建,然后作为参数传递==</p></blockquote><h3 id="4-3-Service接口"><a href="#4-3-Service接口" class="headerlink" title="4.3 Service接口"></a>4.3 Service接口</h3><p>MybatisPlus不仅提供了BaseMapper,还提供了通用的Service接口及默认实现,封装了一些常用的service模板方法。<br>通用接口为<code>IService</code>,默认实现为<code>ServiceImpl</code>,其中封装的方法可以分为以下几类:</p><ul><li><code>save</code>:新增</li><li><code>remove</code>:删除</li><li><code>update</code>:更新</li><li><code>get</code>:查询单个结果</li><li><code>list</code>:查询集合结果</li><li><code>count</code>:计数</li><li><code>page</code>:分页查询</li></ul><h4 id="4-3-1-CRUD"><a href="#4-3-1-CRUD" class="headerlink" title="4.3.1.CRUD"></a>4.3.1.CRUD</h4><p>我们先俩看下基本的CRUD接口。<br><strong>新增</strong>:<br><img src="https://s2.loli.net/2024/09/27/IknQNGXRaDU2tHi.png" alt="image.png"></p><ul><li><code>save</code>是新增单个元素</li><li><code>saveBatch</code>是批量新增</li><li><code>saveOrUpdate</code>是根据id判断,如果数据存在就更新,不存在则新增</li><li><code>saveOrUpdateBatch</code>是批量的新增或修改</li></ul><p><strong>删除:</strong><br><img src="https://s2.loli.net/2024/09/27/kWFEAZKwsGRBNjM.png" alt="image.png"></p><ul><li><code>removeById</code>:根据id删除</li><li><code>removeByIds</code>:根据id批量删除</li><li><code>removeByMap</code>:根据Map中的键值对为条件删除</li><li><code>remove(Wrapper<T>)</code>:根据Wrapper条件删除</li><li><code>~~removeBatchByIds~~</code>:暂不支持</li></ul><p><strong>修改:</strong><br><img src="https://s2.loli.net/2024/09/27/2qXOcJpWSxe65K7.png" alt="image.png"></p><ul><li><code>updateById</code>:根据id修改</li><li><code>update(Wrapper<T>)</code>:根据<code>UpdateWrapper</code>修改,<code>Wrapper</code>中包含<code>set</code>和<code>where</code>部分</li><li><code>update(T,Wrapper<T>)</code>:按照<code>T</code>内的数据修改与<code>Wrapper</code>匹配到的数据</li><li><code>updateBatchById</code>:根据id批量修改</li></ul><p><strong>Get:</strong><br><img src="https://s2.loli.net/2024/09/27/gzd1a7KHYJshTkV.png" alt="image.png"></p><ul><li><code>getById</code>:根据id查询1条数据</li><li><code>getOne(Wrapper<T>)</code>:根据<code>Wrapper</code>查询1条数据</li><li><code>getBaseMapper</code>:获取<code>Service</code>内的<code>BaseMapper</code>实现,某些时候需要直接调用<code>Mapper</code>内的自定义<code>SQL</code>时可以用这个方法获取到<code>Mapper</code></li></ul><p><strong>List:</strong><br><img src="https://s2.loli.net/2024/09/27/QBxXbZCVWhoseDv.png" alt="image.png"></p><ul><li><code>listByIds</code>:根据id批量查询</li><li><code>list(Wrapper<T>)</code>:根据Wrapper条件查询多条数据</li><li><code>list()</code>:查询所有</li></ul><p><strong>Count</strong>:<br><img src="https://s2.loli.net/2024/09/27/7WnBx2T94zaCIrj.png" alt="image.png"></p><ul><li><code>count()</code>:统计所有数量</li><li><code>count(Wrapper<T>)</code>:统计符合<code>Wrapper</code>条件的数据数量</li></ul><p><strong>getBaseMapper</strong>:<br>当我们在service中要调用Mapper中自定义SQL时,就必须获取service对应的Mapper,就可以通过这个方法:<br><img src="https://s2.loli.net/2024/09/27/lF9p4MbYmqLQUHd.png" alt="image.png"></p><h4 id="4-3-2-基本用法"><a href="#4-3-2-基本用法" class="headerlink" title="4.3.2 基本用法"></a>4.3.2 基本用法</h4><p>由于<code>Service</code>中经常需要定义与业务有关的自定义方法,因此我们不能直接使用<code>IService</code>,而是自定义<code>Service</code>接口,然后继承<code>IService</code>以拓展方法。同时,让自定义的<code>Service实现类</code>继承<code>ServiceImpl</code>,这样就不用自己实现<code>IService</code>中的接口了,如下图(<strong>绿色为接口,蓝色为实现类</strong>)</p><p><img src="https://s2.loli.net/2024/09/27/hG8ElYIcPVFDSOw.png"></p><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//自定义接口继承IService接口,需要指定泛型</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">IUserService</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">IService</span><User><br></code></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//自定义实现类继承ServiceImpl,实现自定义接口,需要指定对应的Mapper和泛型,对应的Mapper需要继承BaseMapper</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">UserServiceImpl</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">ServiceImpl</span><UserMapper, User> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">IUserService</span><br></code></pre></td></tr></table></figure><p>测试</p><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Test</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">testSave</span><span class="hljs-params">()</span> {<br> <span class="hljs-type">User</span> <span class="hljs-variable">user</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">User</span>();<br> <span class="hljs-comment">//user.setId(5L);</span><br> user.setUsername(<span class="hljs-string">"kun_"</span>);<br> user.setPassword(<span class="hljs-string">"123"</span>);<br> user.setPhone(<span class="hljs-string">"18688990011"</span>);<br> user.setBalance(<span class="hljs-number">2000</span>);<br> user.setInfo(UserInfo.of(<span class="hljs-number">24</span>, <span class="hljs-string">"英语老师"</span>, <span class="hljs-string">"female"</span>));<br> user.setCreateTime(LocalDateTime.now());<br> user.setUpdateTime(LocalDateTime.now());<br> user.setStatus(UserStatus.NORMAL);<br> userService.save(user);<br>}<br><br><span class="hljs-meta">@Test</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">testGet</span><span class="hljs-params">()</span> {<br> List<User> users = userService.list(<span class="hljs-keyword">new</span> <span class="hljs-title class_">LambdaQueryWrapper</span><User>().select(User::getUsername, User::getBalance).le(User::getBalance, <span class="hljs-number">999</span>));<br> users.forEach(System.out::println);<br>}<br></code></pre></td></tr></table></figure><h4 id="4-3-3-批量新增"><a href="#4-3-3-批量新增" class="headerlink" title="4.3.3 批量新增"></a>4.3.3 批量新增</h4><p>IService中的批量新增功能使用起来非常方便,但有一点注意事项,我们先来测试一下。<br>首先我们测试逐条插入数据:</p><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Test</span><br><span class="hljs-keyword">void</span> <span class="hljs-title function_">testSaveOneByOne</span><span class="hljs-params">()</span> {<br> <span class="hljs-type">long</span> <span class="hljs-variable">b</span> <span class="hljs-operator">=</span> System.currentTimeMillis();<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">1</span>; i <= <span class="hljs-number">100000</span>; i++) {<br> userService.save(buildUser(i));<br> }<br> <span class="hljs-type">long</span> <span class="hljs-variable">e</span> <span class="hljs-operator">=</span> System.currentTimeMillis();<br> System.out.println(<span class="hljs-string">"耗时:"</span> + (e - b));<br>}<br><br><span class="hljs-keyword">private</span> User <span class="hljs-title function_">buildUser</span><span class="hljs-params">(<span class="hljs-type">int</span> i)</span> {<br> <span class="hljs-type">User</span> <span class="hljs-variable">user</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">User</span>();<br> user.setUsername(<span class="hljs-string">"user_"</span> + i);<br> user.setPassword(<span class="hljs-string">"123"</span>);<br> user.setPhone(<span class="hljs-string">""</span> + (<span class="hljs-number">18688190000L</span> + i));<br> user.setBalance(<span class="hljs-number">2000</span>);<br> user.setInfo(<span class="hljs-string">"{\"age\": 24, \"intro\": \"英文老师\", \"gender\": \"female\"}"</span>);<br> user.setCreateTime(LocalDateTime.now());<br> user.setUpdateTime(user.getCreateTime());<br> <span class="hljs-keyword">return</span> user;<br>}<br></code></pre></td></tr></table></figure><p>执行结果如下:<br><img src="https://s2.loli.net/2024/09/27/Q7NhDZuAwc4Mb9Y.png" alt="image.png"><br>可以看到速度非常慢。</p><p>然后再试试MybatisPlus的批处理:</p><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Test</span><br><span class="hljs-keyword">void</span> <span class="hljs-title function_">testSaveBatch</span><span class="hljs-params">()</span> {<br> <span class="hljs-comment">// 准备10万条数据</span><br> List<User> list = <span class="hljs-keyword">new</span> <span class="hljs-title class_">ArrayList</span><>(<span class="hljs-number">1000</span>);<br> <span class="hljs-type">long</span> <span class="hljs-variable">b</span> <span class="hljs-operator">=</span> System.currentTimeMillis();<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">1</span>; i <= <span class="hljs-number">100000</span>; i++) {<br> list.add(buildUser(i));<br> <span class="hljs-comment">// 每1000条批量插入一次</span><br> <span class="hljs-keyword">if</span> (i % <span class="hljs-number">1000</span> == <span class="hljs-number">0</span>) {<br> userService.saveBatch(list);<br> list.clear();<br> }<br> }<br> <span class="hljs-type">long</span> <span class="hljs-variable">e</span> <span class="hljs-operator">=</span> System.currentTimeMillis();<br> System.out.println(<span class="hljs-string">"耗时:"</span> + (e - b));<br>}<br></code></pre></td></tr></table></figure><p>执行最终耗时如下:<br><img src="https://s2.loli.net/2024/09/27/a9XOB8GUkg4iwDl.png" alt="image.png"><br>可以看到使用了批处理以后,比逐条新增效率提高了10倍左右,性能还是不错的。</p><p>不过,我们简单查看一下<code>MybatisPlus</code>源码:</p><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Transactional(rollbackFor = Exception.class)</span><br><span class="hljs-meta">@Override</span><br><span class="hljs-keyword">public</span> <span class="hljs-type">boolean</span> <span class="hljs-title function_">saveBatch</span><span class="hljs-params">(Collection<T> entityList, <span class="hljs-type">int</span> batchSize)</span> {<br> <span class="hljs-type">String</span> <span class="hljs-variable">sqlStatement</span> <span class="hljs-operator">=</span> getSqlStatement(SqlMethod.INSERT_ONE);<br> <span class="hljs-keyword">return</span> executeBatch(entityList, batchSize, (sqlSession, entity) -> sqlSession.insert(sqlStatement, entity));<br>}<br><span class="hljs-comment">// ...SqlHelper</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <E> <span class="hljs-type">boolean</span> <span class="hljs-title function_">executeBatch</span><span class="hljs-params">(Class<?> entityClass, Log log, Collection<E> list, <span class="hljs-type">int</span> batchSize, BiConsumer<SqlSession, E> consumer)</span> {<br> Assert.isFalse(batchSize < <span class="hljs-number">1</span>, <span class="hljs-string">"batchSize must not be less than one"</span>);<br> <span class="hljs-keyword">return</span> !CollectionUtils.isEmpty(list) && executeBatch(entityClass, log, sqlSession -> {<br> <span class="hljs-type">int</span> <span class="hljs-variable">size</span> <span class="hljs-operator">=</span> list.size();<br> <span class="hljs-type">int</span> <span class="hljs-variable">idxLimit</span> <span class="hljs-operator">=</span> Math.min(batchSize, size);<br> <span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">1</span>;<br> <span class="hljs-keyword">for</span> (E element : list) {<br> consumer.accept(sqlSession, element);<br> <span class="hljs-keyword">if</span> (i == idxLimit) {<br> sqlSession.flushStatements();<br> idxLimit = Math.min(idxLimit + batchSize, size);<br> }<br> i++;<br> }<br> });<br>}<br></code></pre></td></tr></table></figure><p>可以发现其实<code>MybatisPlus</code>的批处理是基于<code>PrepareStatement</code>的预编译模式,然后批量提交,最终在数据库执行时还是会有多条insert语句,逐条插入数据。SQL类似这样:</p><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java">Preparing: INSERT INTO <span class="hljs-title function_">user</span> <span class="hljs-params">( username, password, phone, info, balance, create_time, update_time )</span> VALUES ( ?, ?, ?, ?, ?, ?, ? )<br>Parameters: user_1, <span class="hljs-number">123</span>, <span class="hljs-number">18688190001</span>, <span class="hljs-string">""</span>, <span class="hljs-number">2000</span>, <span class="hljs-number">2023</span>-<span class="hljs-number">07</span>-<span class="hljs-number">01</span>, <span class="hljs-number">2023</span>-<span class="hljs-number">07</span>-<span class="hljs-number">01</span><br>Parameters: user_2, <span class="hljs-number">123</span>, <span class="hljs-number">18688190002</span>, <span class="hljs-string">""</span>, <span class="hljs-number">2000</span>, <span class="hljs-number">2023</span>-<span class="hljs-number">07</span>-<span class="hljs-number">01</span>, <span class="hljs-number">2023</span>-<span class="hljs-number">07</span>-<span class="hljs-number">01</span><br>Parameters: user_3, <span class="hljs-number">123</span>, <span class="hljs-number">18688190003</span>, <span class="hljs-string">""</span>, <span class="hljs-number">2000</span>, <span class="hljs-number">2023</span>-<span class="hljs-number">07</span>-<span class="hljs-number">01</span>, <span class="hljs-number">2023</span>-<span class="hljs-number">07</span>-<span class="hljs-number">01</span><br></code></pre></td></tr></table></figure><p>而如果想要得到最佳性能,最好是将多条SQL合并为一条,像这样:</p><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">INSERT INTO</span> <span class="hljs-keyword">user</span> ( username, password, phone, info, balance, create_time, update_time )<br><span class="hljs-keyword">VALUES</span> <br>(user_1, <span class="hljs-number">123</span>, <span class="hljs-number">18688190001</span>, "", <span class="hljs-number">2000</span>, <span class="hljs-number">2023</span><span class="hljs-number">-07</span><span class="hljs-number">-01</span>, <span class="hljs-number">2023</span><span class="hljs-number">-07</span><span class="hljs-number">-01</span>),<br>(user_2, <span class="hljs-number">123</span>, <span class="hljs-number">18688190002</span>, "", <span class="hljs-number">2000</span>, <span class="hljs-number">2023</span><span class="hljs-number">-07</span><span class="hljs-number">-01</span>, <span class="hljs-number">2023</span><span class="hljs-number">-07</span><span class="hljs-number">-01</span>),<br>(user_3, <span class="hljs-number">123</span>, <span class="hljs-number">18688190003</span>, "", <span class="hljs-number">2000</span>, <span class="hljs-number">2023</span><span class="hljs-number">-07</span><span class="hljs-number">-01</span>, <span class="hljs-number">2023</span><span class="hljs-number">-07</span><span class="hljs-number">-01</span>),<br>(user_4, <span class="hljs-number">123</span>, <span class="hljs-number">18688190004</span>, "", <span class="hljs-number">2000</span>, <span class="hljs-number">2023</span><span class="hljs-number">-07</span><span class="hljs-number">-01</span>, <span class="hljs-number">2023</span><span class="hljs-number">-07</span><span class="hljs-number">-01</span>);<br></code></pre></td></tr></table></figure><p>该怎么做呢?</p><p>MySQL的客户端连接参数中有这样的一个参数:<code>rewriteBatchedStatements</code>。顾名思义,就是重写批处理的<code>statement</code>语句。参考文档:<br><a href="https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-connp-props-performance-extensions.html#cj-conn-prop_rewriteBatchedStatements">cj-conn-prop_rewriteBatchedStatements</a><br>这个参数的默认值是false,我们需要修改连接参数,将其配置为true</p><p>修改项目中的application.yml文件,在jdbc的url后面添加参数<code>&rewriteBatchedStatements=true</code>:</p><figure class="highlight yaml"><table><tr><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">spring:</span><br> <span class="hljs-attr">datasource:</span><br> <span class="hljs-attr">url:</span> <span class="hljs-string">jdbc:mysql://127.0.0.1:3306/mp?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true</span><br> <span class="hljs-attr">driver-class-name:</span> <span class="hljs-string">com.mysql.cj.jdbc.Driver</span><br> <span class="hljs-attr">username:</span> <span class="hljs-string">root</span><br> <span class="hljs-attr">password:</span> <span class="hljs-string">MySQL123</span><br></code></pre></td></tr></table></figure><p>再次测试插入10万条数据,可以发现速度有非常明显的提升:<br><img src="https://s2.loli.net/2024/09/27/vR5hF7PEDg4smrZ.png" alt="image.png"></p><p>在<code>ClientPreparedStatement</code>的<code>executeBatchInternal</code>中,有判断<code>rewriteBatchedStatements</code>值是否为true并重写SQL的功能:<br><img src="https://s2.loli.net/2024/09/27/cD4WBbzk9Xs1CRf.png" alt="image.png"><br>最终,SQL被重写了:<br><img src="https://s2.loli.net/2024/09/27/XFi86M3UpjGdIvn.png" alt="image.png"></p><blockquote><p>总结:</p><ol><li><p>插入大量数据的时候,使用<code>saveBatch</code>批量插入一定数量的数据而不是在循环里面一条一条插入数据<code>save</code></p></li><li><p>mysql配置文件中开启批处理</p><figure class="highlight yaml"><table><tr><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">spring:</span><br> <span class="hljs-attr">datasource:</span><br> <span class="hljs-attr">url:</span> <span class="hljs-string">jdbc:mysql://127.0.0.1:3306/mp?rewriteBatchedStatements=true</span> <span class="hljs-comment">#rewriteBatchedStatements=true 开启批处理</span><br></code></pre></td></tr></table></figure></li></ol></blockquote><h4 id="4-3-4-Lambda"><a href="#4-3-4-Lambda" class="headerlink" title="4.3.4 Lambda"></a>4.3.4 Lambda</h4><p>Service中对<code>LambdaQueryWrapper</code>和<code>LambdaUpdateWrapper</code>的用法进一步做了简化。我们无需自己通过<code>new</code>的方式来创建<code>Wrapper</code>,而是直接调用<code>lambdaQuery</code>和<code>lambdaUpdate</code>方法:</p><p>基于Lambda查询:</p><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Test</span><br><span class="hljs-keyword">void</span> <span class="hljs-title function_">testLambdaQuery</span><span class="hljs-params">()</span> {<br> <span class="hljs-comment">// 1.查询1个</span><br> <span class="hljs-type">User</span> <span class="hljs-variable">rose</span> <span class="hljs-operator">=</span> userService.lambdaQuery()<br> .eq(User::getUsername, <span class="hljs-string">"Rose"</span>)<br> .one(); <span class="hljs-comment">// .one()查询1个</span><br> System.out.println(<span class="hljs-string">"rose = "</span> + rose);<br><br> <span class="hljs-comment">// 2.查询多个</span><br> List<User> users = userService.lambdaQuery()<br> .like(User::getUsername, <span class="hljs-string">"o"</span>)<br> .list(); <span class="hljs-comment">// .list()查询集合</span><br> users.forEach(System.out::println);<br><br> <span class="hljs-comment">// 3.count统计</span><br> <span class="hljs-type">Long</span> <span class="hljs-variable">count</span> <span class="hljs-operator">=</span> userService.lambdaQuery()<br> .like(User::getUsername, <span class="hljs-string">"o"</span>)<br> .count(); <span class="hljs-comment">// .count()则计数</span><br> System.out.println(<span class="hljs-string">"count = "</span> + count);<br>}<br></code></pre></td></tr></table></figure><p>可以发现lambdaQuery方法中除了可以构建条件,而且根据链式编程的最后一个方法来判断最终的返回结果,可选的方法有:</p><ul><li><code>.one()</code>:最多1个结果</li><li><code>.list()</code>:返回集合结果</li><li><code>.count()</code>:返回计数结果</li></ul><p>lambdaQuery还支持动态条件查询。比如下面这个需求:</p><blockquote><p>定义一个方法,接收参数为username、status、minBalance、maxBalance,参数可以为空。</p><ul><li>如果username参数不为空,则采用模糊查询;</li><li>如果status参数不为空,则采用精确匹配;</li><li>如果minBalance参数不为空,则余额必须大于minBalance</li><li>如果maxBalance参数不为空,则余额必须小于maxBalance</li></ul></blockquote><p>这个需求就是典型的动态查询,在业务开发中经常碰到,实现如下:</p><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Test</span><br><span class="hljs-keyword">void</span> <span class="hljs-title function_">testQueryUser</span><span class="hljs-params">()</span> {<br> List<User> users = queryUser(<span class="hljs-string">"o"</span>, <span class="hljs-number">1</span>, <span class="hljs-literal">null</span>, <span class="hljs-literal">null</span>);<br> users.forEach(System.out::println);<br>}<br><br><span class="hljs-keyword">public</span> List<User> <span class="hljs-title function_">queryUser</span><span class="hljs-params">(String username, Integer status, Integer minBalance, Integer maxBalance)</span> {<br> <span class="hljs-keyword">return</span> userService.lambdaQuery()<br> .like(username != <span class="hljs-literal">null</span> , User::getUsername, username)<br> .eq(status != <span class="hljs-literal">null</span>, User::getStatus, status)<br> .ge(minBalance != <span class="hljs-literal">null</span>, User::getBalance, minBalance)<br> .le(maxBalance != <span class="hljs-literal">null</span>, User::getBalance, maxBalance)<br> .list();<br>}<br></code></pre></td></tr></table></figure><p>基于Lambda更新:</p><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Test</span><br><span class="hljs-keyword">void</span> <span class="hljs-title function_">testLambdaUpdate</span><span class="hljs-params">()</span> {<br> userService.lambdaUpdate()<br> .set(User::getBalance, <span class="hljs-number">800</span>) <span class="hljs-comment">// set balance = 800</span><br> .eq(User::getUsername, <span class="hljs-string">"Jack"</span>) <span class="hljs-comment">// where username = "Jack"</span><br> .update(); <span class="hljs-comment">// 执行Update</span><br>}<br></code></pre></td></tr></table></figure><p><code>lambdaUpdate()</code>方法后基于链式编程,可以添加<code>set</code>条件和<code>where</code>条件。但最后一定要跟上<code>update()</code>,否则语句不会执行。</p><p>lambdaUpdate()同样支持动态条件,例如下面的需求:</p><blockquote><p>基于IService中的lambdaUpdate()方法实现一个更新方法,满足下列需求:</p><ul><li>参数为balance、id、username</li><li>id或username至少一个不为空,根据id或username精确匹配用户</li><li>将匹配到的用户余额修改为balance</li><li>如果balance为0,则将用户status修改为冻结状态</li></ul></blockquote><p>实现如下:</p><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Test</span><br><span class="hljs-keyword">void</span> <span class="hljs-title function_">testUpdateBalance</span><span class="hljs-params">()</span> {<br> updateBalance(<span class="hljs-number">0L</span>, <span class="hljs-number">1L</span>, <span class="hljs-literal">null</span>);<br>}<br><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">updateBalance</span><span class="hljs-params">(Long balance, Long id, String username)</span>{<br> userService.lambdaUpdate()<br> .set(User::getBalance, balance)<br> .set(balance == <span class="hljs-number">0</span>, User::getStatus, <span class="hljs-number">2</span>)<br> .eq(id != <span class="hljs-literal">null</span>, User::getId, id)<br> .eq(username != <span class="hljs-literal">null</span>, User::getId, username)<br> .update();<br>}<br></code></pre></td></tr></table></figure><h3 id="4-4-静态工具"><a href="#4-4-静态工具" class="headerlink" title="4.4.静态工具"></a>4.4.静态工具</h3><p>有的时候Service之间也会相互调用,为了避免出现循环依赖问题,MybatisPlus提供一个静态工具类:<code>Db</code>,其中的一些静态方法与<code>IService</code>中方法签名基本一致,也可以帮助我们实现CRUD功能:<br><img src="https://s2.loli.net/2024/09/27/d3SvENtfpOsDrGb.png" alt="image.png"></p><p>示例:</p><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Test</span><br><span class="hljs-keyword">void</span> <span class="hljs-title function_">testDbGet</span><span class="hljs-params">()</span> {<br> <span class="hljs-type">User</span> <span class="hljs-variable">user</span> <span class="hljs-operator">=</span> Db.getById(<span class="hljs-number">1L</span>, User.class);<br> System.out.println(user);<br>}<br><br><span class="hljs-meta">@Test</span><br><span class="hljs-keyword">void</span> <span class="hljs-title function_">testDbList</span><span class="hljs-params">()</span> {<br> <span class="hljs-comment">// 利用Db实现复杂条件查询</span><br> List<User> list = Db.lambdaQuery(User.class)<br> .like(User::getUsername, <span class="hljs-string">"o"</span>)<br> .ge(User::getBalance, <span class="hljs-number">1000</span>)<br> .list();<br> list.forEach(System.out::println);<br>}<br><br><span class="hljs-meta">@Test</span><br><span class="hljs-keyword">void</span> <span class="hljs-title function_">testDbUpdate</span><span class="hljs-params">()</span> {<br> Db.lambdaUpdate(User.class)<br> .set(User::getBalance, <span class="hljs-number">2000</span>)<br> .eq(User::getUsername, <span class="hljs-string">"Rose"</span>);<br>}<br></code></pre></td></tr></table></figure><h2 id="5-拓展功能"><a href="#5-拓展功能" class="headerlink" title="5.拓展功能"></a>5.拓展功能</h2><h3 id="5-1-代码生成插件"><a href="#5-1-代码生成插件" class="headerlink" title="5.1 代码生成插件"></a>5.1 代码生成插件</h3><ol><li>安装插件</li></ol><p><img src="https://s2.loli.net/2024/09/27/njOd2rI7hKbBRvc.png" alt="image.png"></p><ol start="2"><li>配置数据库</li></ol><p><img src="https://s2.loli.net/2024/09/27/5VJDMPjly6dearT.png" alt="image.png"></p><p><img src="https://cdn.nlark.com/yuque/0/2023/png/27967491/1688196793381-60acee0a-500e-4b4d-8b3b-aa4cd3af80c8.png#averageHue=%23f4f4f4&clientId=ucb5715c2-9b63-4&from=paste&height=317&id=u4cc24dc2&originHeight=393&originWidth=629&originalType=binary&ratio=1.2395833730697632&rotation=0&showTitle=false&size=17584&status=done&style=none&taskId=ud0ec4100-04a0-443a-bc58-9a65002e9fe&title=&width=507.4285551622998" alt="image.png"></p><ol start="3"><li><p>生成代码</p><p><img src="https://cdn.nlark.com/yuque/0/2023/png/27967491/1688196861061-152ee240-f56d-47a3-9c74-7fc67a82dd17.png#averageHue=%23d7cbb2&clientId=ucb5715c2-9b63-4&from=paste&height=153&id=u30692977&originHeight=190&originWidth=1109&originalType=binary&ratio=1.2395833730697632&rotation=0&showTitle=false&size=31203&status=done&style=none&taskId=u8299bd93-3309-486f-bd92-3804ced967f&title=&width=894.6554335055492" alt="image.png"></p></li></ol><p><img src="https://cdn.nlark.com/yuque/0/2023/png/27967491/1688197265415-9a6942d2-b53f-4788-aa90-d2308690c8c6.png#averageHue=%23f3e9e8&clientId=ucb5715c2-9b63-4&from=paste&height=489&id=u319836cf&originHeight=606&originWidth=1376&originalType=binary&ratio=1.2395833730697632&rotation=0&showTitle=false&size=129466&status=done&style=none&taskId=u0e1d05ee-af06-445f-9ad7-8769e084e36&title=&width=1110.050384583982" alt="image.png"></p><h3 id="5-2-逻辑删除"><a href="#5-2-逻辑删除" class="headerlink" title="5.2 逻辑删除"></a>5.2 逻辑删除</h3><p>对于一些比较重要的数据,我们往往会采用逻辑删除的方案,即:</p><ul><li>在表中添加一个字段标记数据是否被删除</li><li>当删除数据时把标记置为true</li><li>查询时过滤掉标记为true的数据</li></ul><p>一旦采用了逻辑删除,所有的查询和删除逻辑都要跟着变化,非常麻烦。为了解决这个问题,MybatisPlus就添加了对逻辑删除的支持。<br>:::warning<br><strong>注意</strong>,只有MybatisPlus生成的SQL语句才支持自动的逻辑删除,自定义SQL需要自己手动处理逻辑删除。<br>:::</p><p>例如,我们给<code>address</code>表添加一个逻辑删除字段:</p><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">alter table</span> address<br> <span class="hljs-keyword">add</span> deleted bit <span class="hljs-keyword">default</span> b<span class="hljs-string">'0'</span> <span class="hljs-keyword">null</span> comment <span class="hljs-string">'逻辑删除'</span>;<br></code></pre></td></tr></table></figure><p>然后给<code>Address</code>实体添加<code>deleted</code>字段:<br><img src="https://cdn.nlark.com/yuque/0/2023/png/27967491/1688202948723-552f1db1-84ad-4b78-99ee-28ac6f2f5159.png#averageHue=%23f6f8f3&clientId=ucb5715c2-9b63-4&from=paste&height=389&id=ua4217141&originHeight=482&originWidth=856&originalType=binary&ratio=1.2395833730697632&rotation=0&showTitle=false&size=53297&status=done&style=none&taskId=u2aa3f980-e287-47b8-8b36-3ccbc249d3a&title=&width=690.5545997121282" alt="image.png"></p><p>接下来,我们要在<code>application.yml</code>中配置逻辑删除字段:</p><figure class="highlight yaml"><table><tr><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">mybatis-plus:</span><br> <span class="hljs-attr">global-config:</span><br> <span class="hljs-attr">db-config:</span><br> <span class="hljs-attr">logic-delete-field:</span> <span class="hljs-string">deleted</span> <span class="hljs-comment"># 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)</span><br> <span class="hljs-attr">logic-delete-value:</span> <span class="hljs-number">1</span> <span class="hljs-comment"># 逻辑已删除值(默认为 1)</span><br> <span class="hljs-attr">logic-not-delete-value:</span> <span class="hljs-number">0</span> <span class="hljs-comment"># 逻辑未删除值(默认为 0)</span><br></code></pre></td></tr></table></figure><p>测试:<br>首先,我们执行一个删除操作:</p><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Test</span><br><span class="hljs-keyword">void</span> <span class="hljs-title function_">testDeleteByLogic</span><span class="hljs-params">()</span> {<br> <span class="hljs-comment">// 删除方法与以前没有区别</span><br> addressService.removeById(<span class="hljs-number">59L</span>);<br>}<br></code></pre></td></tr></table></figure><p>方法与普通删除一模一样,但是底层的SQL逻辑变了:<br><img src="https://cdn.nlark.com/yuque/0/2023/png/27967491/1688203124731-99a02bc4-df99-4bcf-a38d-4ebf0e82bf6b.png#averageHue=%23f9fcf7&clientId=ucb5715c2-9b63-4&from=paste&height=312&id=uc2faa0ed&originHeight=387&originWidth=1347&originalType=binary&ratio=1.2395833730697632&rotation=0&showTitle=false&size=141086&status=done&style=none&taskId=u07bf156e-f27f-452a-ad3d-64f60e95502&title=&width=1086.6554273507438" alt="image.png"></p><p>查询一下试试:</p><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Test</span><br><span class="hljs-keyword">void</span> <span class="hljs-title function_">testQuery</span><span class="hljs-params">()</span> {<br> List<Address> list = addressService.list();<br> list.forEach(System.out::println);<br>}<br></code></pre></td></tr></table></figure><p>会发现id为59的确实没有查询出来,而且SQL中也对逻辑删除字段做了判断:<br><img src="https://cdn.nlark.com/yuque/0/2023/png/27967491/1688203269018-391c8134-3442-43a8-893f-08f562155ae7.png#averageHue=%23f9fcf7&clientId=ucb5715c2-9b63-4&from=paste&height=452&id=uedd32412&originHeight=560&originWidth=1328&originalType=binary&ratio=1.2395833730697632&rotation=0&showTitle=false&size=233822&status=done&style=none&taskId=ub7380c63-d002-4cad-a704-6bf18ff3948&title=&width=1071.3276967496568" alt="image.png"></p><p>综上, 开启了逻辑删除功能以后,我们就可以像普通删除一样做CRUD,基本不用考虑代码逻辑问题。还是非常方便的。</p><p>:::warning<br><strong>注意</strong>:<br>逻辑删除本身也有自己的问题,比如:</p><ul><li>会导致数据库表垃圾数据越来越多,从而影响查询效率</li><li>SQL中全都需要对逻辑删除字段做判断,影响查询效率</li></ul><p>因此,我不太推荐采用逻辑删除功能,如果数据不能删除,可以采用把数据迁移到其它表的办法。<br>:::</p><h3 id="5-3-通用枚举"><a href="#5-3-通用枚举" class="headerlink" title="5.3 通用枚举"></a>5.3 通用枚举</h3><p>User类中有一个用户状态字段:<br><img src="https://cdn.nlark.com/yuque/0/2023/png/27967491/1688205450700-35d2bce8-ec2d-42f2-8977-bd7ebf101afd.png#averageHue=%23f5f7f4&clientId=ucdd19af3-b6b9-4&from=paste&height=349&id=u29e826cf&originHeight=432&originWidth=688&originalType=binary&ratio=1.2395833730697632&rotation=0&showTitle=false&size=47157&status=done&style=none&taskId=u2b1c794b-d20f-478d-aeff-87d6913e1f2&title=&width=555.025192291991" alt="image.png"><br>像这种字段我们一般会定义一个枚举,做业务判断的时候就可以直接基于枚举做比较。但是我们数据库采用的是<code>int</code>类型,对应的PO也是<code>Integer</code>。因此业务操作时必须手动把<code>枚举</code>与<code>Integer</code>转换,非常麻烦。</p><p>因此,MybatisPlus提供了一个处理枚举的类型转换器,可以帮我们<strong>把枚举类型与数据库类型自动转换</strong>。</p><h4 id="5-3-1-定义枚举"><a href="#5-3-1-定义枚举" class="headerlink" title="5.3.1.定义枚举"></a>5.3.1.定义枚举</h4><p>我们定义一个用户状态的枚举:<br><img src="https://cdn.nlark.com/yuque/0/2023/png/27967491/1688206435536-1e0ebd0f-4185-4e4b-8dc8-8b2dab235f44.png#averageHue=%23f9fbf8&clientId=ucdd19af3-b6b9-4&from=paste&height=403&id=uf7e016c3&originHeight=499&originWidth=915&originalType=binary&ratio=1.2395833730697632&rotation=0&showTitle=false&size=53038&status=done&style=none&taskId=u5e55ee20-4f76-4516-b0f2-c1a81d103fc&title=&width=738.1512368418192" alt="image.png"><br>代码如下:</p><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> com.itheima.mp.enums;<br><br><span class="hljs-keyword">import</span> com.baomidou.mybatisplus.annotation.EnumValue;<br><span class="hljs-keyword">import</span> lombok.Getter;<br><br><span class="hljs-meta">@Getter</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">enum</span> <span class="hljs-title class_">UserStatus</span> {<br> NORMAL(<span class="hljs-number">1</span>, <span class="hljs-string">"正常"</span>),<br> FREEZE(<span class="hljs-number">2</span>, <span class="hljs-string">"冻结"</span>)<br> ;<br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-type">int</span> value;<br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> String desc;<br><br> UserStatus(<span class="hljs-type">int</span> value, String desc) {<br> <span class="hljs-built_in">this</span>.value = value;<br> <span class="hljs-built_in">this</span>.desc = desc;<br> }<br>}<br><br></code></pre></td></tr></table></figure><p>然后把<code>User</code>类中的<code>status</code>字段改为<code>UserStatus</code> 类型:<br><img src="https://cdn.nlark.com/yuque/0/2023/png/27967491/1688207388430-424b3be8-4c08-4c31-aa73-7b41e271a760.png#averageHue=%23f6f7f2&clientId=ucdd19af3-b6b9-4&from=paste&height=340&id=ua014304a&originHeight=422&originWidth=714&originalType=binary&ratio=1.2395833730697632&rotation=0&showTitle=false&size=47282&status=done&style=none&taskId=u2e238813-09bf-4ab7-8ddf-8c8f581014c&title=&width=575.9999815355836" alt="image.png"></p><p>要让<code>MybatisPlus</code>处理枚举与数据库类型自动转换,我们必须告诉<code>MybatisPlus</code>,枚举中的哪个字段的值作为数据库值。<br><code>MybatisPlus</code>提供了<code>@EnumValue</code>注解来标记枚举属性:<br><img src="https://cdn.nlark.com/yuque/0/2023/png/27967491/1688206653554-4bc9d7a3-3c39-4981-a8ba-6f06ba7df734.png#averageHue=%23f8fbf6&clientId=ucdd19af3-b6b9-4&from=paste&height=418&id=ua72da7b8&originHeight=518&originWidth=635&originalType=binary&ratio=1.2395833730697632&rotation=0&showTitle=false&size=67892&status=done&style=none&taskId=ua1ac546f-70cf-4525-a941-afce46f09a2&title=&width=512.2688911415904" alt="image.png"></p><h4 id="5-3-2-配置枚举处理器"><a href="#5-3-2-配置枚举处理器" class="headerlink" title="5.3.2.配置枚举处理器"></a>5.3.2.配置枚举处理器</h4><p>在application.yaml文件中添加配置:</p><figure class="highlight yaml"><table><tr><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">mybatis-plus:</span><br> <span class="hljs-attr">configuration:</span><br> <span class="hljs-attr">default-enum-type-handler:</span> <span class="hljs-string">com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler</span><br></code></pre></td></tr></table></figure><h4 id="5-3-3-测试"><a href="#5-3-3-测试" class="headerlink" title="5.3.3.测试"></a>5.3.3.测试</h4><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Test</span><br><span class="hljs-keyword">void</span> <span class="hljs-title function_">testService</span><span class="hljs-params">()</span> {<br> List<User> list = userService.list();<br> list.forEach(System.out::println);<br>}<br></code></pre></td></tr></table></figure><p>最终,查询出的<code>User</code>类的<code>status</code>字段会是枚举类型:<br><img src="https://cdn.nlark.com/yuque/0/2023/png/27967491/1688207514989-da3d683d-641b-4a05-8330-ec1818604dd4.png#averageHue=%23f7faf3&clientId=ucdd19af3-b6b9-4&from=paste&height=272&id=u5c65f589&originHeight=337&originWidth=758&originalType=binary&ratio=1.2395833730697632&rotation=0&showTitle=false&size=70020&status=done&style=none&taskId=ub88fd551-ea20-4385-9696-bc4800cc4d4&title=&width=611.4957787170481" alt="image.png"></p><h3 id="5-4-字段类型处理器"><a href="#5-4-字段类型处理器" class="headerlink" title="5.4 字段类型处理器"></a>5.4 字段类型处理器</h3><p>数据库的user表中有一个<code>info</code>字段,是JSON类型:<br><img src="https://cdn.nlark.com/yuque/0/2023/png/27967491/1688212092835-57dab221-e046-48f4-907a-d3bf18fc2a30.png#averageHue=%23f7f6f4&clientId=ucdd19af3-b6b9-4&from=paste&height=245&id=u3dcab959&originHeight=304&originWidth=761&originalType=binary&ratio=1.2395833730697632&rotation=0&showTitle=false&size=31811&status=done&style=none&taskId=ubba2f760-5128-4d52-9412-f54fceaf94a&title=&width=613.9159467066934" alt="image.png"><br>格式像这样:</p><figure class="highlight json"><table><tr><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">{</span><span class="hljs-attr">"age"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">20</span><span class="hljs-punctuation">,</span> <span class="hljs-attr">"intro"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"佛系青年"</span><span class="hljs-punctuation">,</span> <span class="hljs-attr">"gender"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"male"</span><span class="hljs-punctuation">}</span><br></code></pre></td></tr></table></figure><p>而目前<code>User</code>实体类中却是<code>String</code>类型:<br><img src="https://cdn.nlark.com/yuque/0/2023/png/27967491/1688212175474-81e91aec-2ef3-4305-9dc6-28d20e139688.png#averageHue=%23f5f8f4&clientId=ucdd19af3-b6b9-4&from=paste&height=310&id=u65d78d1d&originHeight=384&originWidth=814&originalType=binary&ratio=1.2395833730697632&rotation=0&showTitle=false&size=46824&status=done&style=none&taskId=u674ba30b-a2fd-4c65-9c1f-acc92e124de&title=&width=656.6722478570939" alt="image.png"></p><p>这样以来,我们要读取info中的属性时就非常不方便。如果要方便获取,info的类型最好是一个<code>Map</code>或者实体类。<br>而一旦我们把<code>info</code>改为<code>对象</code>类型,就需要在写入数据库是手动转为<code>String</code>,再读取数据库时,手动转换为<code>对象</code>,这会非常麻烦。</p><p>因此MybatisPlus提供了很多特殊类型字段的类型处理器,解决特殊字段类型与数据库类型转换的问题。例如处理JSON就可以使用<code>JacksonTypeHandler</code>处理器。</p><p>接下来,我们就来看看这个处理器该如何使用。</p><h4 id="5-4-1-定义实体"><a href="#5-4-1-定义实体" class="headerlink" title="5.4.1 定义实体"></a>5.4.1 定义实体</h4><p>首先,我们定义一个单独实体类来与info字段的属性匹配:<br><img src="https://cdn.nlark.com/yuque/0/2023/png/27967491/1688215509372-226dae45-89f8-46e5-9cf2-a2d94dafebda.png#averageHue=%23f9fbf8&clientId=ucdd19af3-b6b9-4&from=paste&height=353&id=u6e9cbd35&originHeight=437&originWidth=860&originalType=binary&ratio=1.2395833730697632&rotation=0&showTitle=false&size=44479&status=done&style=none&taskId=u3ef96d28-bf45-4adb-9583-cf6c2ce9cf8&title=&width=693.7814903649886" alt="image.png"><br>代码如下:</p><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> com.itheima.mp.domain.po;<br><br><span class="hljs-keyword">import</span> lombok.Data;<br><br><span class="hljs-meta">@Data</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">UserInfo</span> {<br> <span class="hljs-keyword">private</span> Integer age;<br> <span class="hljs-keyword">private</span> String intro;<br> <span class="hljs-keyword">private</span> String gender;<br>}<br></code></pre></td></tr></table></figure><h4 id="5-4-2-使用类型处理器"><a href="#5-4-2-使用类型处理器" class="headerlink" title="5.4.2 使用类型处理器"></a>5.4.2 使用类型处理器</h4><p>接下来,将User类的info字段修改为UserInfo类型,并声明类型处理器:<br><img src="https://cdn.nlark.com/yuque/0/2023/png/27967491/1688215729313-c3987a13-e3fe-40f6-8028-e837fdfbee63.png#averageHue=%23f6f8f4&clientId=ucdd19af3-b6b9-4&from=paste&height=309&id=u6e6612c3&originHeight=383&originWidth=978&originalType=binary&ratio=1.2395833730697632&rotation=0&showTitle=false&size=49918&status=done&style=none&taskId=u6a82025a-637a-4a02-bd01-036a53384b2&title=&width=788.9747646243708" alt="image.png"></p><p>测试可以发现,所有数据都正确封装到UserInfo当中了:<br><img src="https://cdn.nlark.com/yuque/0/2023/png/27967491/1688215838909-45e8286d-85f4-4521-bede-9d3c8e8be109.png#averageHue=%23f9fbf6&clientId=ucdd19af3-b6b9-4&from=paste&height=278&id=u822a8e35&originHeight=345&originWidth=1034&originalType=binary&ratio=1.2395833730697632&rotation=0&showTitle=false&size=92222&status=done&style=none&taskId=u80ecd7f1-8bf6-4609-8484-22a05c2093f&title=&width=834.1512337644166" alt="image.png"></p><h3 id="5-5配置加密"><a href="#5-5配置加密" class="headerlink" title="5.5配置加密"></a>5.5配置加密</h3><p>目前我们配置文件中的很多参数都是明文,如果开发人员发生流动,很容易导致敏感信息的泄露。所以MybatisPlus支持配置文件的加密和解密功能。</p><p>我们以数据库的用户名和密码为例。</p><h4 id="5-5-1-生成秘钥"><a href="#5-5-1-生成秘钥" class="headerlink" title="5.5.1.生成秘钥"></a>5.5.1.生成秘钥</h4><p>首先,我们利用AES工具生成一个随机秘钥,然后对用户名、密码加密:</p><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> com.itheima.mp;<br><br><span class="hljs-keyword">import</span> com.baomidou.mybatisplus.core.toolkit.AES;<br><span class="hljs-keyword">import</span> org.junit.jupiter.api.Test;<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">MpDemoApplicationTests</span> {<br> <span class="hljs-meta">@Test</span><br> <span class="hljs-keyword">void</span> <span class="hljs-title function_">contextLoads</span><span class="hljs-params">()</span> {<br> <span class="hljs-comment">// 生成 16 位随机 AES 密钥</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">randomKey</span> <span class="hljs-operator">=</span> AES.generateRandomKey();<br> System.out.println(<span class="hljs-string">"randomKey = "</span> + randomKey);<br><br> <span class="hljs-comment">// 利用密钥对用户名加密</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">username</span> <span class="hljs-operator">=</span> AES.encrypt(<span class="hljs-string">"root"</span>, randomKey);<br> System.out.println(<span class="hljs-string">"username = "</span> + username);<br><br> <span class="hljs-comment">// 利用密钥对用户名加密</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">password</span> <span class="hljs-operator">=</span> AES.encrypt(<span class="hljs-string">"MySQL123"</span>, randomKey);<br> System.out.println(<span class="hljs-string">"password = "</span> + password);<br><br> }<br>}<br></code></pre></td></tr></table></figure><p>打印结果如下:</p><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java">randomKey = 6234633<span class="hljs-type">a66fb399f</span><br><span class="hljs-variable">username</span> <span class="hljs-operator">=</span> px2bAbnUfiY8K/IgsKvscg==<br>password = FGvCSEaOuga3ulDAsxw68Q==<br></code></pre></td></tr></table></figure><h4 id="5-5-2-修改配置"><a href="#5-5-2-修改配置" class="headerlink" title="5.5.2.修改配置"></a>5.5.2.修改配置</h4><p>修改application.yaml文件,把jdbc的用户名、密码修改为刚刚加密生成的密文:</p><figure class="highlight yaml"><table><tr><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">spring:</span><br> <span class="hljs-attr">datasource:</span><br> <span class="hljs-attr">url:</span> <span class="hljs-string">jdbc:mysql://127.0.0.1:3306/mp?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true</span><br> <span class="hljs-attr">driver-class-name:</span> <span class="hljs-string">com.mysql.cj.jdbc.Driver</span><br> <span class="hljs-attr">username:</span> <span class="hljs-string">mpw:QWWVnk1Oal3258x5rVhaeQ==</span> <span class="hljs-comment"># 密文要以 mpw:开头</span><br> <span class="hljs-attr">password:</span> <span class="hljs-string">mpw:EUFmeH3cNAzdRGdOQcabWg==</span> <span class="hljs-comment"># 密文要以 mpw:开头</span><br></code></pre></td></tr></table></figure><h4 id="5-5-3-测试"><a href="#5-5-3-测试" class="headerlink" title="5.5.3.测试"></a>5.5.3.测试</h4><p>在启动项目的时候,需要把刚才生成的秘钥添加到启动参数中,像这样:</p><figure class="highlight yaml"><table><tr><td class="code"><pre><code class="hljs yaml"><span class="hljs-string">--mpw.key=6234633a66fb399f</span><br></code></pre></td></tr></table></figure><p>单元测试的时候不能添加启动参数,所以要在测试类的注解上配置:<br><img src="https://cdn.nlark.com/yuque/0/2023/png/27967491/1688218131368-7bba8307-998f-4709-86d1-b5f83d0b363d.png#averageHue=%23f5f7f3&clientId=ucdd19af3-b6b9-4&from=paste&height=392&id=u0b3b23da&originHeight=486&originWidth=1089&originalType=binary&ratio=1.2395833730697632&rotation=0&showTitle=false&size=87247&status=done&style=none&taskId=ue867a34c-18ac-488a-b0c9-a77bdf4d99a&title=&width=878.5209802412472" alt="image.png"></p><p>然后随意运行一个单元测试,可以发现数据库查询正常。</p><h2 id="6-插件功能"><a href="#6-插件功能" class="headerlink" title="6.插件功能"></a>6.插件功能</h2><p>MybatisPlus提供了很多的插件功能,进一步拓展其功能。目前已有的插件有:</p><ul><li><code>PaginationInnerInterceptor</code>:自动分页</li><li><code>TenantLineInnerInterceptor</code>:多租户</li><li><code>DynamicTableNameInnerInterceptor</code>:动态表名</li><li><code>OptimisticLockerInnerInterceptor</code>:乐观锁</li><li><code>IllegalSQLInnerInterceptor</code>:sql 性能规范</li><li><code>BlockAttackInnerInterceptor</code>:防止全表更新与删除</li></ul><p>:::warning<br><strong>注意:</strong><br>使用多个分页插件的时候需要注意插件定义顺序,建议使用顺序如下:</p><ul><li><p>多租户,动态表名</p></li><li><p>分页,乐观锁</p></li><li><p>sql 性能规范,防止全表更新与删除</p><p>:::</p></li></ul><p>这里我们以分页插件为里来学习插件的用法。</p><h2 id="6-1-分页插件"><a href="#6-1-分页插件" class="headerlink" title="6.1.分页插件"></a>6.1.分页插件</h2><p>在未引入分页插件的情况下,<code>MybatisPlus</code>是不支持分页功能的,<code>IService</code>和<code>BaseMapper</code>中的分页方法都无法正常起效。<br>所以,我们必须配置分页插件。</p><h3 id="6-1-1-配置分页插件"><a href="#6-1-1-配置分页插件" class="headerlink" title="6.1.1.配置分页插件"></a>6.1.1.配置分页插件</h3><p>在项目中新建一个配置类:<br><img src="https://cdn.nlark.com/yuque/0/2023/png/27967491/1688220120678-5d7b8c50-05f8-48f0-b32b-21a4e34dc56f.png#averageHue=%23f9fbf8&clientId=ucdd19af3-b6b9-4&from=paste&height=327&id=u5dedcfcd&originHeight=405&originWidth=896&originalType=binary&ratio=1.2395833730697632&rotation=0&showTitle=false&size=42888&status=done&style=none&taskId=ufe639d17-598b-40d7-b932-3133320d017&title=&width=722.8235062407323" alt="image.png"><br>其代码如下:</p><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> com.itheima.mp.config;<br><br><span class="hljs-keyword">import</span> com.baomidou.mybatisplus.annotation.DbType;<br><span class="hljs-keyword">import</span> com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;<br><span class="hljs-keyword">import</span> com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;<br><span class="hljs-keyword">import</span> org.springframework.context.annotation.Bean;<br><span class="hljs-keyword">import</span> org.springframework.context.annotation.Configuration;<br><br><span class="hljs-meta">@Configuration</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">MybatisConfig</span> {<br><br> <span class="hljs-meta">@Bean</span><br> <span class="hljs-keyword">public</span> MybatisPlusInterceptor <span class="hljs-title function_">mybatisPlusInterceptor</span><span class="hljs-params">()</span> {<br> <span class="hljs-comment">// 初始化核心插件</span><br> <span class="hljs-type">MybatisPlusInterceptor</span> <span class="hljs-variable">interceptor</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">MybatisPlusInterceptor</span>();<br> <span class="hljs-comment">// 添加分页插件</span><br> interceptor.addInnerInterceptor(<span class="hljs-keyword">new</span> <span class="hljs-title class_">PaginationInnerInterceptor</span>(DbType.MYSQL));<br> <span class="hljs-keyword">return</span> interceptor;<br> }<br>}<br></code></pre></td></tr></table></figure><h3 id="6-1-2-分页API"><a href="#6-1-2-分页API" class="headerlink" title="6.1.2.分页API"></a>6.1.2.分页API</h3><p>编写一个分页查询的测试:</p><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Test</span><br><span class="hljs-keyword">void</span> <span class="hljs-title function_">testPageQuery</span><span class="hljs-params">()</span> {<br> <span class="hljs-comment">// 1.分页查询,new Page()的两个参数分别是:页码、每页大小</span><br> Page<User> p = userService.page(<span class="hljs-keyword">new</span> <span class="hljs-title class_">Page</span><>(<span class="hljs-number">2</span>, <span class="hljs-number">2</span>));<br> <span class="hljs-comment">// 2.总条数</span><br> System.out.println(<span class="hljs-string">"total = "</span> + p.getTotal());<br> <span class="hljs-comment">// 3.总页数</span><br> System.out.println(<span class="hljs-string">"pages = "</span> + p.getPages());<br> <span class="hljs-comment">// 4.数据</span><br> List<User> records = p.getRecords();<br> records.forEach(System.out::println);<br>}<br></code></pre></td></tr></table></figure><p>运行的SQL如下:<br><img src="https://cdn.nlark.com/yuque/0/2023/png/27967491/1688220648583-06cf029a-4d8e-4002-8b07-dc4db3787fa4.png#averageHue=%23f9fcf7&clientId=ucdd19af3-b6b9-4&from=paste&height=649&id=u381c4f60&originHeight=805&originWidth=1336&originalType=binary&ratio=1.2395833730697632&rotation=0&showTitle=false&size=300593&status=done&style=none&taskId=u05c9d87e-599a-4d44-8b0c-62900316533&title=&width=1077.7814780553776" alt="image.png"></p><p>这里用到了分页参数,Page,即可以支持分页参数,也可以支持排序参数。常见的API如下:</p><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-type">int</span> <span class="hljs-variable">pageNo</span> <span class="hljs-operator">=</span> <span class="hljs-number">1</span>, pageSize = <span class="hljs-number">5</span>;<br><span class="hljs-comment">// 分页参数</span><br>Page<User> page = Page.of(pageNo, pageSize);<br><span class="hljs-comment">// 排序参数, 通过OrderItem来指定</span><br>page.addOrder(<span class="hljs-keyword">new</span> <span class="hljs-title class_">OrderItem</span>(<span class="hljs-string">"balance"</span>, <span class="hljs-literal">false</span>));<br><br>userService.page(page);<br></code></pre></td></tr></table></figure><h2 id="6-2-通用分页实体"><a href="#6-2-通用分页实体" class="headerlink" title="6.2.通用分页实体"></a>6.2.通用分页实体</h2><p>现在要实现一个用户分页查询的接口,接口规范如下:</p><table><thead><tr><th><strong>参数</strong></th><th><strong>说明</strong></th></tr></thead><tbody><tr><td>请求方式</td><td>GET</td></tr><tr><td>请求路径</td><td>/users/page</td></tr><tr><td>请求参数</td><td>```json</td></tr><tr><td>{</td><td></td></tr><tr><td>“pageNo”: 1,</td><td></td></tr><tr><td>“pageSize”: 5,</td><td></td></tr><tr><td>“sortBy”: “balance”,</td><td></td></tr><tr><td>“isAsc”: false</td><td></td></tr><tr><td>}</td><td></td></tr></tbody></table><figure class="highlight json"><table><tr><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"total"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">100006</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"pages"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">50003</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"list"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><br> <span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1685100878975279298</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"username"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"user_9****"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"info"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"age"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">24</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"intro"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"英文老师"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"gender"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"female"</span><br> <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"status"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"正常"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"balance"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">2000</span><br> <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span><br> <span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"id"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1685100878975279299</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"username"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"user_9****"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"info"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span><br> <span class="hljs-attr">"age"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">24</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"intro"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"英文老师"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"gender"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"female"</span><br> <span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"status"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"正常"</span><span class="hljs-punctuation">,</span><br> <span class="hljs-attr">"balance"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">2000</span><br> <span class="hljs-punctuation">}</span><br> <span class="hljs-punctuation">]</span><br><span class="hljs-punctuation">}</span><br></code></pre></td></tr></table></figure><p> |<br>| 特殊说明 | •如果排序字段为空,默认按照更新时间排序<br>•排序字段不为空,则按照排序字段排序 |</p><p>这里需要定义3个实体:</p><ul><li><code>PageQuery</code>:分页查询条件的实体,包含分页、排序参数</li><li><code>PageDTO</code>:分页结果实体,包含总条数、总页数、当前页数据</li><li><code>UserVO</code>:用户页面视图实体</li></ul><p>接下来我们就按照WEB开发的过程来实现这个接口。<br>首先,我们在项目中引入<code>spring-boot-starter-web</code>依赖:</p><figure class="highlight xml"><table><tr><td class="code"><pre><code class="hljs xml"><span class="hljs-comment"><!-- web依赖 --></span><br><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.springframework.boot<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>spring-boot-starter-web<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br><span class="hljs-comment"><!-- hutool 工具包--></span><br><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>cn.hutool<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>hutool-all<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">version</span>></span>5.8.11<span class="hljs-tag"></<span class="hljs-name">version</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br></code></pre></td></tr></table></figure><p>然后,按<code>alt+8</code>打开<code>service</code>控制台,然后添加一个<code>SpringBoot</code>启动项:<br><img src="https://cdn.nlark.com/yuque/0/2023/png/27967491/1688286132717-ae9c6f4d-61a4-4b67-8da1-f4a8a7087ad7.png#averageHue=%23eff2ee&clientId=uba0f15c0-624d-4&from=paste&height=361&id=uc2971250&originHeight=448&originWidth=1127&originalType=binary&ratio=1.2395833730697632&rotation=0&showTitle=false&size=61466&status=done&style=none&taskId=u5c9cacfa-5ca3-4210-8749-71b5221414d&title=&width=909.1764414434211" alt="image.png"><br>弹窗中选择<code>Spring Boot</code>:<br><img src="https://cdn.nlark.com/yuque/0/2023/png/27967491/1688286160462-818bf344-ed58-4ad5-b8aa-68948ce07025.png#averageHue=%23f5f4f2&clientId=uba0f15c0-624d-4&from=paste&height=465&id=u2345aaa0&originHeight=576&originWidth=314&originalType=binary&ratio=1.2395833730697632&rotation=0&showTitle=false&size=37539&status=done&style=none&taskId=u18919e97-fda7-4d6b-88cf-71ffc02b73f&title=&width=253.31091624954234" alt="image.png"><br>弹窗中填写信息:<br><img src="https://cdn.nlark.com/yuque/0/2023/png/27967491/1688286326883-b48cd164-9f97-413f-9b8d-b88cb6fff6f4.png#averageHue=%23f3f1f1&clientId=uba0f15c0-624d-4&from=paste&height=608&id=u7ac5c1e6&originHeight=754&originWidth=1400&originalType=binary&ratio=1.2395833730697632&rotation=0&showTitle=false&size=63187&status=done&style=none&taskId=u8236bb84-f4d7-4993-94d6-676e7bc2812&title=&width=1129.4117285011444" alt="image.png"><br>其中不要忘了配置我们之前添加的数据加密的秘钥。</p><h3 id="6-2-1-实体"><a href="#6-2-1-实体" class="headerlink" title="6.2.1.实体"></a>6.2.1.实体</h3><p>首先是请求参数的<code>PageQuery</code>实体:<br><img src="https://cdn.nlark.com/yuque/0/2023/png/27967491/1690626203583-31dc37fe-6851-4aad-9438-8e1ebef5ce46.png#averageHue=%23f9fbf8&clientId=uacc03b97-2f4b-4&from=paste&height=428&id=u6de6995d&originHeight=530&originWidth=842&originalType=binary&ratio=1.2395833730697632&rotation=0&showTitle=false&size=52422&status=done&style=none&taskId=u0d14f487-0e76-4433-aa87-a89efcba08e&title=&width=679.2604824271168" alt="image.png"><br><code>PageQuery</code>是前端提交的查询参数,一般包含四个属性:</p><ul><li><code>pageNo</code>:页码</li><li><code>pageSize</code>:每页数据条数</li><li><code>sortBy</code>:排序字段</li><li><code>isAsc</code>:是否升序</li></ul><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> com.itheima.mp.domain.query;<br><br><span class="hljs-keyword">import</span> lombok.Data;<br><br><span class="hljs-meta">@Data</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">PageQuery</span> {<br> <span class="hljs-keyword">private</span> Integer pageNo;<br> <span class="hljs-keyword">private</span> Integer pageSize;<br> <span class="hljs-keyword">private</span> String sortBy;<br> <span class="hljs-keyword">private</span> Boolean isAsc;<br>}<br></code></pre></td></tr></table></figure><p>然后我们定义一个<code>UserVO</code>实体:<br><img src="https://cdn.nlark.com/yuque/0/2023/png/27967491/1690626250731-71aa58a9-80af-4191-91e7-e22c21ada79a.png#averageHue=%23f9fbf8&clientId=uacc03b97-2f4b-4&from=paste&height=355&id=u6a9535f1&originHeight=440&originWidth=834&originalType=binary&ratio=1.2395833730697632&rotation=0&showTitle=false&size=42155&status=done&style=none&taskId=ue520cb52-d625-447c-9e92-e2cd2ef7153&title=&width=672.8067011213959" alt="image.png"><br>代码如下:</p><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> com.itheima.mp.domain.vo;<br><br><span class="hljs-keyword">import</span> com.itheima.mp.domain.po.UserInfo;<br><span class="hljs-keyword">import</span> com.itheima.mp.enums.UserStatus;<br><span class="hljs-keyword">import</span> lombok.Data;<br><br><span class="hljs-meta">@Data</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">UserVO</span> {<br><br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 用户id</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-keyword">private</span> Long id;<br><br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 用户名</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-keyword">private</span> String username;<br><br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 详细信息</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-keyword">private</span> UserInfo info;<br><br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 使用状态(1正常 2冻结)</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-keyword">private</span> UserStatus status;<br><br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 账户余额</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-keyword">private</span> Integer balance;<br>}<br></code></pre></td></tr></table></figure><p>最后,则是分页实体PageDTO:<br><img src="https://cdn.nlark.com/yuque/0/2023/png/27967491/1690626340460-6e86f884-5110-47b9-a6ca-429d3bcb9e47.png#averageHue=%23f9fbf8&clientId=uacc03b97-2f4b-4&from=paste&height=403&id=u4b113cac&originHeight=499&originWidth=900&originalType=binary&ratio=1.2395833730697632&rotation=0&showTitle=false&size=48491&status=done&style=none&taskId=ubd543d13-2a93-47cb-afc3-2884221e2f7&title=&width=726.0503968935927" alt="image.png"></p><p>代码如下:</p><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> com.itheima.mp.domain.dto;<br><br><span class="hljs-keyword">import</span> lombok.AllArgsConstructor;<br><span class="hljs-keyword">import</span> lombok.Data;<br><span class="hljs-keyword">import</span> lombok.NoArgsConstructor;<br><br><span class="hljs-keyword">import</span> java.util.List;<br><br><span class="hljs-meta">@Data</span><br><span class="hljs-meta">@NoArgsConstructor</span><br><span class="hljs-meta">@AllArgsConstructor</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">PageDTO</span><T> {<br> <span class="hljs-keyword">private</span> Integer total;<br> <span class="hljs-keyword">private</span> Integer pages;<br> <span class="hljs-keyword">private</span> List<T> list;<br>}<br></code></pre></td></tr></table></figure><h3 id="6-2-2-开发接口"><a href="#6-2-2-开发接口" class="headerlink" title="6.2.2.开发接口"></a>6.2.2.开发接口</h3><p>我们定义一个<code>UserController</code>,在<code>controller</code>中我们定义分页查询用户的接口:</p><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> com.itheima.mp.controller;<br><br><span class="hljs-keyword">import</span> com.itheima.mp.domain.dto.PageDTO;<br><span class="hljs-keyword">import</span> com.itheima.mp.domain.query.PageQuery;<br><span class="hljs-keyword">import</span> com.itheima.mp.domain.vo.UserVO;<br><span class="hljs-keyword">import</span> com.itheima.mp.service.UserService;<br><span class="hljs-keyword">import</span> lombok.RequiredArgsConstructor;<br><span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.GetMapping;<br><span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.RequestMapping;<br><span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.RestController;<br><br><span class="hljs-meta">@RestController</span><br><span class="hljs-meta">@RequestMapping("users")</span><br><span class="hljs-meta">@RequiredArgsConstructor</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">UserController</span> {<br><br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> UserService userService;<br><br> <span class="hljs-meta">@GetMapping("/page")</span><br> <span class="hljs-keyword">public</span> PageDTO<UserVO> <span class="hljs-title function_">queryUserByPage</span><span class="hljs-params">(PageQuery query)</span>{<br> <span class="hljs-keyword">return</span> userService.queryUserByPage(query);<br> }<br>}<br><br></code></pre></td></tr></table></figure><p>然后在<code>UserService</code>中创建<code>queryUserByPage</code>方法:</p><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java">PageDTO<UserVO> <span class="hljs-title function_">queryUserByPage</span><span class="hljs-params">(PageQuery query)</span>;<br></code></pre></td></tr></table></figure><p>接下来,在UserServiceImpl中实现该方法:</p><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Override</span><br><span class="hljs-keyword">public</span> PageDTO<UserVO> <span class="hljs-title function_">queryUserByPage</span><span class="hljs-params">(PageQuery query)</span> {<br> <span class="hljs-comment">// 1.构建条件</span><br> <span class="hljs-comment">// 1.1.分页条件</span><br> Page<User> page = Page.of(query.getPageNo(), query.getPageSize());<br> <span class="hljs-comment">// 1.2.排序条件</span><br> <span class="hljs-keyword">if</span> (query.getSortBy() != <span class="hljs-literal">null</span>) {<br> page.addOrder(<span class="hljs-keyword">new</span> <span class="hljs-title class_">OrderItem</span>(query.getSortBy(), query.getIsAsc()));<br> }<span class="hljs-keyword">else</span>{<br> <span class="hljs-comment">// 默认按照更新时间排序</span><br> page.addOrder(<span class="hljs-keyword">new</span> <span class="hljs-title class_">OrderItem</span>(<span class="hljs-string">"update_time"</span>, <span class="hljs-literal">false</span>));<br> }<br> <span class="hljs-comment">// 2.查询</span><br> page(page);<br> <span class="hljs-comment">// 3.数据非空校验</span><br> List<User> records = page.getRecords();<br> <span class="hljs-keyword">if</span> (records == <span class="hljs-literal">null</span> || records.size() <= <span class="hljs-number">0</span>) {<br> <span class="hljs-comment">// 无数据,返回空结果</span><br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">PageDTO</span><>(page.getTotal(), page.getPages(), Collections.emptyList());<br> }<br> <span class="hljs-comment">// 4.有数据,转换</span><br> List<UserVO> list = BeanUtil.copyToList(records, UserVO.class);<br> <span class="hljs-comment">// 5.封装返回</span><br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">PageDTO</span><UserVO>(page.getTotal(), page.getPages(), list);<br>}<br></code></pre></td></tr></table></figure><p>最后,为了让UserStatus枚举可以展示为文字描述,再给UserStatus中的desc字段添加<code>@JsonValue</code>注解:<br><img src="https://cdn.nlark.com/yuque/0/2023/png/27967491/1690626889628-2c81911b-a714-410e-9cdf-76aa27946746.png#averageHue=%23f9fbf6&clientId=uacc03b97-2f4b-4&from=paste&height=443&id=ud16fa160&originHeight=549&originWidth=751&originalType=binary&ratio=1.2395833730697632&rotation=0&showTitle=false&size=74054&status=done&style=none&taskId=u4d9fbbd2-ddee-48f9-9d79-30479deee8d&title=&width=605.8487200745424" alt="image.png"><br>启动项目,在页面查看:<br><img src="https://cdn.nlark.com/yuque/0/2023/png/27967491/1690626977575-46df0c79-8d99-4965-a1fa-7ef4379f81ba.png#averageHue=%23f8f8fe&clientId=uacc03b97-2f4b-4&from=paste&height=603&id=uc72ff4b0&originHeight=748&originWidth=749&originalType=binary&ratio=1.2395833730697632&rotation=0&showTitle=false&size=77622&status=done&style=none&taskId=u0c90cc4d-4b96-48bb-89b9-fe1fe18fa6f&title=&width=604.2352747481121" alt="image.png"></p><h3 id="6-2-3-改造PageQuery实体"><a href="#6-2-3-改造PageQuery实体" class="headerlink" title="6.2.3.改造PageQuery实体"></a>6.2.3.改造PageQuery实体</h3><p>在刚才的代码中,从<code>PageQuery</code>到<code>MybatisPlus</code>的<code>Page</code>之间转换的过程还是比较麻烦的。<br>我们完全可以在<code>PageQuery</code>这个实体中定义一个工具方法,简化开发。<br>像这样:</p><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> com.itheima.mp.domain.query;<br><br><span class="hljs-keyword">import</span> com.baomidou.mybatisplus.core.metadata.OrderItem;<br><span class="hljs-keyword">import</span> com.baomidou.mybatisplus.extension.plugins.pagination.Page;<br><span class="hljs-keyword">import</span> lombok.Data;<br><br><span class="hljs-meta">@Data</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">PageQuery</span> {<br> <span class="hljs-keyword">private</span> Integer pageNo;<br> <span class="hljs-keyword">private</span> Integer pageSize;<br> <span class="hljs-keyword">private</span> String sortBy;<br> <span class="hljs-keyword">private</span> Boolean isAsc;<br><br> <span class="hljs-keyword">public</span> <T> Page<T> <span class="hljs-title function_">toMpPage</span><span class="hljs-params">(OrderItem ... orders)</span>{<br> <span class="hljs-comment">// 1.分页条件</span><br> Page<T> p = Page.of(pageNo, pageSize);<br> <span class="hljs-comment">// 2.排序条件</span><br> <span class="hljs-comment">// 2.1.先看前端有没有传排序字段</span><br> <span class="hljs-keyword">if</span> (sortBy != <span class="hljs-literal">null</span>) {<br> p.addOrder(<span class="hljs-keyword">new</span> <span class="hljs-title class_">OrderItem</span>(sortBy, isAsc));<br> <span class="hljs-keyword">return</span> p;<br> }<br> <span class="hljs-comment">// 2.2.再看有没有手动指定排序字段</span><br> <span class="hljs-keyword">if</span>(orders != <span class="hljs-literal">null</span>){<br> p.addOrder(orders);<br> }<br> <span class="hljs-keyword">return</span> p;<br> }<br><br> <span class="hljs-keyword">public</span> <T> Page<T> <span class="hljs-title function_">toMpPage</span><span class="hljs-params">(String defaultSortBy, <span class="hljs-type">boolean</span> isAsc)</span>{<br> <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.toMpPage(<span class="hljs-keyword">new</span> <span class="hljs-title class_">OrderItem</span>(defaultSortBy, isAsc));<br> }<br><br> <span class="hljs-keyword">public</span> <T> Page<T> <span class="hljs-title function_">toMpPageDefaultSortByCreateTimeDesc</span><span class="hljs-params">()</span> {<br> <span class="hljs-keyword">return</span> toMpPage(<span class="hljs-string">"create_time"</span>, <span class="hljs-literal">false</span>);<br> }<br><br> <span class="hljs-keyword">public</span> <T> Page<T> <span class="hljs-title function_">toMpPageDefaultSortByUpdateTimeDesc</span><span class="hljs-params">()</span> {<br> <span class="hljs-keyword">return</span> toMpPage(<span class="hljs-string">"update_time"</span>, <span class="hljs-literal">false</span>);<br> }<br>}<br><br></code></pre></td></tr></table></figure><p>这样我们在开发也时就可以省去对从<code>PageQuery</code>到<code>Page</code>的的转换:</p><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 1.构建条件</span><br>Page<User> page = query.toMpPageDefaultSortByCreateTimeDesc();<br></code></pre></td></tr></table></figure><h3 id="6-2-4-改造PageDTO实体"><a href="#6-2-4-改造PageDTO实体" class="headerlink" title="6.2.4.改造PageDTO实体"></a>6.2.4.改造PageDTO实体</h3><p>在查询出分页结果后,数据的非空校验,数据的vo转换都是模板代码,编写起来很麻烦。</p><p>我们完全可以将其封装到PageDTO的工具方法中,简化整个过程:</p><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> com.itheima.mp.domain.dto;<br><br><span class="hljs-keyword">import</span> cn.hutool.core.bean.BeanUtil;<br><span class="hljs-keyword">import</span> com.baomidou.mybatisplus.extension.plugins.pagination.Page;<br><span class="hljs-keyword">import</span> lombok.AllArgsConstructor;<br><span class="hljs-keyword">import</span> lombok.Data;<br><span class="hljs-keyword">import</span> lombok.NoArgsConstructor;<br><br><span class="hljs-keyword">import</span> java.util.Collections;<br><span class="hljs-keyword">import</span> java.util.List;<br><span class="hljs-keyword">import</span> java.util.function.Function;<br><span class="hljs-keyword">import</span> java.util.stream.Collectors;<br><br><span class="hljs-meta">@Data</span><br><span class="hljs-meta">@NoArgsConstructor</span><br><span class="hljs-meta">@AllArgsConstructor</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">PageDTO</span><V> {<br> <span class="hljs-keyword">private</span> Long total;<br> <span class="hljs-keyword">private</span> Long pages;<br> <span class="hljs-keyword">private</span> List<V> list;<br><br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 返回空分页结果</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> p MybatisPlus的分页结果</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> <V> 目标VO类型</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> <P> 原始PO类型</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@return</span> VO的分页对象</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <V, P> PageDTO<V> <span class="hljs-title function_">empty</span><span class="hljs-params">(Page<P> p)</span>{<br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">PageDTO</span><>(p.getTotal(), p.getPages(), Collections.emptyList());<br> }<br><br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 将MybatisPlus分页结果转为 VO分页结果</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> p MybatisPlus的分页结果</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> voClass 目标VO类型的字节码</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> <V> 目标VO类型</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> <P> 原始PO类型</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@return</span> VO的分页对象</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <V, P> PageDTO<V> <span class="hljs-title function_">of</span><span class="hljs-params">(Page<P> p, Class<V> voClass)</span> {<br> <span class="hljs-comment">// 1.非空校验</span><br> List<P> records = p.getRecords();<br> <span class="hljs-keyword">if</span> (records == <span class="hljs-literal">null</span> || records.size() <= <span class="hljs-number">0</span>) {<br> <span class="hljs-comment">// 无数据,返回空结果</span><br> <span class="hljs-keyword">return</span> empty(p);<br> }<br> <span class="hljs-comment">// 2.数据转换</span><br> List<V> vos = BeanUtil.copyToList(records, voClass);<br> <span class="hljs-comment">// 3.封装返回</span><br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">PageDTO</span><>(p.getTotal(), p.getPages(), vos);<br> }<br><br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 将MybatisPlus分页结果转为 VO分页结果,允许用户自定义PO到VO的转换方式</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> p MybatisPlus的分页结果</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> convertor PO到VO的转换函数</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> <V> 目标VO类型</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> <P> 原始PO类型</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@return</span> VO的分页对象</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <V, P> PageDTO<V> <span class="hljs-title function_">of</span><span class="hljs-params">(Page<P> p, Function<P, V> convertor)</span> {<br> <span class="hljs-comment">// 1.非空校验</span><br> List<P> records = p.getRecords();<br> <span class="hljs-keyword">if</span> (records == <span class="hljs-literal">null</span> || records.size() <= <span class="hljs-number">0</span>) {<br> <span class="hljs-comment">// 无数据,返回空结果</span><br> <span class="hljs-keyword">return</span> empty(p);<br> }<br> <span class="hljs-comment">// 2.数据转换</span><br> List<V> vos = records.stream().map(convertor).collect(Collectors.toList());<br> <span class="hljs-comment">// 3.封装返回</span><br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">PageDTO</span><>(p.getTotal(), p.getPages(), vos);<br> }<br>}<br><br></code></pre></td></tr></table></figure><p>最终,业务层的代码可以简化为:</p><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Override</span><br><span class="hljs-keyword">public</span> PageDTO<UserVO> <span class="hljs-title function_">queryUserByPage</span><span class="hljs-params">(PageQuery query)</span> {<br> <span class="hljs-comment">// 1.构建条件</span><br> Page<User> page = query.toMpPageDefaultSortByCreateTimeDesc();<br> <span class="hljs-comment">// 2.查询</span><br> page(page);<br> <span class="hljs-comment">// 3.封装返回</span><br> <span class="hljs-keyword">return</span> PageDTO.of(page, UserVO.class);<br>}<br></code></pre></td></tr></table></figure><p>如果是希望自定义PO到VO的转换过程,可以这样做:</p><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Override</span><br><span class="hljs-keyword">public</span> PageDTO<UserVO> <span class="hljs-title function_">queryUserByPage</span><span class="hljs-params">(PageQuery query)</span> {<br> <span class="hljs-comment">// 1.构建条件</span><br> Page<User> page = query.toMpPageDefaultSortByCreateTimeDesc();<br> <span class="hljs-comment">// 2.查询</span><br> page(page);<br> <span class="hljs-comment">// 3.封装返回</span><br> <span class="hljs-keyword">return</span> PageDTO.of(page, user -> {<br> <span class="hljs-comment">// 拷贝属性到VO</span><br> <span class="hljs-type">UserVO</span> <span class="hljs-variable">vo</span> <span class="hljs-operator">=</span> BeanUtil.copyProperties(user, UserVO.class);<br> <span class="hljs-comment">// 用户名脱敏</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">username</span> <span class="hljs-operator">=</span> vo.getUsername();<br> vo.setUsername(username.substring(<span class="hljs-number">0</span>, username.length() - <span class="hljs-number">2</span>) + <span class="hljs-string">"**"</span>);<br> <span class="hljs-keyword">return</span> vo;<br> });<br>}<br></code></pre></td></tr></table></figure><p>最终查询的结果如下:<br><img src="https://cdn.nlark.com/yuque/0/2023/png/27967491/1688291665223-e96361ac-d315-4d39-b54a-9c54083665f2.png#averageHue=%23f8f9fe&clientId=uba0f15c0-624d-4&from=paste&height=614&id=u88be7c7b&originHeight=761&originWidth=891&originalType=binary&ratio=1.2395833730697632&rotation=0&showTitle=false&size=74030&status=done&style=none&taskId=u44f43f7a-6fb4-4c98-92bf-32cd49ec211&title=&width=718.7898929246568" alt="image.png"></p>]]></content>
<categories>
<category> 后端 </category>
</categories>
<tags>
<tag> mysql </tag>
<tag> mybatis </tag>
<tag> java </tag>
</tags>
</entry>
<entry>
<title>mysql</title>
<link href="/2023/07/28/mysql/"/>
<url>/2023/07/28/mysql/</url>
<content type="html"><![CDATA[<h1 id="MySQL"><a href="#MySQL" class="headerlink" title="MySQL"></a>MySQL</h1><h2 id="执行编写顺序"><a href="#执行编写顺序" class="headerlink" title="执行编写顺序"></a>执行编写顺序</h2><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-comment">-- 语句编写和执行顺序</span><br><span class="hljs-keyword">select</span> <span class="hljs-comment">-- 4</span><br> 字段列表<br><span class="hljs-keyword">from</span> <span class="hljs-comment">-- 1</span><br> 表名列表<br><span class="hljs-keyword">where</span> <span class="hljs-comment">-- 2</span><br> 条件列表<br><span class="hljs-keyword">group</span> <span class="hljs-keyword">by</span> <span class="hljs-comment">--3</span><br> 分组字段列表<br><span class="hljs-keyword">having</span> <br> 分组后条件列表<br><span class="hljs-keyword">order</span> <span class="hljs-keyword">by</span> <span class="hljs-comment">--5</span><br> 排序字段列表<br>limit <span class="hljs-comment">--6</span><br> 分页参数<br></code></pre></td></tr></table></figure><h1 id="一、数据定义语言DDL"><a href="#一、数据定义语言DDL" class="headerlink" title="一、数据定义语言DDL"></a>一、数据定义语言DDL</h1><h2 id="1、数据库操作"><a href="#1、数据库操作" class="headerlink" title="1、数据库操作"></a>1、数据库操作</h2><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"># DDL<span class="hljs-operator">-</span>数据库操作<br><span class="hljs-keyword">show</span> databases; #显示所有数据库<br><span class="hljs-keyword">create</span> database [if <span class="hljs-keyword">not</span> <span class="hljs-keyword">exists</span>] 数据库名; #创建数据库<br>use 数据库名 ; #使用数据库<br><span class="hljs-keyword">select</span> database(); #显示当前在哪个数据库下<br><span class="hljs-keyword">drop</span> database [if <span class="hljs-keyword">exists</span>] 数据库名; #删库跑路<br></code></pre></td></tr></table></figure><h2 id="2、表操作"><a href="#2、表操作" class="headerlink" title="2、表操作"></a>2、表操作</h2><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-comment">-- DDL-表操作</span><br><span class="hljs-keyword">show</span> tables;# 显示所有表<br><span class="hljs-keyword">create table</span> 表名(字段 字段类型,字段 字段类型……) # 创建表<br><span class="hljs-keyword">desc</span> 表名 #查看表中所有字段<br><span class="hljs-keyword">alter table</span> 表名 <span class="hljs-keyword">add</span>(添加字段)<span class="hljs-operator">/</span>modify(修改字段类型)<span class="hljs-operator">/</span>change(修改字段名及类型)<span class="hljs-operator">/</span><span class="hljs-keyword">drop</span>(删除字段)<span class="hljs-operator">/</span>rename <span class="hljs-keyword">to</span>(修改表名);<br><span class="hljs-keyword">drop</span> <span class="hljs-keyword">table</span> 表名;# 删除表<br></code></pre></td></tr></table></figure><h1 id="二、-DML(增删改)"><a href="#二、-DML(增删改)" class="headerlink" title="二、 DML(增删改)"></a>二、 DML(增删改)</h1><h2 id="insert-update-delete"><a href="#insert-update-delete" class="headerlink" title="insert update delete"></a>insert update delete</h2><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"># 插入一条语句<br><span class="hljs-keyword">insert into</span> <span class="hljs-keyword">user</span>(id, name, age, gender)<br><span class="hljs-keyword">values</span> (<span class="hljs-number">123</span>, <span class="hljs-string">'cxk'</span>, <span class="hljs-number">25</span>, <span class="hljs-string">'男'</span>);<br><br><span class="hljs-keyword">insert into</span> <span class="hljs-keyword">user</span>(id, name, age, gender)<br><span class="hljs-keyword">values</span> (<span class="hljs-number">456</span>, <span class="hljs-string">'ikun'</span>, <span class="hljs-number">21</span>, <span class="hljs-string">'男'</span>);<br><br># 插入多条语句<br><span class="hljs-keyword">insert into</span> <span class="hljs-keyword">user</span><br><span class="hljs-keyword">values</span> (<span class="hljs-number">456</span>, <span class="hljs-string">'ikun'</span>, <span class="hljs-number">21</span>, <span class="hljs-string">'男'</span>),<br>(<span class="hljs-number">456</span>, <span class="hljs-string">'ikun'</span>, <span class="hljs-number">21</span>, <span class="hljs-string">'男'</span>),<br>(<span class="hljs-number">456</span>, <span class="hljs-string">'ikun'</span>, <span class="hljs-number">21</span>, <span class="hljs-string">'男'</span>),<br>(<span class="hljs-number">456</span>, <span class="hljs-string">'ikun'</span>, <span class="hljs-number">21</span>, <span class="hljs-string">'男'</span>);<br><br># 查看表<br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span><span class="hljs-keyword">from</span> <span class="hljs-keyword">user</span>;<br><br># 修改 把name为ikun的name全部改为小黑子<br><span class="hljs-keyword">update</span> <span class="hljs-keyword">user</span><br><span class="hljs-keyword">set</span> name <span class="hljs-operator">=</span> <span class="hljs-string">'小黑子'</span><br><span class="hljs-keyword">where</span> name <span class="hljs-operator">=</span> <span class="hljs-string">'ikun'</span>;<br><br># 把name为cxk的 name改为鸡哥,性别改为女<br><span class="hljs-keyword">update</span> <span class="hljs-keyword">user</span><br><span class="hljs-keyword">set</span> name <span class="hljs-operator">=</span> <span class="hljs-string">'鸡哥'</span>,<br>gender<span class="hljs-operator">=</span><span class="hljs-string">'女'</span><br><span class="hljs-keyword">where</span> name <span class="hljs-operator">=</span> <span class="hljs-string">'cxk'</span>;<br><br># 把所有人的年龄改为<span class="hljs-number">69</span><br><span class="hljs-keyword">update</span> <span class="hljs-keyword">user</span><br><span class="hljs-keyword">set</span> age<span class="hljs-operator">=</span><span class="hljs-number">69</span>;<br><br># 删除 name为小黑子的人<br><span class="hljs-keyword">delete</span> <span class="hljs-keyword">from</span> <span class="hljs-keyword">user</span> <span class="hljs-keyword">where</span> name <span class="hljs-operator">=</span> <span class="hljs-string">'小黑子'</span>;<br><br># 删除所有人<br><span class="hljs-keyword">delete</span> <span class="hljs-keyword">from</span> <span class="hljs-keyword">user</span>;<br></code></pre></td></tr></table></figure><h1 id="三、DQL(查)"><a href="#三、DQL(查)" class="headerlink" title="三、DQL(查)"></a>三、DQL(查)</h1><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-comment">-- 创建表并插入数据--------------------------------------------</span><br><br><span class="hljs-keyword">drop</span> <span class="hljs-keyword">table</span> if <span class="hljs-keyword">exists</span> employee;<br><span class="hljs-keyword">create table</span> emp<br>(<br>id <span class="hljs-type">int</span> comment <span class="hljs-string">'编号'</span>,<br>workno <span class="hljs-type">varchar</span>(<span class="hljs-number">10</span>) comment <span class="hljs-string">'工号'</span>,<br>name <span class="hljs-type">varchar</span>(<span class="hljs-number">10</span>) comment <span class="hljs-string">'姓名'</span>,<br>gender <span class="hljs-type">char</span>(<span class="hljs-number">1</span>) comment <span class="hljs-string">'性别'</span>,<br>age tinyint unsigned comment <span class="hljs-string">'年龄'</span>,<br>idcard <span class="hljs-type">char</span>(<span class="hljs-number">18</span>) comment <span class="hljs-string">'身份证号'</span>,<br>workaddress <span class="hljs-type">varchar</span>(<span class="hljs-number">50</span>) comment <span class="hljs-string">'工作地址'</span>,<br>entrydate <span class="hljs-type">date</span> comment <span class="hljs-string">'入职时间'</span><br>) comment <span class="hljs-string">'员工表'</span>;<br><br><span class="hljs-comment">-- 插入数据--------------------------------------------------------</span><br><span class="hljs-keyword">INSERT INTO</span> emp (id, workno, name, gender, age, idcard, workaddress, entrydate)<br><span class="hljs-keyword">VALUES</span> (<span class="hljs-number">1</span>, <span class="hljs-string">'00001'</span>, <span class="hljs-string">'柳岩'</span>, <span class="hljs-string">'女'</span>, <span class="hljs-number">20</span>, <span class="hljs-string">'123456789012345678'</span>, <span class="hljs-string">'北京'</span>, <span class="hljs-string">'2000-01-01'</span>);<br><span class="hljs-keyword">INSERT INTO</span> emp (id, workno, name, gender, age, idcard, workaddress, entrydate)<br><span class="hljs-keyword">VALUES</span> (<span class="hljs-number">2</span>, <span class="hljs-string">'00002'</span>, <span class="hljs-string">'张无忌'</span>, <span class="hljs-string">'男'</span>, <span class="hljs-number">18</span>, <span class="hljs-string">'123456789012345670'</span>, <span class="hljs-string">'北京'</span>, <span class="hljs-string">'2005-09-01'</span>);<br><span class="hljs-keyword">INSERT INTO</span> emp (id, workno, name, gender, age, idcard, workaddress, entrydate)<br><span class="hljs-keyword">VALUES</span> (<span class="hljs-number">3</span>, <span class="hljs-string">'00003'</span>, <span class="hljs-string">'韦一笑'</span>, <span class="hljs-string">'男'</span>, <span class="hljs-number">38</span>, <span class="hljs-string">'123456789712345670'</span>, <span class="hljs-string">'上海'</span>, <span class="hljs-string">'2005-08-01'</span>);<br><span class="hljs-keyword">INSERT INTO</span> emp (id, workno, name, gender, age, idcard, workaddress, entrydate)<br><span class="hljs-keyword">VALUES</span> (<span class="hljs-number">4</span>, <span class="hljs-string">'00004'</span>, <span class="hljs-string">'赵敏'</span>, <span class="hljs-string">'女'</span>, <span class="hljs-number">18</span>, <span class="hljs-string">'123456757123845670'</span>, <span class="hljs-string">'北京'</span>, <span class="hljs-string">'2009-12-01'</span>);<br><span class="hljs-keyword">INSERT INTO</span> emp (id, workno, name, gender, age, idcard, workaddress, entrydate)<br><span class="hljs-keyword">VALUES</span> (<span class="hljs-number">5</span>, <span class="hljs-string">'00005'</span>, <span class="hljs-string">'小昭'</span>, <span class="hljs-string">'女'</span>, <span class="hljs-number">16</span>, <span class="hljs-string">'123456769012345678'</span>, <span class="hljs-string">'上海'</span>, <span class="hljs-string">'2007-07-01'</span>);<br><span class="hljs-keyword">INSERT INTO</span> emp (id, workno, name, gender, age, idcard, workaddress, entrydate)<br><span class="hljs-keyword">VALUES</span> (<span class="hljs-number">6</span>, <span class="hljs-string">'00006'</span>, <span class="hljs-string">'杨逍'</span>, <span class="hljs-string">'男'</span>, <span class="hljs-number">28</span>, <span class="hljs-string">'12345678931234567X'</span>, <span class="hljs-string">'北京'</span>, <span class="hljs-string">'2006-01-01'</span>);<br><span class="hljs-keyword">INSERT INTO</span> emp (id, workno, name, gender, age, idcard, workaddress, entrydate)<br><span class="hljs-keyword">VALUES</span> (<span class="hljs-number">7</span>, <span class="hljs-string">'00007'</span>, <span class="hljs-string">'范瑶'</span>, <span class="hljs-string">'男'</span>, <span class="hljs-number">40</span>, <span class="hljs-string">'123456789212345670'</span>, <span class="hljs-string">'北京'</span>, <span class="hljs-string">'2005-05-01'</span>);<br><span class="hljs-keyword">INSERT INTO</span> emp (id, workno, name, gender, age, idcard, workaddress, entrydate)<br><span class="hljs-keyword">VALUES</span> (<span class="hljs-number">8</span>, <span class="hljs-string">'00008'</span>, <span class="hljs-string">'黛绮丝'</span>, <span class="hljs-string">'女'</span>, <span class="hljs-number">38</span>, <span class="hljs-string">'123456157123645670'</span>, <span class="hljs-string">'天津'</span>, <span class="hljs-string">'2015-05-01'</span>);<br><span class="hljs-keyword">INSERT INTO</span> emp (id, workno, name, gender, age, idcard, workaddress, entrydate)<br><span class="hljs-keyword">VALUES</span> (<span class="hljs-number">9</span>, <span class="hljs-string">'00009'</span>, <span class="hljs-string">'范凉凉'</span>, <span class="hljs-string">'女'</span>, <span class="hljs-number">45</span>, <span class="hljs-string">'123156789012345678'</span>, <span class="hljs-string">'北京'</span>, <span class="hljs-string">'2010-04-01'</span>);<br><span class="hljs-keyword">INSERT INTO</span> emp (id, workno, name, gender, age, idcard, workaddress, entrydate)<br><span class="hljs-keyword">VALUES</span> (<span class="hljs-number">10</span>, <span class="hljs-string">'00010'</span>, <span class="hljs-string">'陈友谅'</span>, <span class="hljs-string">'男'</span>, <span class="hljs-number">53</span>, <span class="hljs-string">'123456789012345670'</span>, <span class="hljs-string">'上海'</span>, <span class="hljs-string">'2011-01-01'</span>);<br><span class="hljs-keyword">INSERT INTO</span> emp (id, workno, name, gender, age, idcard, workaddress, entrydate)<br><span class="hljs-keyword">VALUES</span> (<span class="hljs-number">11</span>, <span class="hljs-string">'00011'</span>, <span class="hljs-string">'张士诚'</span>, <span class="hljs-string">'男'</span>, <span class="hljs-number">55</span>, <span class="hljs-string">'123567897123465670'</span>, <span class="hljs-string">'江苏'</span>, <span class="hljs-string">'2015-05-01'</span>);<br><span class="hljs-keyword">INSERT INTO</span> emp (id, workno, name, gender, age, idcard, workaddress, entrydate)<br><span class="hljs-keyword">VALUES</span> (<span class="hljs-number">12</span>, <span class="hljs-string">'00012'</span>, <span class="hljs-string">'常遇春'</span>, <span class="hljs-string">'男'</span>, <span class="hljs-number">32</span>, <span class="hljs-string">'123446757152345670'</span>, <span class="hljs-string">'北京'</span>, <span class="hljs-string">'2004-02-01'</span>);<br><span class="hljs-keyword">INSERT INTO</span> emp (id, workno, name, gender, age, idcard, workaddress, entrydate)<br><span class="hljs-keyword">VALUES</span> (<span class="hljs-number">13</span>, <span class="hljs-string">'00013'</span>, <span class="hljs-string">'张三丰'</span>, <span class="hljs-string">'男'</span>, <span class="hljs-number">88</span>, <span class="hljs-string">'123656789012345678'</span>, <span class="hljs-string">'江苏'</span>, <span class="hljs-string">'2020-11-01'</span>);<br><span class="hljs-keyword">INSERT INTO</span> emp (id, workno, name, gender, age, idcard, workaddress, entrydate)<br><span class="hljs-keyword">VALUES</span> (<span class="hljs-number">14</span>, <span class="hljs-string">'00014'</span>, <span class="hljs-string">'灭绝'</span>, <span class="hljs-string">'女'</span>, <span class="hljs-number">65</span>, <span class="hljs-string">'123456719012345670'</span>, <span class="hljs-string">'西安'</span>, <span class="hljs-string">'2019-05-01'</span>);<br><span class="hljs-keyword">INSERT INTO</span> emp (id, workno, name, gender, age, idcard, workaddress, entrydate)<br><span class="hljs-keyword">VALUES</span> (<span class="hljs-number">15</span>, <span class="hljs-string">'00015'</span>, <span class="hljs-string">'胡青牛'</span>, <span class="hljs-string">'男'</span>, <span class="hljs-number">70</span>, <span class="hljs-string">'12345674971234567X'</span>, <span class="hljs-string">'西安'</span>, <span class="hljs-string">'2018-04-01'</span>);<br><span class="hljs-keyword">INSERT INTO</span> emp (id, workno, name, gender, age, idcard, workaddress, entrydate)<br><span class="hljs-keyword">VALUES</span> (<span class="hljs-number">16</span>, <span class="hljs-string">'00016'</span>, <span class="hljs-string">'周芷若'</span>, <span class="hljs-string">'女'</span>, <span class="hljs-number">18</span>, <span class="hljs-keyword">null</span>, <span class="hljs-string">'北京'</span>, <span class="hljs-string">'2012-06-01'</span>);<br></code></pre></td></tr></table></figure><h2 id="1、select-from"><a href="#1、select-from" class="headerlink" title="1、select from"></a>1、select from</h2><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-comment">-- 基本查询-------------------------------------------------------</span><br><br><span class="hljs-comment">-- 1、查询指定字段 name,workno,age返回</span><br><span class="hljs-keyword">select</span> name, workno, age<br><span class="hljs-keyword">from</span> emp;<br><br><span class="hljs-comment">-- 2、查询返回所有字段</span><br><span class="hljs-keyword">select</span> id,<br> workno,<br> name,<br> gender,<br> age,<br> idcard,<br> workaddress,<br> entrydate<br><span class="hljs-keyword">from</span> emp;<br><br><span class="hljs-comment">-- 也可以查询所有字段,但实际使用中不建议,因为不明确</span><br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span><br><span class="hljs-keyword">from</span> emp;<br><br><span class="hljs-comment">-- 3、查询所有员工的工作地址并 起一个别名(as可以省略)</span><br><span class="hljs-keyword">select</span> workaddress <span class="hljs-keyword">as</span> <span class="hljs-string">'工作地址'</span> <span class="hljs-keyword">from</span> emp;<br><br><span class="hljs-comment">-- 4、查询所有员工的工作地址并去除重复地址</span><br><span class="hljs-keyword">select</span> <span class="hljs-keyword">distinct</span> workaddress <span class="hljs-string">'上班地址'</span> <span class="hljs-keyword">from</span> emp;<br></code></pre></td></tr></table></figure><h2 id="2、where"><a href="#2、where" class="headerlink" title="2、where"></a>2、where</h2><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-comment">-- 条件查询 where-------------------------------------------------</span><br><br><span class="hljs-comment">-- 1、查询年龄为88的员工</span><br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span> <span class="hljs-keyword">from</span> emp <span class="hljs-keyword">where</span> age<span class="hljs-operator">=</span><span class="hljs-number">88</span>;<br><br><span class="hljs-comment">-- 2、查询年龄小于20的员工</span><br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span><span class="hljs-keyword">from</span> emp <span class="hljs-keyword">where</span> age<span class="hljs-operator"><</span><span class="hljs-number">20</span>;<br><br><span class="hljs-comment">-- 3、查询没有身份证号码的员工</span><br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span> <span class="hljs-keyword">from</span> emp <span class="hljs-keyword">where</span> idcard <span class="hljs-keyword">is</span> <span class="hljs-keyword">null</span>;<br><br><span class="hljs-comment">-- 4、查询有身份证号码的员工</span><br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span> <span class="hljs-keyword">from</span> emp <span class="hljs-keyword">where</span> idcard <span class="hljs-keyword">is</span> <span class="hljs-keyword">not null</span>;<br><br><span class="hljs-comment">-- 5、查询年龄不等于88的员工</span><br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span> <span class="hljs-keyword">from</span> emp <span class="hljs-keyword">where</span> age <span class="hljs-operator">!=</span> <span class="hljs-number">88</span>;<br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span> <span class="hljs-keyword">from</span> emp <span class="hljs-keyword">where</span> age <span class="hljs-operator"><></span> <span class="hljs-number">88</span>;<br><br><span class="hljs-comment">-- 6、查询年龄在15到20(两端都包含)之间的员工</span><br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span> <span class="hljs-keyword">from</span> emp <span class="hljs-keyword">where</span> age <span class="hljs-operator">>=</span> <span class="hljs-number">15</span> <span class="hljs-operator">&&</span> age <span class="hljs-operator"><=</span> <span class="hljs-number">20</span>;<br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span> <span class="hljs-keyword">from</span> emp <span class="hljs-keyword">where</span> age <span class="hljs-operator">>=</span> <span class="hljs-number">15</span> <span class="hljs-keyword">and</span> age <span class="hljs-operator"><=</span> <span class="hljs-number">20</span>;<br><span class="hljs-comment">-- between and 小的在前面 大的在后面 否则什么都查不到</span><br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span> <span class="hljs-keyword">from</span> emp <span class="hljs-keyword">where</span> age <span class="hljs-keyword">between</span> <span class="hljs-number">15</span> <span class="hljs-keyword">and</span> <span class="hljs-number">20</span>;<br><br><span class="hljs-comment">-- 7、查询性别为女且年龄小于25的员工</span><br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span> <span class="hljs-keyword">from</span> emp <span class="hljs-keyword">where</span> gender <span class="hljs-operator">=</span> <span class="hljs-string">'女'</span> <span class="hljs-keyword">and</span> age <span class="hljs-operator"><</span> <span class="hljs-number">25</span>;<br><br><span class="hljs-comment">-- 8、查询年龄或等于18或20或40的员工</span><br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span> <span class="hljs-keyword">from</span> emp <span class="hljs-keyword">where</span> age <span class="hljs-operator">=</span> <span class="hljs-number">18</span> <span class="hljs-operator">||</span>age <span class="hljs-operator">=</span> <span class="hljs-number">20</span> <span class="hljs-operator">||</span> age <span class="hljs-operator">=</span> <span class="hljs-number">40</span>;<br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span> <span class="hljs-keyword">from</span> emp <span class="hljs-keyword">where</span> age <span class="hljs-operator">=</span> <span class="hljs-number">18</span> <span class="hljs-keyword">or</span> age <span class="hljs-operator">=</span> <span class="hljs-number">20</span> <span class="hljs-keyword">or</span> age <span class="hljs-operator">=</span> <span class="hljs-number">40</span>;<br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span> <span class="hljs-keyword">from</span> emp <span class="hljs-keyword">where</span> age <span class="hljs-keyword">in</span> (<span class="hljs-number">18</span>,<span class="hljs-number">20</span>,<span class="hljs-number">40</span>);<br><br><span class="hljs-comment">-- 9、查询名字为两个字的员工的信息 模糊匹配:like _对应单个字符 %对应多个字符</span><br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span> <span class="hljs-keyword">from</span> emp <span class="hljs-keyword">where</span> name <span class="hljs-keyword">like</span> <span class="hljs-string">'__'</span>;<br><br><span class="hljs-comment">-- 10、查询身份证号最后一位是X的员工</span><br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span> <span class="hljs-keyword">from</span> emp <span class="hljs-keyword">where</span> idcard <span class="hljs-keyword">like</span> <span class="hljs-string">'%X'</span>;<br># <span class="hljs-keyword">select</span> <span class="hljs-operator">*</span> <span class="hljs-keyword">from</span> emp <span class="hljs-keyword">where</span> idcard <span class="hljs-keyword">like</span> <span class="hljs-string">'_________________X'</span>; 这样不太好<br><br><span class="hljs-comment">-- 11、查询身份证号倒数第九位是0的员工</span><br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span> <span class="hljs-keyword">from</span> emp <span class="hljs-keyword">where</span> idcard <span class="hljs-keyword">like</span> <span class="hljs-string">'%0________'</span>;<br></code></pre></td></tr></table></figure><h2 id="3、count、max、group"><a href="#3、count、max、group" class="headerlink" title="3、count、max、group"></a>3、count、max、group</h2><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-comment">-- 聚合函数 max min avg:最大最小平均值 sum:整列数据加起来 count:统计整列数据的个数 作用于列 所有聚合函数不参与null的计算--</span><br><br><br><span class="hljs-comment">-- 1、统计企业员工数量</span><br><span class="hljs-keyword">select</span> <span class="hljs-built_in">count</span>(<span class="hljs-operator">*</span>) <span class="hljs-keyword">from</span> emp; <span class="hljs-comment">-- 16</span><br><br><span class="hljs-keyword">select</span> <span class="hljs-built_in">count</span>(idcard) <span class="hljs-keyword">from</span> emp; <span class="hljs-comment">-- 15因为有一个员工的身份证号码为null</span><br><br><span class="hljs-comment">-- 2、统计所有员工的平均年龄</span><br><span class="hljs-keyword">select</span> <span class="hljs-built_in">avg</span>(age) <span class="hljs-keyword">from</span> emp;<br><br><span class="hljs-comment">-- 3、查询最大的年龄</span><br><span class="hljs-keyword">select</span> <span class="hljs-built_in">max</span>(age) <span class="hljs-keyword">from</span> emp;<br><br><span class="hljs-comment">-- 4、查询最小的年龄</span><br><span class="hljs-keyword">select</span> <span class="hljs-built_in">min</span>(age) <span class="hljs-keyword">from</span> emp;<br><br><span class="hljs-comment">-- 5、统计西安地区员工年龄之和</span><br><span class="hljs-keyword">select</span> <span class="hljs-built_in">sum</span>(age) <span class="hljs-keyword">from</span> emp <span class="hljs-keyword">where</span> workaddress <span class="hljs-operator">=</span> <span class="hljs-string">'西安'</span>;<br><br><span class="hljs-comment">-- 分组查询 group by [having 条件]</span><br><br><span class="hljs-comment">-- 1、根据性别分组,分别统计男女人数</span><br><span class="hljs-comment">-- 根据什么字段分组就查询什么字段,查询其他字段无意义</span><br><span class="hljs-comment">-- count(*)此处表示每组的数量</span><br><span class="hljs-keyword">select</span> gender,<span class="hljs-built_in">count</span>(<span class="hljs-operator">*</span>) <span class="hljs-keyword">from</span> emp <span class="hljs-keyword">group</span> <span class="hljs-keyword">by</span> gender;<br><br><span class="hljs-comment">-- 2、根据性别分组,分别统计男女平均年龄</span><br><span class="hljs-keyword">select</span> gender,<span class="hljs-built_in">avg</span>(age) <span class="hljs-keyword">from</span> emp <span class="hljs-keyword">group</span> <span class="hljs-keyword">by</span> gender;<br><br><span class="hljs-comment">-- 3、查询年龄小于45的员工,并根据工作地址分组,获取员工数量大于等于3的工作地址</span><br><span class="hljs-comment">-- having后面也接条件,与where不同的是,having对分组后的数据进行操作,having可以对聚合函数进行判断,where不行</span><br><span class="hljs-comment">-- 执行顺序:where>聚合函数>having 分组后查询字段一般为聚合函数或分组字段,因为查询其他字段无意义</span><br><span class="hljs-keyword">select</span> <span class="hljs-built_in">count</span>(<span class="hljs-operator">*</span>) <span class="hljs-keyword">as</span> address_count,workaddress <span class="hljs-keyword">from</span> emp <br><span class="hljs-keyword">where</span> age <span class="hljs-operator"><</span> <span class="hljs-number">45</span> <br><span class="hljs-keyword">group</span> <span class="hljs-keyword">by</span> workaddress <span class="hljs-keyword">having</span> address_count <span class="hljs-operator">>=</span> <span class="hljs-number">3</span>;<br></code></pre></td></tr></table></figure><h2 id="4、order-by"><a href="#4、order-by" class="headerlink" title="4、order by"></a>4、order by</h2><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-comment">-- 排序查询 order by----------------------------------------------</span><br><br><span class="hljs-comment">-- 1、根据年龄对员工进行排序</span><br><span class="hljs-comment">-- 升序排序</span><br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span> <span class="hljs-keyword">from</span> emp <span class="hljs-keyword">order</span> <span class="hljs-keyword">by</span> age; <span class="hljs-comment">-- 默认升序排序</span><br># <span class="hljs-keyword">select</span> <span class="hljs-operator">*</span> <span class="hljs-keyword">from</span> emp <span class="hljs-keyword">order</span> <span class="hljs-keyword">by</span> age <span class="hljs-keyword">asc</span>;<br><br><span class="hljs-comment">-- 降序排序</span><br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span> <span class="hljs-keyword">from</span> emp <span class="hljs-keyword">order</span> <span class="hljs-keyword">by</span> age <span class="hljs-keyword">desc</span>;<br><br><span class="hljs-comment">-- 2、根据入职时间,对员工进行降序排序</span><br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span> <span class="hljs-keyword">from</span> emp <span class="hljs-keyword">order</span> <span class="hljs-keyword">by</span> entrydate <span class="hljs-keyword">desc</span>;<br><br><span class="hljs-comment">-- 3、根据年龄升序排序,若年龄相同,根据入职时间降序排序</span><br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span> <span class="hljs-keyword">from</span> emp <span class="hljs-keyword">order</span> <span class="hljs-keyword">by</span> age, entrydate <span class="hljs-keyword">desc</span>;<br><br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span> <span class="hljs-keyword">from</span> emp <span class="hljs-keyword">order</span> <span class="hljs-keyword">by</span> name <span class="hljs-keyword">desc</span>;<br><br><span class="hljs-comment">-- 分页查询(把所有数据一页一页展示出来) limit 起始索引,查询的个数--------->起始索引=(页码数-1)*每页展示数--------------------------------------------------------------------</span><br><span class="hljs-comment">-- 分页查询limit是MySQL的方言,不同的数据库语言有不同的实现</span><br><br><span class="hljs-comment">-- 1、查询第1页员工数据,查询个数为10</span><br># <span class="hljs-keyword">select</span> <span class="hljs-operator">*</span> <span class="hljs-keyword">from</span> emp limit <span class="hljs-number">0</span>,<span class="hljs-number">10</span>;<br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span><br><span class="hljs-keyword">from</span> emp<br>limit <span class="hljs-number">10</span>;<br><span class="hljs-comment">-- 第一页的其实索引可以省略</span><br><br><span class="hljs-comment">-- 2、查询第2页员工数据,查询个数为10,不足10条则全部显示</span><br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span><br><span class="hljs-keyword">from</span> emp<br>limit <span class="hljs-number">10</span>,<span class="hljs-number">10</span>;<br></code></pre></td></tr></table></figure><h2 id="5、案例"><a href="#5、案例" class="headerlink" title="5、案例"></a>5、案例</h2><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-comment">-- DQL案例--------------------------------------------------------</span><br><br><span class="hljs-comment">-- 1.查询年龄为20,21,22,23岁的员工信息。</span><br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span><br><span class="hljs-keyword">from</span> emp<br><span class="hljs-keyword">where</span> age <span class="hljs-operator">=</span> <span class="hljs-number">20</span><br><span class="hljs-keyword">or</span> age <span class="hljs-operator">=</span> <span class="hljs-number">21</span><br><span class="hljs-keyword">or</span> age <span class="hljs-operator">=</span> <span class="hljs-number">22</span><br><span class="hljs-keyword">or</span> age <span class="hljs-operator">=</span> <span class="hljs-number">23</span>;<br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span><br><span class="hljs-keyword">from</span> emp<br><span class="hljs-keyword">where</span> age <span class="hljs-keyword">in</span> (<span class="hljs-number">20</span>, <span class="hljs-number">21</span>, <span class="hljs-number">22</span>, <span class="hljs-number">23</span>);<br><br><span class="hljs-comment">-- 2.查询性别为男,并且年龄在20-40岁(含)以内的姓名为三个字的员工。</span><br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span><br><span class="hljs-keyword">from</span> emp<br><span class="hljs-keyword">where</span> gender <span class="hljs-operator">=</span> <span class="hljs-string">'男'</span><br><span class="hljs-keyword">and</span> age <span class="hljs-keyword">between</span> <span class="hljs-number">20</span> <span class="hljs-keyword">and</span> <span class="hljs-number">40</span><br><span class="hljs-keyword">and</span> name <span class="hljs-keyword">like</span> <span class="hljs-string">'___'</span>;<br><br><span class="hljs-comment">-- 3.统计员工表中,年龄小于60岁的,男性员工和女性员工的人数。</span><br><span class="hljs-keyword">select</span> gender, <span class="hljs-built_in">count</span>(<span class="hljs-operator">*</span>)<br><span class="hljs-keyword">from</span> emp<br><span class="hljs-keyword">where</span> age <span class="hljs-operator"><</span> <span class="hljs-number">60</span><br><span class="hljs-keyword">group</span> <span class="hljs-keyword">by</span> gender;<br><br><span class="hljs-comment">-- 4.查询所有年龄小于等于35岁员工的姓名和年龄,并对查询结果按年龄升序排序,如果年龄相同按入职时间降序排序。</span><br><span class="hljs-keyword">select</span> name, age, entrydate<br><span class="hljs-keyword">from</span> emp<br><span class="hljs-keyword">where</span> age <span class="hljs-operator"><=</span> <span class="hljs-number">35</span><br><span class="hljs-keyword">order</span> <span class="hljs-keyword">by</span> age, entrydate <span class="hljs-keyword">desc</span>;<br><br><span class="hljs-comment">-- 5.查询性别为男,且年龄在20-40岁(含)以内的员工信息,对查询的结果按年龄升序排序,年龄相同按入职时间升序排序,取前5个员工信息</span><br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span><br><span class="hljs-keyword">from</span> emp<br><span class="hljs-keyword">where</span> gender <span class="hljs-operator">=</span> <span class="hljs-string">'男'</span><br><span class="hljs-keyword">and</span> age <span class="hljs-keyword">between</span> <span class="hljs-number">20</span> <span class="hljs-keyword">and</span> <span class="hljs-number">40</span>;<br></code></pre></td></tr></table></figure><h1 id="四、-DCL"><a href="#四、-DCL" class="headerlink" title="四、 DCL"></a>四、 DCL</h1><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-comment">-- DCL---------------------------------------------------------</span><br><br><span class="hljs-comment">-- 创建用户</span><br><span class="hljs-comment">-- 创建用户‘tanking’,只能在当前主机localhost访问,密码是123456</span><br><span class="hljs-keyword">create</span> <span class="hljs-keyword">user</span> <span class="hljs-string">'tanking'</span>@<span class="hljs-string">'localhost'</span> identified <span class="hljs-keyword">by</span> <span class="hljs-string">'123456'</span>;<br><br><span class="hljs-comment">-- 创建用户‘cxk’,能在任意主机访问数据库,密码 ctrljntm,%为通配符</span><br><span class="hljs-keyword">create</span> <span class="hljs-keyword">user</span> <span class="hljs-string">'cxk'</span>@<span class="hljs-string">'%'</span> identified <span class="hljs-keyword">by</span> <span class="hljs-string">'ctrljntm'</span>;<br><br><span class="hljs-comment">-- 修改cxk密码为‘123’</span><br><span class="hljs-keyword">alter</span> <span class="hljs-keyword">user</span> <span class="hljs-string">'cxk'</span>@<span class="hljs-string">'%'</span> identified <span class="hljs-keyword">with</span> mysql_native_password <span class="hljs-keyword">by</span> <span class="hljs-string">'123'</span>;<br><br><span class="hljs-comment">-- 删除用户‘tanking’</span><br><span class="hljs-keyword">drop</span> <span class="hljs-keyword">user</span> <span class="hljs-string">'tanking'</span>@<span class="hljs-string">'localhost'</span>;<br><br><span class="hljs-comment">-- 查询权限---------------------------------------------------</span><br><span class="hljs-keyword">show</span> grants <span class="hljs-keyword">for</span> cxk;<br><br><span class="hljs-comment">-- 给 用户cxk 授予 对clb数据库中所有表操作 的 所有权限</span><br><span class="hljs-keyword">grant</span> <span class="hljs-keyword">all</span> <span class="hljs-keyword">on</span> clb.<span class="hljs-operator">*</span> <span class="hljs-keyword">to</span> <span class="hljs-string">'cxk'</span>@<span class="hljs-string">'%'</span>;<br><br><span class="hljs-comment">-- 撤销 cxk对clb数据库中所有表操作的所有权限</span><br><span class="hljs-keyword">revoke</span> <span class="hljs-keyword">all</span> <span class="hljs-keyword">on</span> clb.<span class="hljs-operator">*</span> <span class="hljs-keyword">from</span> <span class="hljs-string">'cxk'</span>@<span class="hljs-string">'%'</span>;<br></code></pre></td></tr></table></figure><h1 id="五、-函数"><a href="#五、-函数" class="headerlink" title="五、 函数"></a>五、 函数</h1><h2 id="1、字符串函数"><a href="#1、字符串函数" class="headerlink" title="1、字符串函数"></a>1、字符串函数</h2><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-comment">-- 字符串函数----------------------------------------------</span><br><span class="hljs-comment">-- concat 拼接字符串</span><br><span class="hljs-keyword">select</span> concat(<span class="hljs-string">'hello'</span>,<span class="hljs-string">'mysql'</span>);<br><br><span class="hljs-comment">-- lower 字符串全部小写</span><br><span class="hljs-keyword">select</span> <span class="hljs-built_in">lower</span>(<span class="hljs-string">'HelloWorld'</span>);<br><br><span class="hljs-comment">-- upper 字符串全部大写</span><br><span class="hljs-keyword">select</span> <span class="hljs-built_in">upper</span>(<span class="hljs-string">'helloworld'</span>);<br><br><span class="hljs-comment">-- lpad 左填充 中间的参数表示 字符串的长度,最后参数表示 以什么字符填充</span><br><span class="hljs-keyword">select</span> lpad(<span class="hljs-string">'cxk'</span>, <span class="hljs-number">6</span>, <span class="hljs-string">'-'</span>);<br><br><span class="hljs-comment">-- rpad 右填充 参数同上</span><br><span class="hljs-keyword">select</span> rpad(<span class="hljs-string">'cxk'</span>, <span class="hljs-number">6</span>, <span class="hljs-string">'<'</span>);<br><br><span class="hljs-comment">-- trim 去除字符串头尾的空格</span><br><span class="hljs-keyword">select</span> <span class="hljs-built_in">trim</span>(<span class="hljs-string">' cxk ctrl '</span>);<br><br><span class="hljs-comment">-- substring(str,1,5) 截取字符串 此处表示截取 从索引1开始截取 字符串str 长度为5 的 子串(注意这个索引是从1开始的)</span><br><span class="hljs-keyword">select</span> <span class="hljs-built_in">substring</span>(<span class="hljs-string">'hello_mysql'</span>, <span class="hljs-number">1</span>, <span class="hljs-number">5</span>);<br></code></pre></td></tr></table></figure><h2 id="2、数值函数"><a href="#2、数值函数" class="headerlink" title="2、数值函数"></a>2、数值函数</h2><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-comment">-- 数值函数----------------------------------------------</span><br><br><span class="hljs-comment">-- ceil 向上取整,只要小数位不是0,就加1</span><br><span class="hljs-keyword">select</span> <span class="hljs-built_in">ceil</span>(<span class="hljs-number">1.1</span>);<br><span class="hljs-keyword">select</span> <span class="hljs-built_in">ceil</span>(<span class="hljs-number">1.0</span>);<br><br><span class="hljs-comment">-- floor 向下取整</span><br><span class="hljs-keyword">select</span> <span class="hljs-built_in">floor</span>(<span class="hljs-number">1.9</span>);<br><br><span class="hljs-comment">-- mod 模 (取余数)</span><br><span class="hljs-keyword">select</span> <span class="hljs-built_in">mod</span>(<span class="hljs-number">6</span>, <span class="hljs-number">4</span>);<br><br><span class="hljs-comment">-- rand 生成 0~1之间 的随机数</span><br><span class="hljs-keyword">select</span> rand();<br><br><span class="hljs-comment">-- round 四舍五入 保留位数</span><br><span class="hljs-keyword">select</span> round(<span class="hljs-number">2.305</span>, <span class="hljs-number">2</span>);<br><span class="hljs-comment">-- 对2.305四舍五入,保留2位小数</span><br><br><span class="hljs-comment">-- 案例:生成一个6位数的随机验证码</span><br><span class="hljs-keyword">select</span> lpad(round(rand(), <span class="hljs-number">6</span>) <span class="hljs-operator">*</span> <span class="hljs-number">1000000</span>, <span class="hljs-number">6</span>, <span class="hljs-string">'0'</span>);<br></code></pre></td></tr></table></figure><h2 id="3、日期函数"><a href="#3、日期函数" class="headerlink" title="3、日期函数"></a>3、日期函数</h2><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-comment">-- 日期函数</span><br><br><span class="hljs-comment">-- curdate 返回当前日期</span><br><span class="hljs-keyword">select</span> curdate();<br><br><span class="hljs-comment">-- curtime 返回当前时间</span><br><span class="hljs-keyword">select</span> curtime();<br><br><span class="hljs-comment">-- now 返回当前时间和日期</span><br><span class="hljs-keyword">select</span> now();<br><br><span class="hljs-comment">-- year month day 分别获取 年月日</span><br><span class="hljs-keyword">select</span> <span class="hljs-keyword">year</span>(now());<br><span class="hljs-keyword">select</span> <span class="hljs-keyword">month</span>(now());<br><span class="hljs-keyword">select</span> <span class="hljs-keyword">day</span>(now());<br><br><span class="hljs-comment">-- date_add 指定日期加上一定时间后的日期及时间</span><br><span class="hljs-keyword">select</span> date_add(now(), <span class="hljs-type">interval</span> <span class="hljs-number">66</span> <span class="hljs-keyword">day</span>); <span class="hljs-comment">-- 从现在往后推66天</span><br><br><span class="hljs-keyword">select</span> date_add(now(), <span class="hljs-type">interval</span> <span class="hljs-number">-10</span> <span class="hljs-keyword">year</span>); <span class="hljs-comment">-- 从现在往前推10年</span><br><br><span class="hljs-comment">-- datediff 返回两个日期之间相差的天数(前面的日期-后面的日期)</span><br><span class="hljs-keyword">select</span> datediff(now(), <span class="hljs-string">'2002-10-26'</span>); <span class="hljs-comment">-- 从我生日到现在多少天了</span><br><br><br><span class="hljs-comment">-- 案例:查询所有员工入职天数,并按照降序排序</span><br><span class="hljs-keyword">select</span> name,datediff(curdate(),entrydate) <span class="hljs-keyword">as</span> 入职天数 <span class="hljs-keyword">from</span> emp <span class="hljs-keyword">order</span> <span class="hljs-keyword">by</span> 入职天数 <span class="hljs-keyword">desc</span>;<br></code></pre></td></tr></table></figure><h2 id="4、流程控制函数"><a href="#4、流程控制函数" class="headerlink" title="4、流程控制函数"></a>4、流程控制函数</h2><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-comment">-- if(bool,val1,val2) true返回val1 false返回val2</span><br><span class="hljs-keyword">select</span> if(<span class="hljs-literal">true</span>, <span class="hljs-string">'cxk'</span>, <span class="hljs-string">'ikun'</span>);<br><span class="hljs-comment">-- 类似于三目运算符</span><br><br><span class="hljs-comment">-- ifnull(v1,v2) 如果v1不为null,返回v1,为null返回v2</span><br><span class="hljs-keyword">select</span> ifnull(<span class="hljs-string">'ok'</span>, <span class="hljs-string">'v2'</span>);<br><br><span class="hljs-keyword">select</span> ifnull(<span class="hljs-string">''</span>, <span class="hljs-string">'v2'</span>); <span class="hljs-comment">-- 注意空字符串不是null</span><br><br><span class="hljs-keyword">select</span> ifnull(<span class="hljs-keyword">null</span>, <span class="hljs-string">'v2'</span>);<br><br><span class="hljs-comment">-- case when then else end</span><br><span class="hljs-comment">-- 查询员工姓名和工作地址,条件 北京/上海 ---> 一线城市 其他 ---> 二线城市</span><br><span class="hljs-keyword">select</span> name,<br> (<span class="hljs-keyword">case</span> workaddress<br> <span class="hljs-keyword">when</span> <span class="hljs-string">'北京'</span> <span class="hljs-keyword">then</span> concat(emp.workaddress, <span class="hljs-string">' 一线城市'</span>)<br> <span class="hljs-keyword">when</span> <span class="hljs-string">'上海'</span> <span class="hljs-keyword">then</span> concat(emp.workaddress, <span class="hljs-string">' 一线城市'</span>)<br> <span class="hljs-keyword">else</span> concat(emp.workaddress, <span class="hljs-string">' 二线城市'</span>) <span class="hljs-keyword">end</span>) <span class="hljs-keyword">as</span> <span class="hljs-string">'工作地址'</span><br><span class="hljs-keyword">from</span> emp;<br></code></pre></td></tr></table></figure><h3 id="案例"><a href="#案例" class="headerlink" title="案例"></a>案例</h3><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-comment">-- 案例:统计班级各个学生的成绩,展示规则如下</span><br><span class="hljs-comment">-- >= 85 优秀</span><br><span class="hljs-comment">-- >= 60 及格</span><br><span class="hljs-comment">-- 否则 不及格</span><br><br><span class="hljs-keyword">create table</span> score<br>(<br> id <span class="hljs-type">int</span> comment <span class="hljs-string">'编号'</span>,<br> name <span class="hljs-type">varchar</span>(<span class="hljs-number">10</span>) comment <span class="hljs-string">'姓名'</span>,<br> math tinyint unsigned comment <span class="hljs-string">'数学'</span>,<br> English tinyint unsigned comment <span class="hljs-string">'英语'</span>,<br> Chinese tinyint unsigned comment <span class="hljs-string">'语文'</span><br>) comment <span class="hljs-string">'成绩表'</span>;<br><br><span class="hljs-keyword">insert into</span> score<br><span class="hljs-keyword">values</span> (<span class="hljs-number">1</span>, <span class="hljs-string">'Tom'</span>, <span class="hljs-number">67</span>, <span class="hljs-number">88</span>, <span class="hljs-number">95</span>),<br> (<span class="hljs-number">2</span>, <span class="hljs-string">'Rose'</span>, <span class="hljs-number">23</span>, <span class="hljs-number">66</span>, <span class="hljs-number">90</span>),<br> (<span class="hljs-number">3</span>, <span class="hljs-string">'Jack'</span>, <span class="hljs-number">56</span>, <span class="hljs-number">98</span>, <span class="hljs-number">76</span>);<br><br><span class="hljs-keyword">select</span> name,<br> (<span class="hljs-keyword">case</span> <span class="hljs-keyword">when</span> math <span class="hljs-operator">>=</span> <span class="hljs-number">85</span> <span class="hljs-keyword">then</span> <span class="hljs-string">'优秀'</span> <span class="hljs-keyword">when</span> math <span class="hljs-operator">>=</span> <span class="hljs-number">60</span> <span class="hljs-keyword">then</span> <span class="hljs-string">'及格'</span> <span class="hljs-keyword">else</span> <span class="hljs-string">'不及格'</span> <span class="hljs-keyword">end</span>) <span class="hljs-keyword">as</span> <span class="hljs-string">'数学'</span>,<br> (<span class="hljs-keyword">case</span> <span class="hljs-keyword">when</span> English <span class="hljs-operator">>=</span> <span class="hljs-number">85</span> <span class="hljs-keyword">then</span> <span class="hljs-string">'优秀'</span> <span class="hljs-keyword">when</span> English <span class="hljs-operator">>=</span> <span class="hljs-number">60</span> <span class="hljs-keyword">then</span> <span class="hljs-string">'及格'</span> <span class="hljs-keyword">else</span> <span class="hljs-string">'不及格'</span> <span class="hljs-keyword">end</span>) <span class="hljs-keyword">as</span> <span class="hljs-string">'英语'</span>,<br> (<span class="hljs-keyword">case</span> <span class="hljs-keyword">when</span> Chinese <span class="hljs-operator">>=</span> <span class="hljs-number">85</span> <span class="hljs-keyword">then</span> <span class="hljs-string">'优秀'</span> <span class="hljs-keyword">when</span> Chinese <span class="hljs-operator">>=</span> <span class="hljs-number">60</span> <span class="hljs-keyword">then</span> <span class="hljs-string">'及格'</span> <span class="hljs-keyword">else</span> <span class="hljs-string">'不及格'</span> <span class="hljs-keyword">end</span>) <span class="hljs-keyword">as</span> <span class="hljs-string">'语文'</span><br><span class="hljs-keyword">from</span> score;<br></code></pre></td></tr></table></figure><h1 id="六、约束"><a href="#六、约束" class="headerlink" title="六、约束"></a>六、约束</h1><h2 id="1、基础约束"><a href="#1、基础约束" class="headerlink" title="1、基础约束"></a>1、基础约束</h2><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">create table</span> user2<br>(<br>id <span class="hljs-type">int</span> <span class="hljs-keyword">primary key</span> auto_increment comment <span class="hljs-string">'主键'</span>, <br> <span class="hljs-comment">-- primary key:主键,auto_increment:id随插入的数据自动增加,无需插入</span><br><br> name <span class="hljs-type">varchar</span>(<span class="hljs-number">10</span>) <span class="hljs-keyword">not null</span> <span class="hljs-keyword">unique</span> comment <span class="hljs-string">'姓名'</span>, <br> <span class="hljs-comment">-- not null:不能为空,unique:唯一的,其他行不能出现重复</span><br><br> age <span class="hljs-type">int</span> <span class="hljs-keyword">check</span> ( age <span class="hljs-operator">></span> <span class="hljs-number">0</span> <span class="hljs-keyword">and</span> age <span class="hljs-operator"><=</span> <span class="hljs-number">120</span> ) comment <span class="hljs-string">'年龄'</span>, <br> <span class="hljs-comment">-- check(条件):必须满足check中的条件</span><br><br> status <span class="hljs-type">char</span>(<span class="hljs-number">1</span>) <span class="hljs-keyword">default</span> <span class="hljs-string">'1'</span> comment <span class="hljs-string">'状态'</span>, <br> <span class="hljs-comment">-- default:没有指定时的默认值</span><br><br> gender <span class="hljs-type">char</span>(<span class="hljs-number">1</span>) comment <span class="hljs-string">'性别'</span><br>) comment <span class="hljs-string">'用户表'</span>;<br></code></pre></td></tr></table></figure><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-comment">-- 插入数据</span><br><span class="hljs-keyword">insert into</span> user2(name, age, status, gender) <span class="hljs-comment">-- id为主键,无需插入,插入也没用,会自动更正</span><br><span class="hljs-keyword">values</span> (<span class="hljs-string">'Tom1'</span>, <span class="hljs-number">10</span>, <span class="hljs-number">0</span>, <span class="hljs-string">'男'</span>),<br>(<span class="hljs-string">'Tom2'</span>, <span class="hljs-number">10</span>, <span class="hljs-number">0</span>, <span class="hljs-string">'男'</span>);<br><br><span class="hljs-keyword">insert into</span> user2(name, age, status, gender)<br><span class="hljs-keyword">values</span> (<span class="hljs-keyword">null</span>, <span class="hljs-number">10</span>, <span class="hljs-number">0</span>, <span class="hljs-string">'男'</span>); <span class="hljs-comment">-- 插入的name不能为null,报错</span><br><br><span class="hljs-keyword">insert into</span> user2 (name, age, status, gender)<br><span class="hljs-keyword">values</span> (<span class="hljs-string">'Tom1'</span>, <span class="hljs-number">11</span>, <span class="hljs-number">0</span>, <span class="hljs-string">'男'</span>); <span class="hljs-comment">-- 插入的name不能重复,报错,虽然没有成功,但是已经申请到主键,所以会空出一个主键</span><br><br><span class="hljs-keyword">insert into</span> user2(name, age, status, gender)<br><span class="hljs-keyword">values</span> (<span class="hljs-string">'Tom3'</span>, <span class="hljs-number">80</span>, <span class="hljs-number">0</span>, <span class="hljs-string">'男'</span>);<br><br><span class="hljs-keyword">insert into</span> user2(name, age, status, gender)<br><span class="hljs-keyword">values</span> (<span class="hljs-string">'Tom3'</span>, <span class="hljs-number">-1</span>, <span class="hljs-number">0</span>, <span class="hljs-string">'男'</span>); <span class="hljs-comment">-- 插入的年龄超出0~120的范围,报错</span><br><br><span class="hljs-keyword">insert into</span> user2(name, age, gender)<br><span class="hljs-keyword">values</span> (<span class="hljs-string">'Tom4'</span>, <span class="hljs-number">10</span>, <span class="hljs-string">'男'</span>); <span class="hljs-comment">-- 不插入status,插入数据后status值默认为1</span><br></code></pre></td></tr></table></figure><h2 id="2、外键约束"><a href="#2、外键约束" class="headerlink" title="2、外键约束"></a>2、外键约束</h2><h3 id="1、增、删外键"><a href="#1、增、删外键" class="headerlink" title="1、增、删外键"></a>1、增、删外键</h3><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-comment">-- 外键约束</span><br><br><span class="hljs-comment">-- 准备数据:准备两张表并插入数据</span><br><br><span class="hljs-keyword">create table</span> dept<br>(<br>id <span class="hljs-type">int</span> <span class="hljs-keyword">primary key</span> auto_increment comment <span class="hljs-string">'ID'</span>,<br>name <span class="hljs-type">varchar</span>(<span class="hljs-number">50</span>) <span class="hljs-keyword">not null</span> comment <span class="hljs-string">'部门名称'</span><br>) comment <span class="hljs-string">'部门表'</span>;<br><br><span class="hljs-keyword">insert into</span> dept(id, name)<br><span class="hljs-keyword">values</span> (<span class="hljs-number">1</span>, <span class="hljs-string">'研发部'</span>),<br> (<span class="hljs-number">2</span>, <span class="hljs-string">'市场部'</span>),<br> (<span class="hljs-number">3</span>, <span class="hljs-string">'财务部'</span>),<br> (<span class="hljs-number">4</span>, <span class="hljs-string">'销售部'</span>),<br> (<span class="hljs-number">5</span>, <span class="hljs-string">'总经办'</span>);<br><br><span class="hljs-keyword">create table</span> emp<br>(<br>id <span class="hljs-type">int</span> <span class="hljs-keyword">primary key</span> auto_increment comment <span class="hljs-string">'主键'</span>,<br>name <span class="hljs-type">varchar</span>(<span class="hljs-number">10</span>) <span class="hljs-keyword">not null</span> <span class="hljs-keyword">unique</span> comment <span class="hljs-string">'姓名'</span>,<br>age <span class="hljs-type">int</span> <span class="hljs-keyword">check</span> ( age <span class="hljs-operator">></span> <span class="hljs-number">0</span> <span class="hljs-keyword">and</span> age <span class="hljs-operator"><=</span> <span class="hljs-number">120</span> ) comment <span class="hljs-string">'年龄'</span>,<br>job <span class="hljs-type">varchar</span>(<span class="hljs-number">10</span>) comment <span class="hljs-string">'工作'</span>,<br>salary <span class="hljs-type">int</span> <span class="hljs-keyword">check</span> ( salary <span class="hljs-operator">></span> <span class="hljs-number">0</span> ) comment <span class="hljs-string">'工资'</span>,<br>entrydate <span class="hljs-type">date</span> comment <span class="hljs-string">'入职时间'</span>,<br>managerid <span class="hljs-type">int</span> comment <span class="hljs-string">'直属领导ID'</span>,<br>dept_id <span class="hljs-type">int</span> comment <span class="hljs-string">'部门ID'</span><br>) comment <span class="hljs-string">'员工表'</span>;<br><br><span class="hljs-keyword">insert into</span> emp(id, name, age, job, salary, entrydate, managerid, dept_id)<br><span class="hljs-keyword">values</span> (<span class="hljs-number">1</span>, <span class="hljs-string">'金庸'</span>, <span class="hljs-number">66</span>, <span class="hljs-string">'总裁'</span>, <span class="hljs-number">20000</span>, <span class="hljs-string">'2000-01-01'</span>, <span class="hljs-keyword">null</span>, <span class="hljs-number">5</span>),<br> (<span class="hljs-number">2</span>, <span class="hljs-string">'张无忌'</span>, <span class="hljs-number">20</span>, <span class="hljs-string">'项目经理'</span>, <span class="hljs-number">12500</span>, <span class="hljs-string">'2005-12-05'</span>, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>),<br> (<span class="hljs-number">3</span>, <span class="hljs-string">'杨逍 '</span>, <span class="hljs-number">33</span>, <span class="hljs-string">'开发'</span>, <span class="hljs-number">8400</span>, <span class="hljs-string">'2000-11-03'</span>, <span class="hljs-number">2</span>, <span class="hljs-number">1</span>),<br> (<span class="hljs-number">4</span>, <span class="hljs-string">'韦一笑'</span>, <span class="hljs-number">48</span>, <span class="hljs-string">'开发 '</span>, <span class="hljs-number">11000</span>, <span class="hljs-string">'2002-02-05'</span>, <span class="hljs-number">2</span>, <span class="hljs-number">1</span>),<br> (<span class="hljs-number">5</span>, <span class="hljs-string">'常遇春'</span>, <span class="hljs-number">43</span>, <span class="hljs-string">'开发'</span>, <span class="hljs-number">10500</span>, <span class="hljs-string">'2004-09-07'</span>, <span class="hljs-number">3</span>, <span class="hljs-number">1</span>),<br> (<span class="hljs-number">6</span>, <span class="hljs-string">'小昭'</span>, <span class="hljs-number">19</span>, <span class="hljs-string">'程序员鼓励师'</span>, <span class="hljs-number">6600</span>, <span class="hljs-string">'2004-10-12'</span>, <span class="hljs-number">2</span>, <span class="hljs-number">1</span>);<br><br><span class="hljs-comment">-- 添加外键</span><br><span class="hljs-keyword">alter table</span> emp <span class="hljs-keyword">add constraint</span> fk_emp_dept_id <span class="hljs-keyword">foreign key</span> (dept_id) <span class="hljs-keyword">references</span><br>dept(id);<br><br><span class="hljs-comment">-- 给emp表的dept_id添加外键约束,关联dept表的主键id,dept变为emp的主表</span><br><span class="hljs-comment">-- fk_emp_dept_id:外键名</span><br><br><span class="hljs-comment">-- 删除外键</span><br><span class="hljs-keyword">alter table</span> emp <span class="hljs-keyword">drop</span> <span class="hljs-keyword">foreign key</span> fk_emp_dept_id;<br></code></pre></td></tr></table></figure><h3 id="2、删除、更新行为"><a href="#2、删除、更新行为" class="headerlink" title="2、删除、更新行为"></a>2、删除、更新行为</h3><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-comment">-- 删除、更新行为</span><br><span class="hljs-comment">-- 添加外键 + on cascade</span><br><span class="hljs-keyword">alter table</span> emp<br><span class="hljs-keyword">add constraint</span> fk_emp_dept_id <span class="hljs-keyword">foreign key</span> (dept_id) <span class="hljs-keyword">references</span> dept (id) <span class="hljs-keyword">on</span> <span class="hljs-keyword">update</span> cascade <span class="hljs-keyword">on</span> <span class="hljs-keyword">delete</span> cascade;<br><br><span class="hljs-comment">-- cascade 在父表中删除/更新外键记录(外键数据)时,检查是否有外键,若有,则子表中的数据也相应的删除/更新</span><br><span class="hljs-comment">-- 通俗:父表删除或更新外键,子表只要有与父表相关的外键,那么子表也跟着更新或删除</span><br><span class="hljs-comment">-- 删除研发部的外键id后,emp表中所有dept_id对应研发部的员工都会被删除</span><br><br><span class="hljs-comment">-- 添加外键 + on set null</span><br><span class="hljs-keyword">alter table</span> emp<br><span class="hljs-keyword">add constraint</span> fk_emp_dept_id <span class="hljs-keyword">foreign key</span> (dept_id) <span class="hljs-keyword">references</span> dept (id) <span class="hljs-keyword">on</span> <span class="hljs-keyword">update</span> <span class="hljs-keyword">set</span> <span class="hljs-keyword">null</span> <span class="hljs-keyword">on</span> <span class="hljs-keyword">delete</span> <span class="hljs-keyword">set</span> <span class="hljs-keyword">null</span>;<br><br><span class="hljs-comment">-- 通俗:父表删除或更新外键,子表只要有与父表相关的外键,那么子表直接设置为null</span><br></code></pre></td></tr></table></figure><h1 id="七、多表查询"><a href="#七、多表查询" class="headerlink" title="七、多表查询"></a>七、多表查询</h1><h2 id="1、概述"><a href="#1、概述" class="headerlink" title="1、概述"></a>1、概述</h2><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-comment">-- 创建dept表,并插入数据</span><br><span class="hljs-keyword">create table</span> dept<br>(<br>id <span class="hljs-type">int</span> auto_increment comment <span class="hljs-string">'ID'</span> <span class="hljs-keyword">primary key</span>,<br>name <span class="hljs-type">varchar</span>(<span class="hljs-number">50</span>) <span class="hljs-keyword">not null</span> comment <span class="hljs-string">'部门名称'</span><br>) comment <span class="hljs-string">'部门表'</span>;<br><span class="hljs-keyword">INSERT INTO</span> dept (id, name)<br><span class="hljs-keyword">VALUES</span> (<span class="hljs-number">1</span>, <span class="hljs-string">'研发部'</span>),<br> (<span class="hljs-number">2</span>, <span class="hljs-string">'市场部'</span>),<br> (<span class="hljs-number">3</span>, <span class="hljs-string">'财务部'</span>),<br> (<span class="hljs-number">4</span>,<br> <span class="hljs-string">'销售部'</span>),<br> (<span class="hljs-number">5</span>, <span class="hljs-string">'总经办'</span>),<br> (<span class="hljs-number">6</span>, <span class="hljs-string">'人事部'</span>);<br><span class="hljs-comment">-- 创建emp表,并插入数据</span><br><span class="hljs-keyword">create table</span> emp<br>(<br> id <span class="hljs-type">int</span> auto_increment comment <span class="hljs-string">'ID'</span> <span class="hljs-keyword">primary key</span>,<br> name <span class="hljs-type">varchar</span>(<span class="hljs-number">50</span>) <span class="hljs-keyword">not null</span> comment <span class="hljs-string">'姓名'</span>,<br> age <span class="hljs-type">int</span> comment <span class="hljs-string">'年龄'</span>,<br> job <span class="hljs-type">varchar</span>(<span class="hljs-number">20</span>) comment <span class="hljs-string">'职位'</span>,<br> salary <span class="hljs-type">int</span> comment <span class="hljs-string">'薪资'</span>,<br> entrydate <span class="hljs-type">date</span> comment <span class="hljs-string">'入职时间'</span>,<br> managerid <span class="hljs-type">int</span> comment <span class="hljs-string">'直属领导ID'</span>,<br> dept_id <span class="hljs-type">int</span> comment <span class="hljs-string">'部门ID'</span><br>) comment <span class="hljs-string">'员工表'</span>;<br><span class="hljs-comment">-- 添加外键</span><br><span class="hljs-keyword">alter table</span> emp<br><span class="hljs-keyword">add constraint</span> fk_emp_dept_id <span class="hljs-keyword">foreign key</span> (dept_id) <span class="hljs-keyword">references</span><br> dept (id);<br><span class="hljs-keyword">INSERT INTO</span> emp (id, name, age, job, salary, entrydate, managerid, dept_id)<br><span class="hljs-keyword">VALUES</span> (<span class="hljs-number">1</span>, <span class="hljs-string">'金庸'</span>, <span class="hljs-number">66</span>, <span class="hljs-string">'总裁'</span>, <span class="hljs-number">20000</span>, <span class="hljs-string">'2000-01-01'</span>, <span class="hljs-keyword">null</span>, <span class="hljs-number">5</span>),<br> (<span class="hljs-number">2</span>, <span class="hljs-string">'张无忌'</span>, <span class="hljs-number">20</span>, <span class="hljs-string">'项目经理'</span>, <span class="hljs-number">12500</span>, <span class="hljs-string">'2005-12-05'</span>, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>),<br> (<span class="hljs-number">3</span>, <span class="hljs-string">'杨逍'</span>, <span class="hljs-number">33</span>, <span class="hljs-string">'开发'</span>, <span class="hljs-number">8400</span>, <span class="hljs-string">'2000-11-03'</span>, <span class="hljs-number">2</span>, <span class="hljs-number">1</span>),<br> (<span class="hljs-number">4</span>, <span class="hljs-string">'韦一笑'</span>, <span class="hljs-number">48</span>, <span class="hljs-string">'开发'</span>, <span class="hljs-number">11000</span>, <span class="hljs-string">'2002-02-05'</span>, <span class="hljs-number">2</span>, <span class="hljs-number">1</span>),<br> (<span class="hljs-number">5</span>, <span class="hljs-string">'常遇春'</span>, <span class="hljs-number">43</span>, <span class="hljs-string">'开发'</span>, <span class="hljs-number">10500</span>, <span class="hljs-string">'2004-09-07'</span>, <span class="hljs-number">3</span>, <span class="hljs-number">1</span>),<br> (<span class="hljs-number">6</span>, <span class="hljs-string">'小昭'</span>, <span class="hljs-number">19</span>, <span class="hljs-string">'程序员鼓励师'</span>, <span class="hljs-number">6600</span>, <span class="hljs-string">'2004-10-12'</span>, <span class="hljs-number">2</span>, <span class="hljs-number">1</span>),<br> (<span class="hljs-number">7</span>, <span class="hljs-string">'灭绝'</span>, <span class="hljs-number">60</span>, <span class="hljs-string">'财务总监'</span>, <span class="hljs-number">8500</span>, <span class="hljs-string">'2002-09-12'</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>),<br> (<span class="hljs-number">8</span>, <span class="hljs-string">'周芷若'</span>, <span class="hljs-number">19</span>, <span class="hljs-string">'会计'</span>, <span class="hljs-number">48000</span>, <span class="hljs-string">'2006-06-02'</span>, <span class="hljs-number">7</span>, <span class="hljs-number">3</span>),<br> (<span class="hljs-number">9</span>, <span class="hljs-string">'丁敏君'</span>, <span class="hljs-number">23</span>, <span class="hljs-string">'出纳'</span>, <span class="hljs-number">5250</span>, <span class="hljs-string">'2009-05-13'</span>, <span class="hljs-number">7</span>, <span class="hljs-number">3</span>),<br> (<span class="hljs-number">10</span>, <span class="hljs-string">'赵敏'</span>, <span class="hljs-number">20</span>, <span class="hljs-string">'市场部总监'</span>, <span class="hljs-number">12500</span>, <span class="hljs-string">'2004-10-12'</span>, <span class="hljs-number">1</span>, <span class="hljs-number">2</span>),<br> (<span class="hljs-number">11</span>, <span class="hljs-string">'鹿杖客'</span>, <span class="hljs-number">56</span>, <span class="hljs-string">'职员'</span>, <span class="hljs-number">3750</span>, <span class="hljs-string">'2006-10-03'</span>, <span class="hljs-number">10</span>, <span class="hljs-number">2</span>),<br> (<span class="hljs-number">12</span>, <span class="hljs-string">'鹤笔翁'</span>, <span class="hljs-number">19</span>, <span class="hljs-string">'职员'</span>, <span class="hljs-number">3750</span>, <span class="hljs-string">'2007-05-09'</span>, <span class="hljs-number">10</span>, <span class="hljs-number">2</span>),<br> (<span class="hljs-number">13</span>, <span class="hljs-string">'方东白'</span>, <span class="hljs-number">19</span>, <span class="hljs-string">'职员'</span>, <span class="hljs-number">5500</span>, <span class="hljs-string">'2009-02-12'</span>, <span class="hljs-number">10</span>, <span class="hljs-number">2</span>),<br> (<span class="hljs-number">14</span>, <span class="hljs-string">'张三丰'</span>, <span class="hljs-number">88</span>, <span class="hljs-string">'销售总监'</span>, <span class="hljs-number">14000</span>, <span class="hljs-string">'2004-10-12'</span>, <span class="hljs-number">1</span>, <span class="hljs-number">4</span>),<br> (<span class="hljs-number">15</span>, <span class="hljs-string">'俞莲舟'</span>, <span class="hljs-number">38</span>, <span class="hljs-string">'销售'</span>, <span class="hljs-number">4600</span>, <span class="hljs-string">'2004-10-12'</span>, <span class="hljs-number">14</span>, <span class="hljs-number">4</span>),<br> (<span class="hljs-number">16</span>, <span class="hljs-string">'宋远桥'</span>, <span class="hljs-number">40</span>, <span class="hljs-string">'销售'</span>, <span class="hljs-number">4600</span>, <span class="hljs-string">'2004-10-12'</span>, <span class="hljs-number">14</span>, <span class="hljs-number">4</span>),<br> (<span class="hljs-number">17</span>, <span class="hljs-string">'陈友谅'</span>, <span class="hljs-number">42</span>, <span class="hljs-keyword">null</span>, <span class="hljs-number">2000</span>, <span class="hljs-string">'2011-10-12'</span>, <span class="hljs-number">1</span>, <span class="hljs-keyword">null</span>);<br><br><span class="hljs-comment">-- 多表查询 -- 笛卡尔积(A集合与B集合所有的组合的情况)</span><br><br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span><br><span class="hljs-keyword">from</span> emp,<br>dept<br><span class="hljs-keyword">where</span> dept_id <span class="hljs-operator">=</span> dept.id; <span class="hljs-comment">-- 通过条件消除多余的笛卡尔积</span><br></code></pre></td></tr></table></figure><h2 id="2、连接查询"><a href="#2、连接查询" class="headerlink" title="2、连接查询"></a>2、连接查询</h2><h3 id="内连接"><a href="#内连接" class="headerlink" title="内连接"></a>内连接</h3><blockquote><p>查询两张表中符合条件的数据,不符合条件的不会返回结果</p></blockquote><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-comment">-- 内连接演示</span><br><span class="hljs-comment">-- 查询员工姓名,并查询关联部门的名称(隐式内连接实现)</span><br><span class="hljs-comment">-- 表结构:emp,dept</span><br><span class="hljs-comment">-- 条件:emp.dept_id = dept.id</span><br><br># <span class="hljs-keyword">select</span> emp.name, dept.name<br># <span class="hljs-keyword">from</span> emp,<br># dept<br># <span class="hljs-keyword">where</span> dept_id <span class="hljs-operator">=</span> dept.id;<br><span class="hljs-comment">-- 给表起别名,起了别名后不能用原名</span><br><span class="hljs-keyword">select</span> e.name, d.name<br><span class="hljs-keyword">from</span> emp e,<br>dept d<br><span class="hljs-keyword">where</span> e.dept_id <span class="hljs-operator">=</span> d.id;<br><br><br><span class="hljs-comment">-- 查询员工姓名,并查询关联部门的名称(显式内连接实现) -- inner join...on... inner可以省略,on后面接条件</span><br><span class="hljs-comment">-- 表结构:emp,dept</span><br><span class="hljs-comment">-- 条件:emp.dept_id = dept.id</span><br><br><span class="hljs-keyword">select</span> e.name, d.name<br><span class="hljs-keyword">from</span> emp e<br> <span class="hljs-keyword">join</span> dept d <span class="hljs-keyword">on</span> e.dept_id <span class="hljs-operator">=</span> d.id;<br></code></pre></td></tr></table></figure><h3 id="外连接"><a href="#外连接" class="headerlink" title="外连接"></a>外连接</h3><blockquote><p>左外连接:查询==左表所有数据==和==右表中符合条件的数据==</p><p>右外连接:查询左表所有数据和右表中符合条件的数据</p></blockquote><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-comment">-- 外连接演示</span><br><span class="hljs-comment">-- 表结构:emp,dept</span><br><span class="hljs-comment">-- 条件:emp.dept_id = dept.id</span><br><br><span class="hljs-comment">-- 左外连接:查询左表所有数据和右、左表相交的数据 left [outer] join...on... outer可以省略,on后面接条件</span><br><span class="hljs-comment">-- 查询所有员工信息和对应部门信息</span><br><span class="hljs-keyword">select</span> e.name, d.name<br><span class="hljs-keyword">from</span> emp e<br> <span class="hljs-keyword">left</span> <span class="hljs-keyword">join</span> dept d <span class="hljs-keyword">on</span> d.id <span class="hljs-operator">=</span> e.dept_id;<br><br><span class="hljs-comment">-- 查询右表所有数据和左、右表相交的数据</span><br><span class="hljs-keyword">select</span> d.<span class="hljs-operator">*</span>, e.<span class="hljs-operator">*</span><br><span class="hljs-keyword">from</span> emp e<br> <span class="hljs-keyword">right</span> <span class="hljs-keyword">join</span> dept d <span class="hljs-keyword">on</span> d.id <span class="hljs-operator">=</span> e.dept_id;<br><span class="hljs-comment">-- 左外连接和右外连接可以互相转化,表的顺序换一下就行了(习惯上左外连接)</span><br></code></pre></td></tr></table></figure><h3 id="自连接"><a href="#自连接" class="headerlink" title="自连接"></a>自连接</h3><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-comment">-- 自连接(自连接时要给表起别名用于区分哪张表)</span><br><br><span class="hljs-comment">-- 表结构:emp</span><br><span class="hljs-comment">-- 内自连接</span><br><span class="hljs-comment">-- 查询员工及其所属领导的名字</span><br><span class="hljs-keyword">select</span> e1.name, e2.name<br><span class="hljs-keyword">from</span> emp e1,<br>emp e2<br><span class="hljs-keyword">where</span> e1.managerid <span class="hljs-operator">=</span> e2.id;<br><br><span class="hljs-comment">-- 外自连接</span><br><span class="hljs-comment">-- 查询员工及其所属领导的名字,没有领导也要查询出来</span><br><span class="hljs-keyword">select</span> a.name <span class="hljs-string">'员工'</span>, b.name <span class="hljs-string">'领导'</span><br><span class="hljs-keyword">from</span> emp a<br> <span class="hljs-keyword">left</span> <span class="hljs-keyword">join</span> emp b <span class="hljs-keyword">on</span> a.managerid <span class="hljs-operator">=</span> b.id;<br></code></pre></td></tr></table></figure><h2 id="union查询"><a href="#union查询" class="headerlink" title="union查询"></a>union查询</h2><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-comment">-- 联合查询 union ,union all</span><br><span class="hljs-comment">-- 将薪资低于5000和年龄大于50的员工全部查询出来</span><br><br><span class="hljs-comment">-- union all 相当于把两张表直接加起来,所以有可能有重复的员工</span><br><span class="hljs-keyword">select</span> name<br><span class="hljs-keyword">from</span> emp<br><span class="hljs-keyword">where</span> salary <span class="hljs-operator"><</span> <span class="hljs-number">5000</span><br><span class="hljs-keyword">union</span> <span class="hljs-keyword">all</span><br><span class="hljs-keyword">select</span> name<br><span class="hljs-keyword">from</span> emp<br><span class="hljs-keyword">where</span> age <span class="hljs-operator">></span> <span class="hljs-number">50</span>;<br><br><span class="hljs-comment">-- union 对union all结果去重</span><br><span class="hljs-keyword">select</span> name<br><span class="hljs-keyword">from</span> emp<br><span class="hljs-keyword">where</span> salary <span class="hljs-operator"><</span> <span class="hljs-number">5000</span><br><span class="hljs-keyword">union</span><br><span class="hljs-keyword">select</span> name<br><span class="hljs-keyword">from</span> emp<br><span class="hljs-keyword">where</span> age <span class="hljs-operator">></span> <span class="hljs-number">50</span>;<br><br><span class="hljs-comment">-- 查询的列数必须相同,字段类型也必须相同</span><br></code></pre></td></tr></table></figure><h2 id="3、子查询"><a href="#3、子查询" class="headerlink" title="3、子查询"></a>3、子查询</h2><h3 id="标量子查询"><a href="#标量子查询" class="headerlink" title="标量子查询"></a>标量子查询</h3><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-comment">-- 标量子查询:后一个select查询的结果为单个值</span><br><span class="hljs-comment">-- 常用操作符:> < <> >= = <=</span><br><span class="hljs-comment">-- 查询 销售部 所有员工信息</span><br><span class="hljs-comment">-- a.先查询销售部门id</span><br><span class="hljs-keyword">select</span> id<br><span class="hljs-keyword">from</span> dept<br><span class="hljs-keyword">where</span> name <span class="hljs-operator">=</span> <span class="hljs-string">'销售部'</span>;<br><span class="hljs-comment">-- b.根据销售部门id查出员工信息</span><br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span><br><span class="hljs-keyword">from</span> emp<br><span class="hljs-keyword">where</span> dept_id <span class="hljs-operator">=</span> (<span class="hljs-keyword">select</span> id <span class="hljs-keyword">from</span> dept <span class="hljs-keyword">where</span> name <span class="hljs-operator">=</span> <span class="hljs-string">'销售部'</span>);<br><br><br><span class="hljs-comment">-- 查询在 方东白 入职之后的员工信息</span><br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span><br><span class="hljs-keyword">from</span> emp<br><span class="hljs-keyword">where</span> entrydate <span class="hljs-operator">></span> (<span class="hljs-keyword">select</span> entrydate <span class="hljs-keyword">from</span> emp <span class="hljs-keyword">where</span> name <span class="hljs-operator">=</span> <span class="hljs-string">'方东白'</span>);<br></code></pre></td></tr></table></figure><h3 id="列子查询"><a href="#列子查询" class="headerlink" title="列子查询"></a>列子查询</h3><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-comment">-- 列子查询</span><br><span class="hljs-comment">-- 1、查询 销售部 和 市场部 的所有员工信息</span><br><br><span class="hljs-comment">-- a.先查询 销售部 和 市场部 的id</span><br><span class="hljs-keyword">select</span> id<br><span class="hljs-keyword">from</span> dept<br><span class="hljs-keyword">where</span> name <span class="hljs-operator">=</span> <span class="hljs-string">'销售部'</span><br><span class="hljs-keyword">or</span> name <span class="hljs-operator">=</span> <span class="hljs-string">'市场部'</span>;<br><br><span class="hljs-comment">-- b.根据id查询员工信息</span><br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span><br><span class="hljs-keyword">from</span> emp<br><span class="hljs-keyword">where</span> dept_id <span class="hljs-keyword">in</span> (<span class="hljs-keyword">select</span> id <span class="hljs-keyword">from</span> dept <span class="hljs-keyword">where</span> name <span class="hljs-operator">=</span> <span class="hljs-string">'销售部'</span> <span class="hljs-keyword">or</span> name <span class="hljs-operator">=</span> <span class="hljs-string">'市场部'</span>);<br><br><span class="hljs-comment">-- 2、查询工资比财务部所有员工工资都高的员工</span><br><br><span class="hljs-comment">-- a.查询财务部所有人的工资</span><br><span class="hljs-keyword">select</span> salary<br><span class="hljs-keyword">from</span> emp<br><span class="hljs-keyword">where</span> dept_id <span class="hljs-operator">=</span> (<span class="hljs-keyword">select</span> id <span class="hljs-keyword">from</span> dept <span class="hljs-keyword">where</span> name <span class="hljs-operator">=</span> <span class="hljs-string">'财务部'</span>);<br><span class="hljs-comment">-- b.根据上述信息查询比财务部所有人工资都高的员工</span><br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span><br><span class="hljs-keyword">from</span> emp<br><span class="hljs-keyword">where</span> salary <span class="hljs-operator">></span> <span class="hljs-keyword">all</span> (<span class="hljs-keyword">select</span> salary <span class="hljs-keyword">from</span> emp <span class="hljs-keyword">where</span> dept_id <span class="hljs-operator">=</span> (<span class="hljs-keyword">select</span> id <span class="hljs-keyword">from</span> dept <span class="hljs-keyword">where</span> name <span class="hljs-operator">=</span> <span class="hljs-string">'财务部'</span>));<br><span class="hljs-comment">-- > all :大于后面查询到的所有数据</span><br><br><span class="hljs-comment">-- 3、查询比研发部至少一人工资高的员工 任意一人:研发部至少存在一个人的工资比我低</span><br><br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span><br><span class="hljs-keyword">from</span> emp<br><span class="hljs-keyword">where</span> salary <span class="hljs-operator">></span> <span class="hljs-keyword">any</span><br>(<span class="hljs-keyword">select</span> salary <span class="hljs-keyword">from</span> emp <span class="hljs-keyword">where</span> dept_id <span class="hljs-operator">=</span> (<span class="hljs-keyword">select</span> id <span class="hljs-keyword">from</span> dept <span class="hljs-keyword">where</span> name <span class="hljs-operator">=</span> <span class="hljs-string">'研发部'</span>));<br><br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span><br><span class="hljs-keyword">from</span> emp<br><span class="hljs-keyword">where</span> salary <span class="hljs-operator">></span> <span class="hljs-keyword">some</span><br>(<span class="hljs-keyword">select</span> salary <span class="hljs-keyword">from</span> emp <span class="hljs-keyword">where</span> dept_id <span class="hljs-operator">=</span> (<span class="hljs-keyword">select</span> id <span class="hljs-keyword">from</span> dept <span class="hljs-keyword">where</span> name <span class="hljs-operator">=</span> <span class="hljs-string">'研发部'</span>));<br><br><span class="hljs-comment">-- any和some等同</span><br></code></pre></td></tr></table></figure><h3 id="行子查询"><a href="#行子查询" class="headerlink" title="行子查询"></a>行子查询</h3><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-comment">-- 行子查询</span><br><span class="hljs-comment">-- 查询与 张无忌 薪资和直属领导相同的员工信息</span><br><br><span class="hljs-comment">-- a.查询张无忌的薪资和直属领导</span><br><span class="hljs-keyword">select</span> salary, managerid<br><span class="hljs-keyword">from</span> emp<br><span class="hljs-keyword">where</span> name <span class="hljs-operator">=</span> <span class="hljs-string">'张无忌'</span><br><br><br><span class="hljs-comment">-- b.查询与 张无忌 薪资和直属领导相同的员工信息</span><br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span><br><span class="hljs-keyword">from</span> emp<br><span class="hljs-keyword">where</span> (salary, managerid) <span class="hljs-operator">=</span> (<span class="hljs-keyword">select</span> salary, managerid <span class="hljs-keyword">from</span> emp <span class="hljs-keyword">where</span> name <span class="hljs-operator">=</span> <span class="hljs-string">'张无忌'</span>);<br></code></pre></td></tr></table></figure><h3 id="表子查询"><a href="#表子查询" class="headerlink" title="表子查询"></a>表子查询</h3><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-comment">-- 表子查询</span><br><span class="hljs-comment">-- 查询和 鹿杖客 宋远桥 职位和薪资相同的员工信息</span><br><br><span class="hljs-comment">-- a.查询鹿杖客 宋远桥 的职位和薪资</span><br><span class="hljs-keyword">select</span> job, salary<br><span class="hljs-keyword">from</span> emp<br><span class="hljs-keyword">where</span> name <span class="hljs-keyword">in</span> (<span class="hljs-string">'鹿杖客'</span>, <span class="hljs-string">'宋远桥'</span>);<br><br><span class="hljs-comment">-- b.查询和 鹿杖客 宋远桥 职位和薪资相同的员工信息</span><br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span><br><span class="hljs-keyword">from</span> emp<br><span class="hljs-keyword">where</span> (job, salary) <span class="hljs-keyword">in</span> (<span class="hljs-keyword">select</span> job, salary <span class="hljs-keyword">from</span> emp <span class="hljs-keyword">where</span> name <span class="hljs-keyword">in</span> (<span class="hljs-string">'鹿杖客'</span>, <span class="hljs-string">'宋远桥'</span>));<br><br><span class="hljs-comment">-- 查询入职时间是‘2006-01-01’之后的员工及其部门信息</span><br><span class="hljs-comment">-- a.查询入职时间是‘2006-01-01’之后的员工</span><br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span><br><span class="hljs-keyword">from</span> emp<br><span class="hljs-keyword">where</span> entrydate <span class="hljs-operator">></span> <span class="hljs-string">'2006-01-01'</span>;<br><span class="hljs-comment">-- b.查询这些员工的部门信息</span><br><span class="hljs-comment">-- 把上述查询的结果当做需要查询的一张表</span><br><span class="hljs-keyword">select</span> e.<span class="hljs-operator">*</span>, d.<span class="hljs-operator">*</span><br><span class="hljs-keyword">from</span> (<span class="hljs-keyword">select</span> <span class="hljs-operator">*</span> <span class="hljs-keyword">from</span> emp <span class="hljs-keyword">where</span> entrydate <span class="hljs-operator">></span> <span class="hljs-string">'2006-01-01'</span>) e<br> <span class="hljs-keyword">left</span> <span class="hljs-keyword">join</span> dept d <span class="hljs-keyword">on</span> e.dept_id <span class="hljs-operator">=</span> d.id;<br></code></pre></td></tr></table></figure><h2 id="4、多表查询案例"><a href="#4、多表查询案例" class="headerlink" title="4、多表查询案例"></a>4、多表查询案例</h2><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-comment">-- --------------->多表查询案例 <--------------</span><br><span class="hljs-comment">-- 准备数据</span><br><span class="hljs-keyword">create table</span> salgrade<br>(<br>grade <span class="hljs-type">int</span>,<br>losal <span class="hljs-type">int</span>,<br>hisal <span class="hljs-type">int</span><br>) comment <span class="hljs-string">'薪资等级表'</span>;<br><span class="hljs-keyword">insert into</span> salgrade<br><span class="hljs-keyword">values</span> (<span class="hljs-number">1</span>, <span class="hljs-number">0</span>, <span class="hljs-number">3000</span>);<br><span class="hljs-keyword">insert into</span> salgrade<br><span class="hljs-keyword">values</span> (<span class="hljs-number">2</span>, <span class="hljs-number">3001</span>, <span class="hljs-number">5000</span>);<br><span class="hljs-keyword">insert into</span> salgrade<br><span class="hljs-keyword">values</span> (<span class="hljs-number">3</span>, <span class="hljs-number">5001</span>, <span class="hljs-number">8000</span>);<br><span class="hljs-keyword">insert into</span> salgrade<br><span class="hljs-keyword">values</span> (<span class="hljs-number">4</span>, <span class="hljs-number">8001</span>, <span class="hljs-number">10000</span>);<br><span class="hljs-keyword">insert into</span> salgrade<br><span class="hljs-keyword">values</span> (<span class="hljs-number">5</span>, <span class="hljs-number">10001</span>, <span class="hljs-number">15000</span>);<br><span class="hljs-keyword">insert into</span> salgrade<br><span class="hljs-keyword">values</span> (<span class="hljs-number">6</span>, <span class="hljs-number">15001</span>, <span class="hljs-number">20000</span>);<br><span class="hljs-keyword">insert into</span> salgrade<br><span class="hljs-keyword">values</span> (<span class="hljs-number">7</span>, <span class="hljs-number">20001</span>, <span class="hljs-number">25000</span>);<br><span class="hljs-keyword">insert into</span> salgrade<br><span class="hljs-keyword">values</span> (<span class="hljs-number">8</span>, <span class="hljs-number">25001</span>, <span class="hljs-number">30000</span>);<br><br><br><span class="hljs-comment">-- 1.查询员工的姓名、年龄、职位、部门信息。</span><br><span class="hljs-keyword">select</span> e.name, e.age, e.job, d.<span class="hljs-operator">*</span><br><span class="hljs-keyword">from</span> emp e<br> <span class="hljs-keyword">left</span> <span class="hljs-keyword">join</span> dept d <span class="hljs-keyword">on</span> e.dept_id <span class="hljs-operator">=</span> d.id;<br><br><span class="hljs-comment">-- 2.查询年龄小于30岁的员工姓名、年龄、职位、部门信息。</span><br><span class="hljs-keyword">select</span> e.name, e.age, e.job, d.<span class="hljs-operator">*</span><br><span class="hljs-keyword">from</span> emp e<br> <span class="hljs-keyword">join</span> dept d <span class="hljs-keyword">on</span> e.dept_id <span class="hljs-operator">=</span> d.id<br><span class="hljs-keyword">where</span> e.age <span class="hljs-operator"><</span> <span class="hljs-number">30</span>;<br><br><span class="hljs-comment">-- 3.查询拥有员工的部门ID、部门名称。</span><br><span class="hljs-keyword">select</span> <span class="hljs-keyword">distinct</span> d.id, d.name<br><span class="hljs-keyword">from</span> dept d,<br>emp e<br><span class="hljs-keyword">where</span> d.id <span class="hljs-operator">=</span> e.dept_id;<br><br><span class="hljs-comment">-- 4.查询所有年龄大于40岁的员工,及其归属的部门名称:如果员工没有分配部门,也需要展示出来。</span><br><span class="hljs-keyword">select</span> e.<span class="hljs-operator">*</span>, d.name<br><span class="hljs-keyword">from</span> emp e<br> <span class="hljs-keyword">left</span> <span class="hljs-keyword">join</span> dept d <span class="hljs-keyword">on</span> e.dept_id <span class="hljs-operator">=</span> d.id<br><span class="hljs-keyword">where</span> e.age <span class="hljs-operator">></span> <span class="hljs-number">40</span>;<br><br><span class="hljs-comment">-- 5.查询所有员工的工资等级。</span><br><span class="hljs-keyword">select</span> e.name, e.salary, s.losal, s.hisal, s.grade<br><span class="hljs-keyword">from</span> emp e<br> <span class="hljs-keyword">left</span> <span class="hljs-keyword">join</span> salgrade s <span class="hljs-keyword">on</span> e.salary <span class="hljs-keyword">between</span> s.losal <span class="hljs-keyword">and</span> s.hisal;<br><br><span class="hljs-comment">-- 6.查询"研发部”所有员工的信息及工资等级。</span><br><br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span><br><span class="hljs-keyword">from</span> emp e,<br>salgrade s,<br>dept d<br><span class="hljs-keyword">where</span> e.dept_id <span class="hljs-operator">=</span> d.id<br><span class="hljs-keyword">and</span> e.salary <span class="hljs-keyword">between</span> s.losal <span class="hljs-keyword">and</span> s.hisal<br><span class="hljs-keyword">and</span> d.name <span class="hljs-operator">=</span> <span class="hljs-string">'研发部'</span>;<br><br><span class="hljs-keyword">select</span> e.<span class="hljs-operator">*</span>, grade<br><span class="hljs-keyword">from</span> (<span class="hljs-keyword">select</span> <span class="hljs-operator">*</span> <span class="hljs-keyword">from</span> emp e <span class="hljs-keyword">where</span> e.dept_id <span class="hljs-operator">=</span> (<span class="hljs-keyword">select</span> id <span class="hljs-keyword">from</span> dept d <span class="hljs-keyword">where</span> d.name <span class="hljs-operator">=</span> <span class="hljs-string">'研发部'</span>)) e<br> <span class="hljs-keyword">left</span> <span class="hljs-keyword">join</span> salgrade s <span class="hljs-keyword">on</span> e.salary <span class="hljs-operator"><=</span> s.hisal <span class="hljs-keyword">and</span> e.salary <span class="hljs-operator">>=</span> s.losal;<br><br><span class="hljs-comment">-- 7.查询"研发部”员工的平均工资。</span><br><span class="hljs-keyword">select</span> <span class="hljs-built_in">avg</span>(e.salary)<br><span class="hljs-keyword">from</span> emp e,<br>dept d<br><span class="hljs-keyword">where</span> e.dept_id <span class="hljs-operator">=</span> d.id<br><span class="hljs-keyword">and</span> d.name <span class="hljs-operator">=</span> <span class="hljs-string">'研发部'</span>;<br><br><span class="hljs-comment">-- 8.查询工资比"灭绝"高的员工信息。</span><br><span class="hljs-comment">-- 子查询</span><br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span><br><span class="hljs-keyword">from</span> emp<br><span class="hljs-keyword">where</span> salary <span class="hljs-operator">></span> (<span class="hljs-keyword">select</span> salary <span class="hljs-keyword">from</span> emp <span class="hljs-keyword">where</span> name <span class="hljs-operator">=</span> <span class="hljs-string">'灭绝'</span>);<br><br><span class="hljs-comment">-- 自查询</span><br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span><br><span class="hljs-keyword">from</span> emp a,<br>emp b<br><span class="hljs-keyword">where</span> a.salary <span class="hljs-operator">></span> b.salary<br><span class="hljs-keyword">and</span> b.name <span class="hljs-operator">=</span> <span class="hljs-string">'灭绝'</span>;<br><br><span class="hljs-comment">-- 9.查询比平均薪资高的员工信息。</span><br><span class="hljs-keyword">select</span> <span class="hljs-built_in">avg</span>(salary)<br><span class="hljs-keyword">from</span> emp;<br><br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span><br><span class="hljs-keyword">from</span> emp<br><span class="hljs-keyword">where</span> salary <span class="hljs-operator">></span> (<span class="hljs-keyword">select</span> <span class="hljs-built_in">avg</span>(salary) <span class="hljs-keyword">from</span> emp);<br><br><span class="hljs-comment">-- 10.查询低于本部门平均工资的员工信息。</span><br><span class="hljs-comment">-- 查询某部门平均薪资</span><br><span class="hljs-keyword">select</span> <span class="hljs-built_in">avg</span>(e.salary)<br><span class="hljs-keyword">from</span> emp e,<br>dept d<br><span class="hljs-keyword">where</span> e.dept_id <span class="hljs-operator">=</span> d.id<br><span class="hljs-keyword">and</span> d.id <span class="hljs-operator">=</span> <span class="hljs-number">1</span>;<br><span class="hljs-comment">-- 查询低于本部门平均工资的员工信息。</span><br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span><br><span class="hljs-keyword">from</span> emp e1<br><span class="hljs-keyword">where</span> e1.salary <span class="hljs-operator"><</span> (<span class="hljs-keyword">select</span> <span class="hljs-built_in">avg</span>(e2.salary)<br> <span class="hljs-keyword">from</span> emp e2,<br> dept d<br> <span class="hljs-keyword">where</span> e2.dept_id <span class="hljs-operator">=</span> d.id<br> <span class="hljs-keyword">and</span> d.id <span class="hljs-operator">=</span> e1.dept_id);<br><br><span class="hljs-comment">-- 11.查询所有的部门信息,并统计部门的员工人数。</span><br><span class="hljs-comment">-- 查询所有部门信息</span><br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span><br><span class="hljs-keyword">from</span> dept;<br><span class="hljs-comment">-- 统计1号部门员工数量</span><br><span class="hljs-keyword">select</span> <span class="hljs-built_in">count</span>(<span class="hljs-operator">*</span>)<br><span class="hljs-keyword">from</span> emp<br><span class="hljs-keyword">where</span> dept_id <span class="hljs-operator">=</span> <span class="hljs-number">1</span>;<br><span class="hljs-comment">-- 查询所有的部门信息,并统计部门的员工人数</span><br><span class="hljs-keyword">select</span> d.id, d.name, (<span class="hljs-keyword">select</span> <span class="hljs-built_in">count</span>(<span class="hljs-operator">*</span>) <span class="hljs-keyword">from</span> emp e <span class="hljs-keyword">where</span> e.dept_id <span class="hljs-operator">=</span> d.id) <span class="hljs-string">'人数'</span><br><span class="hljs-keyword">from</span> dept d;<br></code></pre></td></tr></table></figure><h1 id="八、事务"><a href="#八、事务" class="headerlink" title="八、事务"></a>八、事务</h1><h2 id="1、事务简介"><a href="#1、事务简介" class="headerlink" title="1、事务简介"></a>1、事务简介</h2><blockquote><p>事务 是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系 统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败。</p></blockquote><h2 id="2、事务操作"><a href="#2、事务操作" class="headerlink" title="2、事务操作"></a>2、事务操作</h2><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-comment">-- 事务</span><br><br><span class="hljs-comment">-- ---> 准备数据 <---------------------</span><br><span class="hljs-keyword">create table</span> account<br>(<br>id <span class="hljs-type">int</span> <span class="hljs-keyword">primary key</span> AUTO_INCREMENT comment <span class="hljs-string">'ID'</span>,<br>name <span class="hljs-type">varchar</span>(<span class="hljs-number">10</span>) comment <span class="hljs-string">'姓名'</span>,<br>money <span class="hljs-keyword">double</span>(<span class="hljs-number">10</span>, <span class="hljs-number">2</span>) comment <span class="hljs-string">'余额'</span><br>) comment <span class="hljs-string">'账户表'</span>;<br><br><span class="hljs-keyword">insert into</span> account(name, money)<br><span class="hljs-keyword">VALUES</span> (<span class="hljs-string">'张三'</span>, <span class="hljs-number">2000</span>),<br> (<span class="hljs-string">'李四'</span>, <span class="hljs-number">2000</span>);<br><br><span class="hljs-comment">-- -----------------------------------</span><br><br><span class="hljs-comment">-- 恢复数据操作</span><br><span class="hljs-keyword">update</span> account<br><span class="hljs-keyword">set</span> money <span class="hljs-operator">=</span> <span class="hljs-number">2000</span><br><span class="hljs-keyword">where</span> name <span class="hljs-keyword">in</span> (<span class="hljs-string">'张三'</span>, <span class="hljs-string">'李四'</span>);<br><br><br><span class="hljs-keyword">select</span> @<span class="hljs-variable">@autocommit</span>; <span class="hljs-comment">-- 查询事务提交方式 0-手动 1-自动</span><br><br><span class="hljs-comment">-- 方式一</span><br><span class="hljs-keyword">set</span> @<span class="hljs-variable">@autocommit</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;<span class="hljs-comment">-- 设置为手动提交</span><br><br><br><span class="hljs-comment">-- 转账操作(张三转给李四1000元)</span><br><span class="hljs-comment">-- 查询张三余额</span><br><span class="hljs-keyword">select</span> money<br><span class="hljs-keyword">from</span> account<br><span class="hljs-keyword">where</span> name <span class="hljs-operator">=</span> <span class="hljs-string">'张三'</span>;<br><br><span class="hljs-comment">-- 张三余额-1000</span><br><span class="hljs-keyword">update</span> account<br><span class="hljs-keyword">set</span> money <span class="hljs-operator">=</span> money <span class="hljs-operator">-</span> <span class="hljs-number">1000</span><br><span class="hljs-keyword">where</span> name <span class="hljs-operator">=</span> <span class="hljs-string">'张三'</span>;<br><br><span class="hljs-comment">-- 若此处出现异常,则张三-1000元,但李四并没有+1000元</span><br>异常... <span class="hljs-comment">-- 模拟出错场景</span><br><br><span class="hljs-comment">-- 李四余额+1000</span><br><span class="hljs-keyword">update</span> account<br><span class="hljs-keyword">set</span> money <span class="hljs-operator">=</span> money <span class="hljs-operator">+</span> <span class="hljs-number">1000</span><br><span class="hljs-keyword">where</span> name <span class="hljs-operator">=</span> <span class="hljs-string">'李四'</span>;<br><br><span class="hljs-comment">-- 提交事务</span><br><span class="hljs-keyword">commit</span>;<br><span class="hljs-comment">-- 回滚事务</span><br><span class="hljs-keyword">rollback</span>;<br><br><span class="hljs-comment">-- 方式二</span><br><br><span class="hljs-keyword">set</span> @<span class="hljs-variable">@autocommit</span> <span class="hljs-operator">=</span> <span class="hljs-number">1</span>; <span class="hljs-comment">-- 设置为自动提交</span><br><br><span class="hljs-keyword">start</span> transaction;<br><br><span class="hljs-comment">-- 转账操作(张三转给李四1000元)</span><br><span class="hljs-comment">-- 查询张三余额</span><br><span class="hljs-keyword">select</span> money<br><span class="hljs-keyword">from</span> account<br><span class="hljs-keyword">where</span> name <span class="hljs-operator">=</span> <span class="hljs-string">'张三'</span>;<br><br><span class="hljs-comment">-- 张三余额-1000</span><br><span class="hljs-keyword">update</span> account<br><span class="hljs-keyword">set</span> money <span class="hljs-operator">=</span> money <span class="hljs-operator">-</span> <span class="hljs-number">1000</span><br><span class="hljs-keyword">where</span> name <span class="hljs-operator">=</span> <span class="hljs-string">'张三'</span>;<br><br><span class="hljs-comment">-- 若此处出现异常,则张三-1000元,但李四并没有+1000元</span><br>异常... <span class="hljs-comment">-- 模拟出错场景</span><br><br><span class="hljs-comment">-- 李四余额+1000</span><br><span class="hljs-keyword">update</span> account<br><span class="hljs-keyword">set</span> money <span class="hljs-operator">=</span> money <span class="hljs-operator">+</span> <span class="hljs-number">1000</span><br><span class="hljs-keyword">where</span> name <span class="hljs-operator">=</span> <span class="hljs-string">'李四'</span>;<br><br><span class="hljs-comment">-- 提交事务</span><br><span class="hljs-keyword">commit</span>;<br><span class="hljs-comment">-- 回滚事务</span><br><span class="hljs-keyword">rollback</span>;<br></code></pre></td></tr></table></figure><h2 id="3、事务的四大特性(ACID)"><a href="#3、事务的四大特性(ACID)" class="headerlink" title="3、事务的四大特性(ACID)"></a>3、事务的四大特性(ACID)</h2><blockquote><ol><li><strong>原子性(Atomicity):事务是不可分割的最小操作单元,要么全部成功,要么全部失败。</strong></li></ol></blockquote><blockquote><ol start="2"><li><p><strong>一致性(Consistency):事务完成时,必须使所有的数据都保持一致状态。</strong></p></li><li><p><strong>一致性(Consistency):事务完成时,必须使所有的数据都保持一致状态。</strong></p></li></ol></blockquote><blockquote><ol start="3"><li><p><strong>隔离性(Isolation):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立 环境下运行。</strong></p></li><li><p><strong>持久性(Durability):事务一旦提交或回滚,它对数据库中的数据的改变就是永久的。</strong></p></li></ol></blockquote><h2 id="4、并发事务问题"><a href="#4、并发事务问题" class="headerlink" title="4、并发事务问题"></a>4、并发事务问题</h2><blockquote><p><strong>脏读:一个事务读取到另外一个事务未提交的数据。</strong></p><p><strong>不可重复读:一个事务先后读取同一条数据,但两次读取到的数据不同。</strong></p><p><strong>幻读:一个事务查询数据时没有对应数据,插入该数据时又发现该数据已经存在,好像出现“幻影”。</strong></p></blockquote><h2 id="5、隔离级别"><a href="#5、隔离级别" class="headerlink" title="5、隔离级别"></a>5、隔离级别</h2><figure class="highlight sql"><table><tr><td class="code"><pre><code class="hljs sql"><span class="hljs-comment">-- 并发事务与隔离级别</span><br><br><span class="hljs-comment">-- 查询事务隔离级别</span><br><span class="hljs-keyword">select</span> @<span class="hljs-variable">@transaction_isolation</span>;<br><br><span class="hljs-comment">-- 设置事务隔离级别 session-仅当前页面起作用 transaction isolation level-事务隔离级别</span><br><span class="hljs-keyword">set</span> session transaction isolation level read uncommitted;<br><br><span class="hljs-comment">-- repeatable read是默认级别</span><br><span class="hljs-keyword">set</span> session transaction isolation level repeatable read ;<br><br><span class="hljs-comment">-- 隔离级别越高,数据越安全,但是性能越低</span><br></code></pre></td></tr></table></figure><h1 id="九、JDBC"><a href="#九、JDBC" class="headerlink" title="九、JDBC"></a>九、JDBC</h1><h2 id="1、jdbc7步编程"><a href="#1、jdbc7步编程" class="headerlink" title="1、jdbc7步编程"></a>1、jdbc7步编程</h2><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//JDBC快速入门</span><br><span class="hljs-comment">//1、注册驱动</span><br>Class.forName(<span class="hljs-string">"com.mysql.cj.jdbc.Driver"</span>);<br><br><span class="hljs-comment">//2、获取连接</span><br><span class="hljs-type">String</span> <span class="hljs-variable">url</span> <span class="hljs-operator">=</span> <span class="hljs-string">"jdbc:mysql://localhost:3306/clb"</span>;<br><span class="hljs-type">String</span> <span class="hljs-variable">username</span> <span class="hljs-operator">=</span> <span class="hljs-string">"root"</span>;<br><span class="hljs-type">String</span> <span class="hljs-variable">password</span> <span class="hljs-operator">=</span> <span class="hljs-string">"123456"</span>;<br><span class="hljs-type">Connection</span> <span class="hljs-variable">conn</span> <span class="hljs-operator">=</span> <span class="hljs-literal">null</span>;<br>conn = DriverManager.getConnection(url, username, password);<br><br><br><span class="hljs-comment">//3、定义sql</span><br><span class="hljs-type">String</span> <span class="hljs-variable">sql</span> <span class="hljs-operator">=</span> <span class="hljs-string">"update account set money = 666 where id = 1"</span>;<br><br><span class="hljs-comment">//4、获取执行sql的对象 Statement</span><br><span class="hljs-type">Statement</span> <span class="hljs-variable">stmt</span> <span class="hljs-operator">=</span> <span class="hljs-literal">null</span>;<br>stmt = conn.createStatement();<br><br><br><span class="hljs-comment">//5、执行sql</span><br><span class="hljs-type">int</span> <span class="hljs-variable">count</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;<span class="hljs-comment">//受影响的行数</span><br>count = stmt.executeUpdate(sql);<br><br><br><span class="hljs-comment">//6、处理结果</span><br>System.out.println(<span class="hljs-string">"受影响的行数有"</span> + count + <span class="hljs-string">"行"</span>);<br><br><span class="hljs-comment">//7、释放资源</span><br>stmt.close();<br>conn.close();<br></code></pre></td></tr></table></figure><h2 id="2、DriverManager"><a href="#2、DriverManager" class="headerlink" title="2、DriverManager"></a>2、DriverManager</h2><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"> <span class="hljs-comment">//1、注册驱动 -->可以省略</span><br> <span class="hljs-comment">//Class.forName("com.mysql.cj.jdbc.Driver");</span><br><br> <span class="hljs-comment">//2、获取连接 -->如果连接的是本机的数据库且端口是默认的 3306 则可以简写</span><br> <span class="hljs-comment">// String url = "jdbc:mysql://localhost:3306/clb";</span><br> <span class="hljs-comment">//简写:</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">url</span> <span class="hljs-operator">=</span> <span class="hljs-string">"jdbc:mysql:/clb"</span>;<br> <span class="hljs-type">String</span> <span class="hljs-variable">username</span> <span class="hljs-operator">=</span> <span class="hljs-string">"root"</span>;<br> <span class="hljs-type">String</span> <span class="hljs-variable">password</span> <span class="hljs-operator">=</span> <span class="hljs-string">"123456"</span>;<br> <span class="hljs-type">Connection</span> <span class="hljs-variable">conn</span> <span class="hljs-operator">=</span> DriverManager.getConnection(url, username, password);<br><br><br> <span class="hljs-comment">//3、定义sql</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">sql</span> <span class="hljs-operator">=</span> <span class="hljs-string">"update account set money = 1000 where id = 1"</span>;<br><br> <span class="hljs-comment">//4、获取执行sql的对象 Statement</span><br> <span class="hljs-type">Statement</span> <span class="hljs-variable">stmt</span> <span class="hljs-operator">=</span> <span class="hljs-literal">null</span>;<br> stmt = conn.createStatement();<br><br><br> <span class="hljs-comment">//5、执行sql</span><br> <span class="hljs-type">int</span> <span class="hljs-variable">count</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;<span class="hljs-comment">//受影响的行数</span><br> count = stmt.executeUpdate(sql);<br><br><br> <span class="hljs-comment">//6、处理结果</span><br> System.out.println(<span class="hljs-string">"受影响的行数有"</span> + count + <span class="hljs-string">"行"</span>);<br><br> <span class="hljs-comment">//7、释放资源</span><br><br> stmt.close();<br> conn.close();<br><span class="hljs-comment">/*</span><br><span class="hljs-comment"> DriverManager有两个作用:</span><br><span class="hljs-comment"> 1、注册驱动</span><br><span class="hljs-comment"> 2、获取数据库连接</span><br><span class="hljs-comment">*/</span><br></code></pre></td></tr></table></figure><h2 id="3、Connection"><a href="#3、Connection" class="headerlink" title="3、Connection"></a>3、Connection</h2><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//1、管理事务 </span><br><span class="hljs-keyword">try</span> {<br> <span class="hljs-comment">//开启事务</span><br> conn.setAutoCommit(<span class="hljs-literal">false</span>);<span class="hljs-comment">//将自动提交改为手动</span><br><br> <span class="hljs-comment">//5、执行sql</span><br> <span class="hljs-type">int</span> <span class="hljs-variable">count</span> <span class="hljs-operator">=</span> stmt.executeUpdate(sql);<br> <span class="hljs-comment">//6、处理结果</span><br> System.out.println(<span class="hljs-string">"受影响的行数有"</span> + count + <span class="hljs-string">"行"</span>);<br><br> <span class="hljs-comment">//制造异常</span><br> <span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">3</span> / <span class="hljs-number">0</span>;<br><br> <span class="hljs-comment">//5、执行sql</span><br> <span class="hljs-type">int</span> <span class="hljs-variable">count2</span> <span class="hljs-operator">=</span> stmt.executeUpdate(sql2);<br> <span class="hljs-comment">//6、处理结果</span><br> System.out.println(<span class="hljs-string">"受影响的行数有"</span> + count2 + <span class="hljs-string">"行"</span>);<br><br> <span class="hljs-comment">//提交事务</span><br> conn.commit();<br>} <span class="hljs-keyword">catch</span> (Exception e) {<br> <span class="hljs-comment">//回滚事务</span><br> conn.rollback();<br> e.printStackTrace();<br>}<br></code></pre></td></tr></table></figure><h2 id="4、Statement"><a href="#4、Statement" class="headerlink" title="4、Statement"></a>4、Statement</h2><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> JDBC;<br><br><span class="hljs-keyword">import</span> org.junit.Test;<br><br><span class="hljs-keyword">import</span> java.sql.Connection;<br><span class="hljs-keyword">import</span> java.sql.DriverManager;<br><span class="hljs-keyword">import</span> java.sql.Statement;<br><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Demo3Statement</span> {<br><span class="hljs-comment">/*</span><br><span class="hljs-comment"> Statement:</span><br><span class="hljs-comment"> 1、执行SQL语句</span><br><span class="hljs-comment"> int executeUpdate(sql) -> 执行DML、DDL语句</span><br><span class="hljs-comment"> 执行DML语句</span><br><span class="hljs-comment">*/</span><br><br><span class="hljs-meta">@Test</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">testDML</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Exception {<br> <span class="hljs-comment">//1、注册驱动</span><br> <span class="hljs-comment">//Class.forName("com.mysql.cj.jdbc.Driver");</span><br><br> <span class="hljs-comment">//2、获取连接</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">url</span> <span class="hljs-operator">=</span> <span class="hljs-string">"jdbc:mysql://localhost:3306/clb"</span>;<br> <span class="hljs-type">String</span> <span class="hljs-variable">username</span> <span class="hljs-operator">=</span> <span class="hljs-string">"root"</span>;<br> <span class="hljs-type">String</span> <span class="hljs-variable">password</span> <span class="hljs-operator">=</span> <span class="hljs-string">"123456"</span>;<br> <span class="hljs-type">Connection</span> <span class="hljs-variable">conn</span> <span class="hljs-operator">=</span> <span class="hljs-literal">null</span>;<br> conn = DriverManager.getConnection(url, username, password);<br><br><br> <span class="hljs-comment">//3、定义sql</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">sql</span> <span class="hljs-operator">=</span> <span class="hljs-string">"update account set money = 666 where id = 1"</span>;<br><br> <span class="hljs-comment">//4、获取执行sql的对象 Statement</span><br> <span class="hljs-type">Statement</span> <span class="hljs-variable">stmt</span> <span class="hljs-operator">=</span> conn.createStatement();<br><br> <span class="hljs-comment">//5、执行sql</span><br> <span class="hljs-type">int</span> <span class="hljs-variable">count</span> <span class="hljs-operator">=</span> stmt.executeUpdate(sql);<br><br> <span class="hljs-comment">//6、处理结果</span><br> System.out.println(count > <span class="hljs-number">0</span> ? <span class="hljs-string">"修改成功"</span> : <span class="hljs-string">"修改失败"</span>);<br><br> <span class="hljs-comment">//7、释放资源</span><br> stmt.close();<br> conn.close();<br>}<br><br><br><span class="hljs-comment">/*</span><br><span class="hljs-comment"> 执行DDL语句</span><br><span class="hljs-comment">*/</span><br><br><span class="hljs-meta">@Test</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">testDDL</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Exception {<br> <span class="hljs-comment">//1、注册驱动</span><br> <span class="hljs-comment">//Class.forName("com.mysql.cj.jdbc.Driver");</span><br><br> <span class="hljs-comment">//2、获取连接</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">url</span> <span class="hljs-operator">=</span> <span class="hljs-string">"jdbc:mysql://localhost:3306/clb"</span>;<br> <span class="hljs-type">String</span> <span class="hljs-variable">username</span> <span class="hljs-operator">=</span> <span class="hljs-string">"root"</span>;<br> <span class="hljs-type">String</span> <span class="hljs-variable">password</span> <span class="hljs-operator">=</span> <span class="hljs-string">"123456"</span>;<br> <span class="hljs-type">Connection</span> <span class="hljs-variable">conn</span> <span class="hljs-operator">=</span> <span class="hljs-literal">null</span>;<br> conn = DriverManager.getConnection(url, username, password);<br><br> <span class="hljs-comment">//3、定义sql</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">sql</span> <span class="hljs-operator">=</span> <span class="hljs-string">"drop database cxk"</span>;<br><br> <span class="hljs-comment">//4、获取执行sql的对象 Statement</span><br> <span class="hljs-type">Statement</span> <span class="hljs-variable">stmt</span> <span class="hljs-operator">=</span> conn.createStatement();<br><br> <span class="hljs-comment">//5、执行sql</span><br> <span class="hljs-type">int</span> <span class="hljs-variable">count</span> <span class="hljs-operator">=</span> stmt.executeUpdate(sql);<br><br> <span class="hljs-comment">//6、处理结果</span><br><span class="hljs-comment">// System.out.println(count > 0 ? "修改成功" : "修改失败");</span><br> System.out.println(count);<br><br> <span class="hljs-comment">//7、释放资源</span><br><br> stmt.close();<br> conn.close();<br>}<br>}<br></code></pre></td></tr></table></figure><h2 id="5、ResultSet"><a href="#5、ResultSet" class="headerlink" title="5、ResultSet"></a>5、ResultSet</h2><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> JDBC;<br><br><span class="hljs-keyword">import</span> org.junit.Test;<br><span class="hljs-keyword">import</span> pojo.Account;<br><br><span class="hljs-keyword">import</span> java.sql.Connection;<br><span class="hljs-keyword">import</span> java.sql.DriverManager;<br><span class="hljs-keyword">import</span> java.sql.ResultSet;<br><span class="hljs-keyword">import</span> java.sql.Statement;<br><span class="hljs-keyword">import</span> java.util.ArrayList;<br><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Demo4ResultSet</span> {<br><span class="hljs-comment">/*</span><br><span class="hljs-comment"> ResultSet:</span><br><span class="hljs-comment"> 执行DQL查询语句</span><br><span class="hljs-comment">*/</span><br><br><span class="hljs-meta">@Test</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">ResultSetDemo</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Exception {<br> <span class="hljs-comment">//1、注册驱动</span><br> <span class="hljs-comment">//Class.forName("com.mysql.cj.jdbc.Driver");</span><br><br> <span class="hljs-comment">//2、获取连接</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">url</span> <span class="hljs-operator">=</span> <span class="hljs-string">"jdbc:mysql://localhost:3306/clb"</span>;<br> <span class="hljs-type">String</span> <span class="hljs-variable">username</span> <span class="hljs-operator">=</span> <span class="hljs-string">"root"</span>;<br> <span class="hljs-type">String</span> <span class="hljs-variable">password</span> <span class="hljs-operator">=</span> <span class="hljs-string">"123456"</span>;<br> <span class="hljs-type">Connection</span> <span class="hljs-variable">conn</span> <span class="hljs-operator">=</span> <span class="hljs-literal">null</span>;<br> conn = DriverManager.getConnection(url, username, password);<br> <span class="hljs-comment">//3、定义sql语句</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">sql</span> <span class="hljs-operator">=</span> <span class="hljs-string">"select * from account"</span>;<br> <span class="hljs-comment">//4、获取statement对象</span><br> <span class="hljs-type">Statement</span> <span class="hljs-variable">stmt</span> <span class="hljs-operator">=</span> conn.createStatement();<br> <span class="hljs-comment">//5、执行sql</span><br> <span class="hljs-type">ResultSet</span> <span class="hljs-variable">rs</span> <span class="hljs-operator">=</span> stmt.executeQuery(sql);<br><br> <span class="hljs-comment">//6、处理数据,遍历rs中的所有数据</span><br> <span class="hljs-comment">//6.1光标向下移动一行,判断当前行是否有数据</span><br> <span class="hljs-comment">/*</span><br><span class="hljs-comment"> while (rs.next()) {</span><br><span class="hljs-comment"> //6.2 获取数据getXxx()</span><br><span class="hljs-comment"> int id = rs.getInt(1);</span><br><span class="hljs-comment"> String name = rs.getString(2);</span><br><span class="hljs-comment"> double money = rs.getDouble(3);</span><br><span class="hljs-comment"></span><br><span class="hljs-comment"> System.out.println(id);</span><br><span class="hljs-comment"> System.out.println(name);</span><br><span class="hljs-comment"> System.out.println(money);</span><br><span class="hljs-comment"> System.out.println("-------------------");</span><br><span class="hljs-comment"> }</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-keyword">while</span> (rs.next()) {<br> <span class="hljs-comment">//6.2 获取数据getXxx()</span><br> <span class="hljs-type">int</span> <span class="hljs-variable">id</span> <span class="hljs-operator">=</span> rs.getInt(<span class="hljs-string">"id"</span>);<br> <span class="hljs-type">String</span> <span class="hljs-variable">name</span> <span class="hljs-operator">=</span> rs.getString(<span class="hljs-string">"name"</span>);<br> <span class="hljs-type">double</span> <span class="hljs-variable">money</span> <span class="hljs-operator">=</span> rs.getDouble(<span class="hljs-string">"money"</span>);<br><br> System.out.println(id);<br> System.out.println(name);<br> System.out.println(money);<br> System.out.println(<span class="hljs-string">"-------------------"</span>);<br> }<br><br> <span class="hljs-comment">//7、释放资源</span><br> rs.close();<br> stmt.close();<br> conn.close();<br>}<br><br> <span class="hljs-comment">/*</span><br><span class="hljs-comment"> 查询数据库中account表的数据,封装成Account对象,并存储到ArrayList表中</span><br><span class="hljs-comment"> 1、定义实体类Account</span><br><span class="hljs-comment"> 2、查询数据</span><br><span class="hljs-comment"> 3、封装数据到ArrayList表中</span><br><span class="hljs-comment">*/</span><br><br><span class="hljs-meta">@Test</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">ResultSetDemo2</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Exception {<br> <span class="hljs-comment">//1、注册驱动</span><br> <span class="hljs-comment">//Class.forName("com.mysql.cj.jdbc.Driver");</span><br><br> <span class="hljs-comment">//2、获取连接</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">url</span> <span class="hljs-operator">=</span> <span class="hljs-string">"jdbc:mysql://localhost:3306/clb"</span>;<br> <span class="hljs-type">String</span> <span class="hljs-variable">username</span> <span class="hljs-operator">=</span> <span class="hljs-string">"root"</span>;<br> <span class="hljs-type">String</span> <span class="hljs-variable">password</span> <span class="hljs-operator">=</span> <span class="hljs-string">"123456"</span>;<br> <span class="hljs-type">Connection</span> <span class="hljs-variable">conn</span> <span class="hljs-operator">=</span> <span class="hljs-literal">null</span>;<br> conn = DriverManager.getConnection(url, username, password);<br> <span class="hljs-comment">//3、定义sql语句</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">sql</span> <span class="hljs-operator">=</span> <span class="hljs-string">"select * from account"</span>;<br> <span class="hljs-comment">//4、获取statement对象</span><br> <span class="hljs-type">Statement</span> <span class="hljs-variable">stmt</span> <span class="hljs-operator">=</span> conn.createStatement();<br> <span class="hljs-comment">//5、执行sql</span><br> <span class="hljs-type">ResultSet</span> <span class="hljs-variable">rs</span> <span class="hljs-operator">=</span> stmt.executeQuery(sql);<br><br> ArrayList<Account> lst = <span class="hljs-keyword">new</span> <span class="hljs-title class_">ArrayList</span><>();<br><br> <span class="hljs-keyword">while</span> (rs.next()) {<br> <span class="hljs-comment">//6.2 获取数据getXxx()</span><br> <span class="hljs-type">int</span> <span class="hljs-variable">id</span> <span class="hljs-operator">=</span> rs.getInt(<span class="hljs-string">"id"</span>);<br> <span class="hljs-type">String</span> <span class="hljs-variable">name</span> <span class="hljs-operator">=</span> rs.getString(<span class="hljs-string">"name"</span>);<br> <span class="hljs-type">double</span> <span class="hljs-variable">money</span> <span class="hljs-operator">=</span> rs.getDouble(<span class="hljs-string">"money"</span>);<br> lst.add(<span class="hljs-keyword">new</span> <span class="hljs-title class_">Account</span>(id, name, money));<br> }<br><br> System.out.println(lst);<br><br> <span class="hljs-comment">//7、释放资源</span><br> rs.close();<br> stmt.close();<br> conn.close();<br>}<br><br>}<br></code></pre></td></tr></table></figure><h2 id="6、用户登陆"><a href="#6、用户登陆" class="headerlink" title="6、用户登陆"></a>6、用户登陆</h2><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> JDBC;<br><br><span class="hljs-keyword">import</span> org.junit.Test;<br><span class="hljs-keyword">import</span> java.sql.Connection;<br><span class="hljs-keyword">import</span> java.sql.DriverManager;<br><span class="hljs-keyword">import</span> java.sql.ResultSet;<br><span class="hljs-keyword">import</span> java.sql.Statement;<br><br><span class="hljs-comment">/*</span><br><span class="hljs-comment">用户登陆</span><br><span class="hljs-comment">*/</span><br><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">UserLoginDemo5</span> {<br><br><span class="hljs-meta">@Test</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">UserLogin</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Exception {<br> <span class="hljs-comment">//1、注册驱动</span><br> Class.forName(<span class="hljs-string">"com.mysql.cj.jdbc.Driver"</span>);<br><br> <span class="hljs-comment">//2、获取连接</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">url</span> <span class="hljs-operator">=</span> <span class="hljs-string">"jdbc:mysql://localhost:3306/clb"</span>;<br> <span class="hljs-type">String</span> <span class="hljs-variable">username</span> <span class="hljs-operator">=</span> <span class="hljs-string">"root"</span>;<br> <span class="hljs-type">String</span> <span class="hljs-variable">password</span> <span class="hljs-operator">=</span> <span class="hljs-string">"123456"</span>;<br> <span class="hljs-type">Connection</span> <span class="hljs-variable">conn</span> <span class="hljs-operator">=</span> <span class="hljs-literal">null</span>;<br> conn = DriverManager.getConnection(url, username, password);<br><br> <span class="hljs-comment">//接收用户名和密码</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">name</span> <span class="hljs-operator">=</span> <span class="hljs-string">"cxk"</span>;<br> <span class="hljs-type">String</span> <span class="hljs-variable">pwd</span> <span class="hljs-operator">=</span> <span class="hljs-string">"jntm"</span>;<br><br> <span class="hljs-type">String</span> <span class="hljs-variable">sql</span> <span class="hljs-operator">=</span> <span class="hljs-string">"select * from tb_user where name = '"</span> + name + <span class="hljs-string">"' and password = '"</span> + pwd + <span class="hljs-string">"'"</span>;<br><br> <span class="hljs-comment">//获取Statement对象</span><br> <span class="hljs-type">Statement</span> <span class="hljs-variable">stmt</span> <span class="hljs-operator">=</span> conn.createStatement();<br><br> <span class="hljs-comment">//执行sql语句</span><br> <span class="hljs-type">ResultSet</span> <span class="hljs-variable">rs</span> <span class="hljs-operator">=</span> stmt.executeQuery(sql);<br><br> <span class="hljs-comment">//判断是否登陆成功</span><br> System.out.println(rs.next() ? <span class="hljs-string">"登陆成功"</span> : <span class="hljs-string">"登陆失败"</span>);<br><br> <span class="hljs-comment">//7、释放资源</span><br> rs.close();<br> stmt.close();<br> conn.close();<br>}<br><br><span class="hljs-comment">/*</span><br><span class="hljs-comment"> sql注入问题</span><br><span class="hljs-comment">*/</span><br><br><span class="hljs-meta">@Test</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">UserLogin_Inject</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Exception {<br> <span class="hljs-comment">//1、注册驱动</span><br> Class.forName(<span class="hljs-string">"com.mysql.cj.jdbc.Driver"</span>);<br><br> <span class="hljs-comment">//2、获取连接</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">url</span> <span class="hljs-operator">=</span> <span class="hljs-string">"jdbc:mysql://localhost:3306/clb"</span>;<br> <span class="hljs-type">String</span> <span class="hljs-variable">username</span> <span class="hljs-operator">=</span> <span class="hljs-string">"root"</span>;<br> <span class="hljs-type">String</span> <span class="hljs-variable">password</span> <span class="hljs-operator">=</span> <span class="hljs-string">"123456"</span>;<br> <span class="hljs-type">Connection</span> <span class="hljs-variable">conn</span> <span class="hljs-operator">=</span> <span class="hljs-literal">null</span>;<br> conn = DriverManager.getConnection(url, username, password);<br><br> <span class="hljs-comment">//接收用户名和密码 sql注入->此处密码错误却还能成功登陆</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">name</span> <span class="hljs-operator">=</span> <span class="hljs-string">"ikunngmngmngm"</span>;<br> <span class="hljs-type">String</span> <span class="hljs-variable">pwd</span> <span class="hljs-operator">=</span> <span class="hljs-string">"'or '1' = '1"</span>;<br><br> <span class="hljs-type">String</span> <span class="hljs-variable">sql</span> <span class="hljs-operator">=</span> <span class="hljs-string">"select * from tb_user where name = '"</span> + name + <span class="hljs-string">"' and password = '"</span> + pwd + <span class="hljs-string">"'"</span>;<br><br> <span class="hljs-comment">//获取Statement对象</span><br> <span class="hljs-type">Statement</span> <span class="hljs-variable">stmt</span> <span class="hljs-operator">=</span> conn.createStatement();<br><br> <span class="hljs-comment">//执行sql语句</span><br> <span class="hljs-type">ResultSet</span> <span class="hljs-variable">rs</span> <span class="hljs-operator">=</span> stmt.executeQuery(sql);<br> System.out.println(<span class="hljs-string">"sql = "</span> + sql);<br><br> <span class="hljs-comment">//判断是否登陆成功</span><br> System.out.println(rs.next() ? <span class="hljs-string">"登陆成功"</span> : <span class="hljs-string">"登陆失败"</span>);<br><br> <span class="hljs-comment">//7、释放资源</span><br> rs.close();<br> stmt.close();<br> conn.close();<br>}<br></code></pre></td></tr></table></figure><h2 id="7、PrepareStatement"><a href="#7、PrepareStatement" class="headerlink" title="7、PrepareStatement"></a>7、PrepareStatement</h2><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> JDBC;<br><br><span class="hljs-keyword">import</span> org.junit.Test;<br><br><span class="hljs-keyword">import</span> java.sql.*;<br><br><span class="hljs-comment">/*</span><br><span class="hljs-comment"> PrepareStatement解决sql注入问题</span><br><span class="hljs-comment"> 将sql语句中的敏感符号加\ 进行转义</span><br><span class="hljs-comment">*/</span><br><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">PrepareStatementDemo6</span> {<br><br> <span class="hljs-meta">@Test</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">PrepareStatement</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Exception {<br> <span class="hljs-comment">//1、注册驱动</span><br> Class.forName(<span class="hljs-string">"com.mysql.cj.jdbc.Driver"</span>);<br><br> <span class="hljs-comment">//2、获取连接</span><br> <span class="hljs-comment">//useSeverPrepStmts=true 开启预编译功能 :性能更高</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">url</span> <span class="hljs-operator">=</span> <span class="hljs-string">"jdbc:mysql:/clb?useSSL=false&useSeverPrepStmts=true"</span>;<br> <span class="hljs-type">String</span> <span class="hljs-variable">username</span> <span class="hljs-operator">=</span> <span class="hljs-string">"root"</span>;<br> <span class="hljs-type">String</span> <span class="hljs-variable">password</span> <span class="hljs-operator">=</span> <span class="hljs-string">"123456"</span>;<br> <span class="hljs-type">Connection</span> <span class="hljs-variable">conn</span> <span class="hljs-operator">=</span> <span class="hljs-literal">null</span>;<br> conn = DriverManager.getConnection(url, username, password);<br><br> <span class="hljs-comment">//接收用户名和密码 sql注入->此处密码错误却还能成功登陆</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">name</span> <span class="hljs-operator">=</span> <span class="hljs-string">"ikunngmngmngm"</span>;<br> <span class="hljs-type">String</span> <span class="hljs-variable">pwd</span> <span class="hljs-operator">=</span> <span class="hljs-string">"'or '1' = '1"</span>;<br><br> <span class="hljs-comment">//定义sql语句</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">sql</span> <span class="hljs-operator">=</span> <span class="hljs-string">"select * from tb_user where name = ? and password = ?"</span>;<br><br> <span class="hljs-comment">//获取Statement对象</span><br> <span class="hljs-type">PreparedStatement</span> <span class="hljs-variable">pstmt</span> <span class="hljs-operator">=</span> conn.prepareStatement(sql);<br><br> <span class="hljs-comment">//设置?的值</span><br> pstmt.setString(<span class="hljs-number">1</span>, name);<br> pstmt.setString(<span class="hljs-number">2</span>, pwd);<br><br> <span class="hljs-comment">//执行sql语句</span><br> <span class="hljs-type">ResultSet</span> <span class="hljs-variable">rs</span> <span class="hljs-operator">=</span> pstmt.executeQuery();<br><br> <span class="hljs-comment">//判断是否登陆成功</span><br> System.out.println(rs.next() ? <span class="hljs-string">"登陆成功"</span> : <span class="hljs-string">"登陆失败"</span>);<br><br> <span class="hljs-comment">//7、释放资源</span><br> rs.close();<br> pstmt.close();<br> conn.close();<br> }<br>}<br></code></pre></td></tr></table></figure><h2 id="8、Druid数据库连接池"><a href="#8、Druid数据库连接池" class="headerlink" title="8、Druid数据库连接池"></a>8、Druid数据库连接池</h2><figure class="highlight java"><table><tr><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> Druid;<br><br><span class="hljs-comment">/*</span><br><span class="hljs-comment"> * Druid数据库连接池</span><br><span class="hljs-comment"> * */</span><br><br><span class="hljs-keyword">import</span> com.alibaba.druid.pool.DruidDataSourceFactory;<br><br><span class="hljs-keyword">import</span> javax.sql.DataSource;<br><span class="hljs-keyword">import</span> java.io.FileInputStream;<br><span class="hljs-keyword">import</span> java.sql.Connection;<br><span class="hljs-keyword">import</span> java.util.Properties;<br><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">DruidDemo</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> Exception {<br> <span class="hljs-comment">//1、导入jar包</span><br> <span class="hljs-comment">//2、定义配置文件</span><br> <span class="hljs-comment">//3、加载配置文件</span><br><br> <span class="hljs-comment">//4、获取连接池对象</span><br> <span class="hljs-type">Properties</span> <span class="hljs-variable">prop</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Properties</span>();<br> prop.load(<span class="hljs-keyword">new</span> <span class="hljs-title class_">FileInputStream</span>(<span class="hljs-string">"jdbcDemo/src/druid.properties"</span>));<br> <span class="hljs-type">DataSource</span> <span class="hljs-variable">dataSource</span> <span class="hljs-operator">=</span> DruidDataSourceFactory.createDataSource(prop);<br><br> <span class="hljs-comment">//5、获取数据库连接</span><br> <span class="hljs-type">Connection</span> <span class="hljs-variable">connection</span> <span class="hljs-operator">=</span> dataSource.getConnection();<br> System.out.println(connection);<br><br><br><span class="hljs-comment">// System.out.println(System.getProperty("user.dir"));</span><br> }<br>}<br><br><span class="hljs-comment">//配置文件(根据具体情况配置)</span><br>driverClassName=com.mysql.cj.jdbc.Driver<br>url=jdbc:mysql:/clb?useSSL=<span class="hljs-literal">false</span>&useServerPrepStmts=<span class="hljs-literal">true</span><br>username=root<br>password=<span class="hljs-number">123456</span><br># 初始化连接数量<br>initialSize=<span class="hljs-number">5</span><br># 最大连接数量<br>maxActive=<span class="hljs-number">10</span><br># 最大等待时间-><span class="hljs-number">3</span>秒<br>maxWait=<span class="hljs-number">30000</span><br></code></pre></td></tr></table></figure><h1 id="十、进阶"><a href="#十、进阶" class="headerlink" title="十、进阶"></a>十、进阶</h1><h2 id="1、索引"><a href="#1、索引" class="headerlink" title="1、索引"></a>1、索引</h2><figure class="highlight plaintext"><table><tr><td class="code"><pre><code class="hljs mysql">-- 索引<br><br>-- 查询表中所有索引<br>show index from tb_user;<br><br>-- 创建索引: create index 索引名 on 表名(字段名);<br>-- 给表中name字段创建常规索引<br>create index idx_user_name on tb_user (name);<br><br>-- 给表中phone字段创建唯一索引<br>create unique index idx_user_phone on tb_user (phone);<br><br>-- 给表中profession,age,status创建联合索引,字段的顺序是有讲究的<br>create index idx_user_pro_age_sta on tb_user (profession, age, status);<br><br>-- 给表中email字段创建常规索引<br>create index idx_user_email on tb_user (email);<br><br>-- 删除索引: drop index 索引名 on 表名;<br>drop index idx_user_email on tb_user;<br><br><br>-- SQL性能分析<br>-- 1、查看各种sql语句的执行频率<br>show global status like 'Com_______';<br><br>-- 2、慢查询日志,查询语句执行时间超过指定时间(默认10秒)就会记录到慢查询日志<br>show variables like 'slow_query_log';<br><br><br><br>-- 3、profile 详情<br>-- 查看是否支持profiling<br>select @@have_profiling;<br><br>-- 查看profiling是否开启 0-未开启 1-开启<br>select @@profiling;<br><br>-- 开启profiling<br>set profiling = 1;<br><br>select *<br>from tb_user;<br><br><br>-- 查看所有查询语句耗时情况<br>show profiles;<br><br>-- 查看指定查询语句的各个阶段的耗时<br>-- show profile for query 10;<br><br>-- 额外查看sql语句cpu消耗<br>-- show profile cpu for query 10;<br><br><br>-- 4、explain 执行计划<br><br>explain<br>select *<br>from tb_user<br>where id = 1;<br><br><br>-- 索引的使用<br>/**<br>1-最左前缀法则:<br> 最左边的索引必须存在,否则索引全部失效<br> 如果跳过了某个索引,那么从该索引开始的后面字段的索引失效<br>*/<br><br>show index from tb_user;<br><br>-- 没有跳过索引,索引全部生效<br>explain<br>select *<br>from tb_user<br>where profession = '软件工程'<br>and age = 31<br>and status = '0';<br><br>explain<br>select *<br>from tb_user<br>where profession = '软件工程'<br>and age = 31;<br><br>explain<br>select *<br>from tb_user<br>where profession = '软件工程';<br><br>-- 跳过最左边的索引,索引全部失效<br>explain<br>select *<br>from tb_user<br>where age = 31<br>and status = '0';<br><br>-- 跳过age索引,后面的status索引失效,前面的profession索引正常<br>explain<br>select *<br>from tb_user<br>where profession = '软件工程'<br>and status = '0';<br><br>-- 查询语句的条件的位置不影响索引是否生效<br>-- 没有跳过任何索引,索引全部生效<br>explain<br>select *<br>from tb_user<br>where status = '0'<br>and age = 31<br>and profession = '软件工程';<br><br>/**<br>2-出现范围查询(>,<)时,后面的索引失效,使用>=,<=不会<br>*/<br><br>explain<br>select *<br>from tb_user<br>where profession = '软件工程'<br>and age > 30<br>and status = '0';<br><br>/**<br>3-索引运算<br> ·不要在索引上进行运算,否则索引失效<br> ·字符串字段查询时不加单引号,索引失效<br>*/<br><br>explain<br>select *<br>from tb_user<br>where substring(phone, 10, 2) = '15';<br><br>explain<br>select *<br>from tb_user<br>where phone = 17799990004;<br>-- 没用单引号,索引失效<br><br>/**<br>4-模糊匹配<br> ·后面加%/_模糊匹配,索引正常<br> ·前面模糊,索引失效<br>*/<br><br>explain<br>select *<br>from tb_user<br>where profession like '软件%'; -- 索引有效<br><br><br>explain<br>select *<br>from tb_user<br>where profession like '%工程';<br><br>/**<br>5-or连接的条件<br> ·只有两个条件都有索引,索引才会生效,否则失效<br> ·解决方法:建立相关字段的索引<br>*/<br>explain<br>select *<br>from tb_user<br>where phone = '17799990004'<br>or age = 31; -- phone有索引,但是age没有索引,索引失效<br><br>explain<br>select *<br>from tb_user<br>where phone = '17799990004'<br>or id = 1;<br>-- phone 和 id都有索引,索引生效<br><br><br>/**<br>6-数据分布影响<br>·如果MySQL评估使用索引比全表更慢,则放弃索引,大部分数据都符合条件时会出现这种情况<br>*/<br><br>explain<br>select *<br>from tb_user<br>where phone > '0';<br>-- 所有电话号码都大于0,使用全表扫描<br><br>-- 使用了索引,具体使用索引还是不使用索引取决表中的数据,符合条件的数据多->不用,少->用<br>explain<br>select *<br>from tb_user<br>where profession is null;<br><br>-- 未使用索引<br>explain<br>select *<br>from tb_user<br>where profession is not null;<br><br><br><br>-- SQL提示:是优化数据库的一个重要手段,就是在sql语句中加入一些人为的提示达到优化操作的目的<br>-- 查询一个字段时,如果同时存在单列索引和联合索引,默认使用联合索引<br><br>-- profession有两个索引,默认使用的是联合索引<br>explain<br>select *<br>from tb_user<br>where profession = '软件工程';<br><br>-- use index 建议MySQL使用这个索引,至于用不用,取决于MySQL<br>explain<br>select *<br>from tb_user use index (idx_user_pro)<br>where profession = '软件工程';<br><br>-- ignore index 忽略索引,指定不会使用该索引<br>explain<br>select *<br>from tb_user ignore index (idx_user_pro)<br>where profession = '软件工程';<br><br>-- force index 强制使用,指定必须使用该索引,use可能MySQL并不接受,force强制MySQL使用<br><br>-- ---------------------------------------------------------------<br><br></code></pre></td></tr></table></figure><h2 id="2、SQL优化"><a href="#2、SQL优化" class="headerlink" title="2、SQL优化"></a>2、SQL优化</h2><figure class="highlight plaintext"><table><tr><td class="code"><pre><code class="hljs mysql">-- SQL优化<br><br>-- 1、插入优化:<br>-- 批量插入<br>-- 手动控制事务<br>-- 主键顺序插入<br>-- load指令<br><br>select @@local_infile;<br>set global local_infile = 1;<br><br>load data local infile 'D:/Edge下载/进阶篇/相关SQL脚本/load_user_100w_sort.sql' into table tb_user fields terminated by ',' lines terminated by '\n';<br><br>-- 2、主键优化:<br>-- 尽量降低主键的长度<br>-- 插入数据时,尽量使用顺序插入<br>-- 尽量不要使用UUID或者其他自然主键<br>-- 尽量不对主键进行修改<br><br>-- 3、order by 优化:<br>-- using filesort:效率低<br>-- using index:效率高<br>show index from tb_user; -- Collation:索引的顺序,A->升序,D->降序<br><br>explain select age,phone from tb_user order by age,phone;<br><br>-- 创建age和phone字段的一个联合索引,根据索引查询,结果不需要再进行排序<br>create index idx_user_age_phone on tb_user(age,phone);<br><br>-- 两个条件都是倒序,使用index倒序扫描 Backward index scan; Using index<br>explain select age,phone from tb_user order by age desc,phone desc ;<br><br>-- 先对phone排序,再根据age排序,不符合最左前缀法则,Using index; Using filesort<br>explain select age,phone from tb_user order by phone,age;<br><br>-- 先对age升序,再对phone降序排序<br>explain select age,phone from tb_user order by age,phone desc;<br><br>-- 优化:创建age升序phone降序的索引(默认是升序)<br>create index idx_user_age_phone_ad on tb_user(age,phone desc);<br><br>-- 尽量避免使用select*,否则需要所有字段都建立了联合索引才能走索引,否则就是filesort<br>-- 不可避免使用filesort时,可以适当增大排序文件缓冲区大小<br><br>-- limit 优化<br>select *<br>from tb_user<br>limit 900000,10;<br><br>-- 覆盖索引加子查询的形式<br>select t.*<br>from tb_user t,<br>(select id from tb_user order by id limit 900000,10) e<br>where t.id = e.id;<br><br><br>-- count()优化<br>-- 效率 count(*) ≈ count(1) > count(主键) > count(字段)<br>select count(*)from tb_user;<br>select count(id)from tb_user;<br>select count(1)from tb_user;<br>select count(username) from tb_user;<br><br>-- update 优化<br>-- update更新的数据最好有索引,否则执行update语句时行锁会升级为表锁,并发性能降低<br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category> 数据库 </category>
</categories>
<tags>
<tag> 数据库 </tag>
<tag> mysql </tag>
</tags>
</entry>
<entry>
<title>便携小空调 - 为你的夏日带去清凉!</title>
<link href="/air-conditioner/index.html"/>
<url>/air-conditioner/index.html</url>
<content type="html"><![CDATA[<blockquote><p>吹赛博空调,享电子人生</p></blockquote><style>.copyright-box a { border-bottom: none !important; padding: 0 !important;}</style><div id="air-conditioner-vue"></div><script defer data-pjax src='https://npm.elemecdn.com/[email protected]/index.3f125bc6.js'></script>]]></content>
</entry>
<entry>
<title>分类</title>
<link href="/categories/index.html"/>
<url>/categories/index.html</url>
<content type="html"><![CDATA[]]></content>
</entry>
<entry>
<title>charts</title>
<link href="/charts/index.html"/>
<url>/charts/index.html</url>
<content type="html"><![CDATA[]]></content>
</entry>
<entry>
<title>关于</title>
<link href="/about/index.html"/>
<url>/about/index.html</url>
<content type="html"><![CDATA[]]></content>
</entry>
<entry>
<title></title>
<link href="/css/custom.css"/>
<url>/css/custom.css</url>
<content type="html"><![CDATA[@font-face { font-family: ZhuZiAYuanJWD; src: url(https://npm.elemecdn.com/[email protected]/fonts/ZhuZiAWan.woff2); font-display: swap; font-weight: lighter;}@font-face { font-family: 'MiaoZiGuoZhiTi'; src: url(https://cdn.jsdelivr.net/gh/caolib/cdn@main/fonts/MiaoZi-GuoZhiTi.ttf); font-display: swap; font-weight: lighter;}:root { --font-family: 'Cascadia Code', 'MiaoZiGuoZhiTi', ZhuZiAYuanJWD, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;}div#menus { font-family: var(--font-family);}h1#site-title { font-family: var(--font-family); font-size: 3em !important;}a.article-title,a.blog-slider__title,a.categoryBar-list-link,h1.post-title { font-family: var(--font-family);}.iconfont { font-family: "iconfont" !important; font-size: 3em; /* 可以定义图标大小 */ font-style: normal; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;}/* 时间轴生肖icon */svg.icon { /* 这里定义svg.icon,避免和Butterfly自带的note标签冲突 */ width: 1em; height: 1em; /* width和height定义图标的默认宽度和高度*/ vertical-align: -0.15em; fill: currentColor; overflow: hidden;}.icon-zhongbiao::before { color: #f7c768;}/* bilibli番剧插件 */#article-container .bangumi-tab.bangumi-active { background: var(--anzhiyu-theme); color: var(--anzhiyu-ahoverbg); border-radius: 10px;}a.bangumi-tab:hover { text-decoration: none !important;}.bangumi-button:hover { background: var(--anzhiyu-theme) !important; border-radius: 10px !important; color: var(--anzhiyu-ahoverbg) !important;}a.bangumi-button.bangumi-nextpage:hover { text-decoration: none !important;}.bangumi-button { padding: 5px 10px !important;}a.bangumi-tab { padding: 5px 10px !important;}svg.icon.faa-tada { font-size: 1.1em;}.bangumi-info-item { border-right: 1px solid #f2b94b;}.bangumi-info-item span { color: #f2b94b;}.bangumi-info-item em { color: #f2b94b;}/* 解决artitalk的图标问题 */#uploadSource>svg { width: 1.19em; height: 1.5em;}/*top-img黑色透明玻璃效果移除,不建议加,除非你执着于完全一图流或者背景图对比色明显 */#page-header:not(.not-top-img):before { background-color: transparent !important;}/* 首页文章卡片 */#recent-posts>.recent-post-item { background: rgba(255, 255, 255, 0.9);}/* 首页侧栏卡片 */#aside-content .card-widget { background: rgba(255, 255, 255, 0.9);}/* 文章页面正文背景 */div#post { background: rgba(255, 255, 255, 0.9);}/* 分页页面 */div#page { background: rgba(255, 255, 255, 0.9);}/* 归档页面 */div#archive { background: rgba(255, 255, 255, 0.9);}/* 标签页面 */div#tag { background: rgba(255, 255, 255, 0.9);}/* 分类页面 */div#category { background: rgba(255, 255, 255, 0.9);}/*夜间模式伪类遮罩层透明*/[data-theme="dark"] #recent-posts>.recent-post-item { background: #121212;}[data-theme="dark"] .card-widget { background: #121212 !important;}[data-theme="dark"] div#post { background: #121212 !important;}[data-theme="dark"] div#tag { background: #121212 !important;}[data-theme="dark"] div#archive { background: #121212 !important;}[data-theme="dark"] div#page { background: #121212 !important;}[data-theme="dark"] div#category { background: #121212 !important;}[data-theme="dark"] div#category { background: transparent !important;}/* 页脚透明 */#footer { background: transparent !important;}/* 头图透明 */#page-header { background: transparent !important;}#rightside>div>button { border-radius: 5px;}/* 滚动条 */::-webkit-scrollbar { width: 10px; height: 10px;}::-webkit-scrollbar-thumb { background-color: #3b70fc; border-radius: 2em;}::-webkit-scrollbar-corner { background-color: transparent;}::-moz-selection { color: #fff; background-color: #3b70fc;}/* 音乐播放器 *//* .aplayer .aplayer-lrc { display: none !important;} */.aplayer.aplayer-fixed.aplayer-narrow .aplayer-body { left: -66px !important; transition: all 0.3s; /* 默认情况下缩进左侧66px,只留一点箭头部分 */}.aplayer.aplayer-fixed.aplayer-narrow .aplayer-body:hover { left: 0 !important; transition: all 0.3s; /* 鼠标悬停是左侧缩进归零,完全显示按钮 */}.aplayer.aplayer-fixed { z-index: 999999 !important;}/* 评论框 */.vwrap { box-shadow: 2px 2px 5px #bbb; background: rgba(255, 255, 255, 0.3); border-radius: 8px; padding: 30px; margin: 30px 0px 30px 0px;}/* 设置评论框 */.vcard { box-shadow: 2px 2px 5px #bbb; background: rgba(255, 255, 255, 0.3); border-radius: 8px; padding: 30px; margin: 30px 0px 0px 0px;}/* md网站下划线 */#article-container a:hover { text-decoration: none !important;}#article-container #hpp_talk p img { display: inline;}/* 404页面 */#error-wrap { position: absolute; top: 40%; right: 0; left: 0; margin: 0 auto; padding: 0 1rem; max-width: 1000px; transform: translate(0, -50%);}#error-wrap .error-content { display: flex; flex-direction: row; justify-content: center; align-items: center; margin: 0 1rem; height: 18rem; border-radius: 8px; background: var(--card-bg); box-shadow: var(--card-box-shadow); transition: all 0.3s;}#error-wrap .error-content .error-img { box-flex: 1; flex: 1; height: 100%; border-top-left-radius: 8px; border-bottom-left-radius: 8px; background-color: #3b70fc; background-position: center; background-size: cover;}#error-wrap .error-content .error-info { box-flex: 1; flex: 1; padding: 0.5rem; text-align: center; font-size: 14px; font-family: Titillium Web, "PingFang SC", "Hiragino Sans GB", "Microsoft JhengHei", "Microsoft YaHei", sans-serif;}#error-wrap .error-content .error-info .error_title { margin-top: -4rem; font-size: 9em;}#error-wrap .error-content .error-info .error_subtitle { margin-top: -3.5rem; word-break: break-word; font-size: 1.6em;}#error-wrap .error-content .error-info a { display: inline-block; margin-top: 0.5rem; padding: 0.3rem 1.5rem; background: var(--btn-bg); color: var(--btn-color);}#body-wrap.error .aside-list { display: flex; flex-direction: row; flex-wrap: nowrap; bottom: 0px; position: absolute; padding: 1rem; width: 100%; overflow: scroll;}#body-wrap.error .aside-list .aside-list-group { display: flex; flex-direction: row; flex-wrap: nowrap; max-width: 1200px; margin: 0 auto;}#body-wrap.error .aside-list .aside-list-item { padding: 0.5rem;}#body-wrap.error .aside-list .aside-list-item img { width: 100%; object-fit: cover; border-radius: 12px;}#body-wrap.error .aside-list .aside-list-item .thumbnail { overflow: hidden; width: 230px; height: 143px; background: var(--anzhiyu-card-bg); display: flex;}#body-wrap.error .aside-list .aside-list-item .content .title { -webkit-line-clamp: 2; overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; line-height: 1.5; justify-content: center; align-items: flex-end; align-content: center; padding-top: 0.5rem; color: white;}#body-wrap.error .aside-list .aside-list-item .content time { display: none;}/* 代码框主题 */#article-container figure.highlight { border-radius: 10px; background-color: transparent;}#article-container code { line-height: 1.5 !important;}]]></content>
</entry>
<entry>
<title>link</title>
<link href="/link/index.html"/>
<url>/link/index.html</url>
<content type="html"><![CDATA[]]></content>
</entry>
<entry>
<title></title>
<link href="/self/one-light-theme.css"/>
<url>/self/one-light-theme.css</url>
<content type="html"><![CDATA[@import url('https://cdn.jsdelivr.net/gh/caolib/cdn@main/css/blog.css');]]></content>
</entry>
<entry>
<title>即刻短文</title>
<link href="/essay/index.html"/>
<url>/essay/index.html</url>
<content type="html"><![CDATA[]]></content>
</entry>
<entry>
<title></title>
<link href="/self/onedark.css"/>
<url>/self/onedark.css</url>
<content type="html"><![CDATA[/*! Theme: OneDark Author: Lalit Magant (http://github.com/tilal6991) License: ~ MIT (or more permissive) [via base16-schemes-source] Maintainer: @highlightjs/core-team Version: 2021.09.0*//* WARNING: DO NOT EDIT THIS FILE DIRECTLY. This theme file was auto-generated from the Base16 scheme onedark by the Highlight.js Base16 template builder. - https://github.com/highlightjs/base16-highlightjs*//* 新添加的内容 ------------------------------------- --hl-color 代码框字体顔色 【必须】 (把下面.hljs的 color复制到这里来) --hl-bg 代码框背景色 【必须】 (把下面.hljs的 background复制到这里来) --hltools-bg: #321a0f 代码框顶部工具栏背景色 【可选】(如果你关掉了 copy、lang 和 shrink,可不用配置这个) --hltools-color: #fff 代码框顶部工具栏字体顔色 【可选】(如果你关掉了 copy、lang 和 shrink,可不用配置这个) --hlnumber-bg: #221a0f 代码框行数背景色 【可选】(如果已经关掉 line_number,可以不用配置这个) --hlnumber-color: #fff 代码框行数字体顔色 【可选】 (如果已经关掉 line_number,可以不用配置这个) --hlscrollbar-bg: #d3af86 代码框滚动条顔色 【可选】(默认为主题主顔色) --hlexpand-bg: #d3af86 代码框底部展开背景色 【可选】(如果已经关掉 highlight_height_limit,可以不用配置这个)*/:root { --hl-color: #abb2bf; --hl-bg: #282c34; --hltools-bg: #282c34; --hltools-color: #fff; --hlnumber-bg: #282c34; --hlnumber-color: #353b45; --hlexpand-bg: #282c34;}/*base00 #282c34 Default Backgroundbase01 #353b45 Lighter Background (Used for status bars, line number and folding marks)base02 #3e4451 Selection Backgroundbase03 #545862 Comments, Invisibles, Line Highlightingbase04 #565c64 Dark Foreground (Used for status bars)base05 #abb2bf Default Foreground, Caret, Delimiters, Operatorsbase06 #b6bdca Light Foreground (Not often used)base07 #c8ccd4 Light Background (Not often used)base08 #e06c75 Variables, XML Tags, Markup Link Text, Markup Lists, Diff Deletedbase09 #d19a66 Integers, Boolean, Constants, XML Attributes, Markup Link Urlbase0A #e5c07b Classes, Markup Bold, Search Text Backgroundbase0B #98c379 Strings, Inherited Class, Markup Code, Diff Insertedbase0C #56b6c2 Support, Regular Expressions, Escape Characters, Markup Quotesbase0D #61afef Functions, Methods, Attribute IDs, Headingsbase0E #c678dd Keywords, Storage, Selector, Markup Italic, Diff Changedbase0F #be5046 Deprecated, Opening/Closing Embedded Language Tags, e.g. <?php ?>*/pre code.hljs { display: block; overflow-x: auto; padding: 1em;}code.hljs { padding: 3px 5px;}.hljs { color: #abb2bf; /* background: #171717; */ background: #00282c34;}.hljs::selection,.hljs ::selection { background-color: #3e4451; color: #abb2bf;}/* purposely do not highlight these things */.hljs-formula,.hljs-params,.hljs-property {}/* base03 - #545862 - Comments, Invisibles, Line Highlighting */.hljs-comment { color: #545862;}/* base04 - #565c64 - Dark Foreground (Used for status bars) */.hljs-tag { color: #565c64;}/* base05 - #abb2bf - Default Foreground, Caret, Delimiters, Operators */.hljs-subst,.hljs-punctuation,.hljs-operator { color: #abb2bf;}.hljs-operator { opacity: 0.7;}/* base08 - Variables, XML Tags, Markup Link Text, Markup Lists, Diff Deleted */.hljs-bullet,.hljs-variable,.hljs-template-variable,.hljs-selector-tag,.hljs-name,.hljs-deletion { color: #e06c75;}/* base09 - Integers, Boolean, Constants, XML Attributes, Markup Link Url */.hljs-symbol,.hljs-number,.hljs-link,.hljs-attr,.hljs-variable.constant_,.hljs-literal { color: #d19a66;}/* base0A - Classes, Markup Bold, Search Text Background */.hljs-title,.hljs-class .hljs-title,.hljs-title.class_ { color: #e5c07b;}.hljs-strong { font-weight: bold; color: #e5c07b;}/* base0B - Strings, Inherited Class, Markup Code, Diff Inserted */.hljs-code,.hljs-addition,.hljs-title.class_.inherited__,.hljs-string { color: #98c379;}/* base0C - Support, Regular Expressions, Escape Characters, Markup Quotes */.hljs-built_in,.hljs-doctag, /* guessing */.hljs-quote,.hljs-keyword.hljs-atrule,.hljs-regexp { color: #56b6c2;}/* base0D - Functions, Methods, Attribute IDs, Headings */.hljs-function .hljs-title,.hljs-attribute,.ruby .hljs-property,.hljs-title.function_,.hljs-section { color: #61afef;}/* base0E - Keywords, Storage, Selector, Markup Italic, Diff Changed */.hljs-type,/* .hljs-selector-id, *//* .hljs-selector-class, *//* .hljs-selector-attr, *//* .hljs-selector-pseudo, */.hljs-template-tag,.diff .hljs-meta,.hljs-keyword { color: #c678dd;}.hljs-emphasis { color: #c678dd; font-style: italic;}/* base0F - Deprecated, Opening/Closing Embedded Language Tags, e.g. <?php ?> */.hljs-meta,/* prevent top level .keyword and .string scopes from leaking into meta by accident*/.hljs-meta .hljs-keyword,.hljs-meta .hljs-string { color: #be5046;}.hljs-meta .hljs-keyword,/* for v10 compatible themes */.hljs-meta-keyword { font-weight: bold;}]]></content>
</entry>
<entry>
<title>标签</title>
<link href="/tags/index.html"/>
<url>/tags/index.html</url>
<content type="html"><![CDATA[]]></content>
</entry>
</search>