diff --git a/docs/nice-article/BOKEYUANruhegaoxiaodixuexijishu.md b/docs/nice-article/BOKEYUANruhegaoxiaodixuexijishu.md deleted file mode 100644 index 79b4be4eae268b0d35d36a703bad847d4a283687..0000000000000000000000000000000000000000 --- a/docs/nice-article/BOKEYUANruhegaoxiaodixuexijishu.md +++ /dev/null @@ -1,127 +0,0 @@ ---- -title: 如何高效的学习技术 -shortTitle: 如何高效的学习技术 -description: 转载链接:https://www.cnblogs.com/xiaoyangjia/p/11535486.html -author: 编码砖家 -category: - - 优质文章 ---- - -* [学什么](#what) -* [基础与应用](#base) -* [广度与深度](#wide) -* [哲学](#philosophy) -* [英语](#english) -* [怎么学](#how) -* [知识体系](#system) -* [克服遗忘](#forget) -* [碎片时间](#fragment) -* [用起来](#use) -* [技术分享](#share) -* [造个轮子](#wheel) - -  我们相信努力学习一定会有收获,但是方法不当,既让人身心疲惫,也没有切实的回报。高中时代,我的同桌是个漂亮女同学。她的物理成绩很差,虽然她非常勤奋的学习,但成绩总是不理想。为了巩固纯洁的同学关系,我亲密无间地辅导她的物理,发现**她不知道题目考什么**。我们的教科书与试题都围绕着考试大纲展开,看到一道题,应该先想想它在考哪些定理和公式的运用。 -  不少朋友每天都阅读技术文章,但是第二天就忘干净了。工作中领导和同事都认可你的沟通和技术能力,但是跳槽面试却屡屡碰壁。面试官问技术方案,明明心里清楚,用嘴说出来却前言不搭后语。面试官再问底层算法,你说看过但是忘记了。他不在乎你看没看过,答不上就是零分。正如男女相亲,男方谈吐潇洒才能吸引姑娘。可是男方紧张了,平时挺能说,关键时候却支支吾吾,姑娘必然认为他不行。人生充满了许多考试,有形的和无形的,每次考试的机会只有一次。 -  工作五年十年后,别人成了架构师,自己还在基层打滚,原因是什么?职场上无法成功升迁的原因有很多,没有持续学习、学习效果不好、无法通过心仪公司的的面试,一定是很重要的原因。 -  **把自己当成一台计算机,既有输入,也要有输出,用输出倒逼输入**。 - -### 学什么 - -#### 基础与应用 - -  近些年诞生了许多新技术,比如最时髦的AI(目前还在智障阶段),数学基础是初中就接触过的概率统计。万丈高楼从地起,不要被新工具或者中间件迷住双眼,一味地追新求快。基础知识是所有技术的基石,在未来很长的时间都不会变化,应该花费足够的时间巩固基础。 -  以数据结构和算法为例,大家阅读一下Java的BitSet的源码,里面有大量的移位操作,移位运算掌握的好,看这份源码就没问题。Java同步工具类AQS用到了双向链表,链表知识不过关,肯定搞不懂它的原理。互联网大厂都喜欢考算法,为了通过面试也要精通算法。 -  以Java工程师应该掌握的知识为例,按重要程度排出六个梯度: - -* 第一梯度:计算机组成原理、数据结构和算法、网络通信原理、操作系统原理; -* 第二梯度:Java基础、JVM内存模型和GC算法、JVM性能调优、JDK工具、设计模式; -* 第三梯度:Spring系列、Mybatis、Dubbo等主流框架的运用和原理; -* 第四梯度:MySQL(含SQL编程)、Redis、RabbitMQ/RocketMQ/Kafka、ZooKeeper等数据库或者中间件的运用和原理; -* 第五梯度:CAP理论、BASE理论、Paxos和Raft算法等其他分布式理论; -* 第六梯度:容器化、大数据、AI、区块链等等前沿技术理论; - -有同学认为第五梯度应该在移到第一梯度。其实很多小公司的日活犹如古天乐一样平平无奇,离大型分布式架构还远得很。学习框架和中间件的时候,顺手掌握分布式理论,效果更好。 - -#### 广度与深度 - -  许多公司的招聘JD没有设定技术人员年龄门槛,但是会加上一句“具备与年龄相当的知识的广度与深度”。多广才算广,多深才算深?这是很主观的话题,这里不展开讨论。 -  如何变得更广更深呢?**突破收入上升的瓶颈,发掘自己真正的兴趣**。 -  大多数人只是公司的普通职员,收入上升的瓶颈就是升职加薪。许多IT公司会对技术人员有个评级,如果你的评级不高,那就依照晋级章程努力升级。如果你在一个小公司,收入一般,发展前景不明,准备大厂的面试就是最好的学习过程。在这些过程中,你必然学习更多知识,变得更广更深。 -  个人兴趣是前进的动力之一,许多知名开源项目都源于作者的兴趣。个人兴趣并不局限技术领域,可以是其他学科。我有个朋友喜欢玩山地自行车,还给一些做自行车话题的自媒体投稿。久而久之,居然能够写一手好文章了,我相信他也能写好技术文档。 - -#### 哲学 - -  哲学不是故作高深的学科,它的现实意义就是解决问题。年轻小伙是怎么泡妞的?三天两头花不断,大庭广众跪求爱。这类套路为什么总是能成功呢?礼物满足女人的物欲,当众求爱满足女人的虚荣心,投其所好。食堂大妈打菜的手越来越抖,辣子鸡丁变成辣子辣丁,为什么呢?食堂要控制成本,直接提价会惹众怒。 -  科学上的哲学,一般指研究事物发展的规律,归纳终极的解决方案。软件行业充满哲学味道的作品非常多,比如[《人月神话》](https://item.jd.com/12401749.html)。举个例子,当软件系统遇到性能问题,尝试下面两种哲学思想提升性能: - -* 空间换时间:比如引入缓存,消耗额外的存储提高响应速度。 -* 时间换空间:比如大文件的分片处理,分段处理后再汇总结果。 - -设计稳健高可用的系统,尝试从三个方面考虑问题: - -* 存储:数据会丢失吗,数据一致性怎么解决。 -* 计算:计算怎么扩容,应用允许任意增加节点吗。 -* 传输:网络中断或拥塞怎么办。 - -从无数的失败或者成功的经验中,总结出高度概括性的方案,让我们下一步做的更好。 - -#### 英语 - -  英语是极为重要的基础,学好英语与掌握编程语言一样重要。且不说外企对英语的要求,许多知名博客就是把英文翻译成中文,充当知识的搬运工。如果英语足够好,直接阅读一手英语资料,避免他人翻译存在的谬误。 - -### 怎么学 - -#### 知识体系 - -  体系化的知识比零散的更容易记忆和理解,这正如一部好的电视剧,剧情环环相扣才能吸引观众。建议大家使用思维导图罗列知识点,构建体系结构,如下图所示: - -![](https://img2018.cnblogs.com/blog/31085/201906/31085-20190607182832666-1215142380.png) - -#### 克服遗忘 - -  高中是我们知识的巅峰时刻,每周小考每月大考,教辅资料堆成山,地狱式的反复操练强化记忆。复习是对抗遗忘的唯一办法。大脑的遗忘是有规律的,先快后慢。一天后,学到的知识只剩下原来的25%,甚至更低。随着时间的推移,遗忘的速度减慢,遗忘的数量也就减少。 - -时间间隔 - -记忆量 - -刚看完 - -100% - -20分钟后 - -60% - -1小时后 - -40% - -1天后 - -30% - -2天后 - -27% - -每个人的遗忘程度都不一样,建议第二天复习前一天的内容,七天后复习这段时间的所有内容。 - -#### 碎片时间 - -  不少朋友利用碎片时间学习,比如在公交上看公众号的推送。其实我们都高估了自己的抗干扰能力,如果处在嘈杂的环境,注意力容易被打断,记忆留存度也很低。碎片时间适合学习简单孤立的知识点,比如链表的定义与实现。 -  学习复杂的知识,需要大段的连续时间。图书馆是个好地方,安静氛围好。手机放一边,不要理会QQ微信,最好阅读纸质书,泡上一整天。有些城市出现了付费自习室,提供格子间、茶水等等,也是非常好的选择。 - -### 用起来 - -### 技术分享 - -  从下面这张图我们可以看到,教授他人是知识留存率最高的方式。 -![](https://img2018.cnblogs.com/blog/31085/201905/31085-20190527232022474-1815067006.png) - -  准备PPT和演讲内容,给同事来一场技术分享。不光复习知识,还锻炼口才。曾经有个同事说话又快又急,口头禅也多,比如"对吧、是不是”,别人经常听不清,但是他本人不以为然。领导让他做了几次技术分享,听众的反应可想而知,他才彻底认清缺点。 -  坚持写技术博客,别在意你写的东西在网上已经重复千百遍。当自己动手的时候,才会意识到眼高手低。让文章读起来流畅清晰,需要呕心沥血的删改。写作是对大脑的长期考验,想不到肯定写不出,想不清楚肯定写不清楚。 - -### 造个轮子 - -我们经常说不要重复造轮子。为了开发效率,可以不造轮子,但是必须具备造轮子的能力。建议造一个简单的MQ,你能用到通信协议、设计模式、队列等许多知识。在造轮子的过程中,你会频繁的翻阅各种手册或者博客,这就是**用输出倒逼输入**。 \ No newline at end of file diff --git a/docs/nice-article/haibukuaiyongmybatisplusshoubashoujiaoni.md b/docs/nice-article/haibukuaiyongmybatisplusshoubashoujiaoni.md index 2398d65c1160bebc6845c827fed0183c730a14be..fdc7acbba19cc72c8c91607010252c1c92fe47d1 100644 --- a/docs/nice-article/haibukuaiyongmybatisplusshoubashoujiaoni.md +++ b/docs/nice-article/haibukuaiyongmybatisplusshoubashoujiaoni.md @@ -1,40 +1,42 @@ --- title: 还不会用mybatis-plus,手把手教你 shortTitle: 还不会用mybatis-plus,手把手教你 -description: 转载链接:https://juejin.cn/post/7054726274362638350 +description: 还不会用mybatis-plus,手把手教你 author: 我犟不过你 category: - 优质文章 --- ---- -theme: fancy ---- +本文主要介绍 mybatis-plus 这款插件,针对 springboot 用户。包括引入,配置,使用,以及扩展等常用的方面做一个汇总整理,尽量包含大家常用的场景内容。 -「这是我参与2022首次更文挑战的第2天,活动详情查看:[2022首次更文挑战](https://juejin.cn/post/7052884569032392740 \"https://juejin.cn/post/7052884569032392740\")」 +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/haibukuaiyongmybatisplusshoubashoujiaoni-ed20d595-a4a8-42af-bcbe-7afa578ad065.jpg) -本文主要介绍mybatis-plus这款插件,针对springboot用户。包括引入,配置,使用,以及扩展等常用的方面做一个汇总整理,尽量包含大家常用的场景内容。 +>原文链接:https://juejin.cn/post/7054726274362638350 作者:我犟不过你,整理:沉默王二 ->本文项目代码gitee地址: [https://gitee.com/wei_rong_xin/rob-necessities.git](https://gitee.com/wei_rong_xin/rob-necessities.git) +关于 mybatis-plus 是什么,不多做介绍了,看官方文档:[https://baomidou.com/](https://baomidou.com/),咱们直接代码撸起来。 -关于mybatis-plus是什么,不多做介绍了,看官方文档:[https://baomidou.com/](https://baomidou.com/),咱们直接代码撸起来。 +## 一、快速开始 -# 一、快速开始 +本文基于 springboot、maven、jdk1.8、mysql 开发,所以开始前我们需要准备好这套环境。我的环境使用了 nacos 作为注册中心,不了解或需要搭建的参考: -本文基于springboot、maven、jdk1.8、mysql开发,所以开始前我们需要准备好这套环境。我的环境使用了nacos作为注册中心,不了解或需要搭建的参考:[nacos2.0搭建与配置](https://juejin.cn/post/7053977860612030477) +> [https://juejin.cn/post/7053977860612030477](https://juejin.cn/post/7053977860612030477)) 新建如下数据库: -![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6ebfb19ffe4446e3b3c2a2367e334206~tplv-k3u1fbpfcp-watermark.image?) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/haibukuaiyongmybatisplusshoubashoujiaoni-9cf4609a-722e-47ab-a89c-64fe31a849fd.jpg) + +建议大家选择 utf8mb4 这种字符集,做过微信的同学应该会知道,微信用户名称的表情,是需要这种字符集才能存储的。 + +我就默认其他环境已经准备好了,咱们直接从 mybatis-plus 开始。 + +### 1.1 依赖准备 -建议大家选择utf8mb4这种字符集,做过微信的同学应该会知道,微信用户名称的表情,是需要这种字符集才能存储的。 +想要什么依赖版本的去 maven 仓库查看: -我就默认其他环境已经准备好了,咱们直接从mybatis-plus开始。 +>[https://mvnrepository.com/](https://mvnrepository.com/) -## 1.1 依赖准备 -想要什么依赖版本的去maven仓库查看:[maven仓库地址](https://mvnrepository.com/) +引入 mybatis-plus 依赖: -引入mybatis-plus依赖: ```xml com.baomidou @@ -42,7 +44,9 @@ theme: fancy 3.5.0 ``` -引入mysql依赖: + +引入 mysql 依赖: + ```xml mysql @@ -51,7 +55,8 @@ theme: fancy ``` -目前,多数项目会有多数据源的要求,或者是主从部署的要求,所以我们还需要引入mybatis-plus关于多数据源的依赖: +目前,多数项目会有多数据源的要求,或者是主从部署的要求,所以我们还需要引入 mybatis-plus 关于多数据源的依赖: + ```xml @@ -60,9 +65,11 @@ theme: fancy 3.5.0 ``` -## 1.2 配置准备 -springboot启动类。配置@MapperScan注解,用于扫描Mapper文件位置: +### 1.2 配置准备 + +springboot 启动类。配置@MapperScan 注解,用于扫描 Mapper 文件位置: + ```java import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; @@ -82,6 +89,7 @@ public class RobNecessitiesUserApplication { ``` 数据源配置,此处配置一主一从的环境,当前我只有一台,所以此处配置一样的: + ```yml spring: datasource: @@ -98,7 +106,9 @@ spring: username: root password: 123456 ``` + **补充**:这里面因为默认使用的是`HikariCP`数据源,目前也推荐使用这个,相比于`druid`有更高的性能,但是不能忽略下面的配置,否则服务会不断抛出异常,原因是数据库的连接时常和连接池的配置没有做好。 + ```yml spring: datasource: @@ -111,21 +121,23 @@ spring: min-idle: 4 connection-test-query: /**ping*/ ``` -## 1.3 启动服务 -下面直接启动服务: +### 1.3 启动服务 + +下面直接启动服务: -![image.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c07b4dae7ff841c599977a29b0aaa8a6~tplv-k3u1fbpfcp-watermark.image?) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/haibukuaiyongmybatisplusshoubashoujiaoni-e649b313-4ad3-459d-bb22-59725d57fe8c.jpg) 得到如上结果表示启动成功了。 -# 二、使用 +### 二、使用 + +前面我们成功的集成进来了 mybatis-plus,配合 springboot 使用不要太方便。下面我们看看如何使用它来操作我们的数据库。介绍一下常规的用法。 -前面我们成功的集成进来了mybatis-plus,配合springboot使用不要太方便。下面我们看看如何使用它来操作我们的数据库。介绍一下常规的用法。 +### 2.1 实体类注解 -## 2.1 实体类注解 +mybatis-plus 为使用者封装了很多的注解,方便我们使用,我们首先看下实体类中有哪些注解。有如下的实体类: -mybatis-plus为使用者封装了很多的注解,方便我们使用,我们首先看下实体类中有哪些注解。有如下的实体类: ```java @TableName(value = \"user\") public class UserDO { @@ -149,110 +161,115 @@ public class UserDO { } ``` -* @TableName 表名注解,用于标识实体类对应的表。 - - 其说明如下,关于这些书写,常规情况基本很少用到,不做多余解释了: - ```java - @Documented - @Retention(RetentionPolicy.RUNTIME) - @Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE}) - public @interface TableName { - - /** - * 实体对应的表名 - */ - String value() default \"\"; - - /** - * schema - * - * @since 3.1.1 - */ - String schema() default \"\"; - - /** - * 是否保持使用全局的 tablePrefix 的值 - *

只生效于 既设置了全局的 tablePrefix 也设置了上面 {@link #value()} 的值

- *
  • 如果是 false , 全局的 tablePrefix 不生效
  • - * - * @since 3.1.1 - */ - boolean keepGlobalPrefix() default false; - - /** - * 实体映射结果集, - * 只生效与 mp 自动注入的 method - */ - String resultMap() default \"\"; - - /** - * 是否自动构建 resultMap 并使用, - * 只生效与 mp 自动注入的 method, - * 如果设置 resultMap 则不会进行 resultMap 的自动构建并注入, - * 只适合个别字段 设置了 typeHandler 或 jdbcType 的情况 - * - * @since 3.1.2 - */ - boolean autoResultMap() default false; - - /** - * 需要排除的属性名 - * - * @since 3.3.1 - */ - String[] excludeProperty() default {}; - } - ``` - -* @TableId 主键注解 - - 看看其源码: - ```java - @Documented - @Retention(RetentionPolicy.RUNTIME) - @Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE}) - public @interface TableId { - - /** - * 字段值(驼峰命名方式,该值可无) - */ - String value() default \"\"; - - /** - * 主键ID - * {@link IdType} - */ - IdType type() default IdType.NONE; - } - ``` - 其中IdType很重要: - - - | 名称 | 描述 | - | --- | --- | - | AUTO | 数据库自增ID | - | NONE | 该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT) - | INPUT | 用户自己设置的ID | - | ASSIGN_ID | 当用户传入为空时,自动分配类型为Number或String的主键(雪花算法) | - | ASSIGN_UUID | 当用户传入为空时,自动分配类型为String的主键 | - -* @TableFiled 表字段标识 - - 下面看看其主要常用属性: - - | 名称 | 描述 | - | --- | --- | - | value | 数据库字段名 | - | condition | 字段 `where` 实体查询比较条件,通过`SqlCondition`设置
    如果未设置条件,则按照正常相等来查询
    若设置则按照以下规则:
    等于:EQUAL = \"%s=#{%s}\";
    不等于:NOT_EQUAL = \"%s<>#{%s}\";
    左右模糊:LIKE = \"%s LIKE CONCAT('%%',#{%s},'%%')\";
    oracle左右模糊ORACLE_LIKE = \"%s LIKE CONCAT(CONCAT('%%',#{%s}),'%%')\";
    左模糊:LIKE_LEFT = \"%s LIKE CONCAT('%%',#{%s})\";
    右模糊:LIKE_RIGHT = \"%s LIKE CONCAT(#{%s},'%%')\";| - | fill | 自动填充策略,通过`FieldFill`设置
    不处理:FieldFill.DEFAULT
    插入时填充字段:FieldFill.INSERT
    更新时填充字段:FieldFill.UPDATE
    插入或新增时填充字段:FieldFill.INSERT_UPDATE | +- @TableName 表名注解,用于标识实体类对应的表。 + + 其说明如下,关于这些书写,常规情况基本很少用到,不做多余解释了: + +```java +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE}) +public @interface TableName { + + /** + * 实体对应的表名 + */ + String value() default \"\"; + + /** + * schema + * + * @since 3.1.1 + */ + String schema() default \"\"; + + /** + * 是否保持使用全局的 tablePrefix 的值 + *

    只生效于 既设置了全局的 tablePrefix 也设置了上面 {@link #value()} 的值

    + *
  • 如果是 false , 全局的 tablePrefix 不生效
  • + * + * @since 3.1.1 + */ + boolean keepGlobalPrefix() default false; + + /** + * 实体映射结果集, + * 只生效与 mp 自动注入的 method + */ + String resultMap() default \"\"; + + /** + * 是否自动构建 resultMap 并使用, + * 只生效与 mp 自动注入的 method, + * 如果设置 resultMap 则不会进行 resultMap 的自动构建并注入, + * 只适合个别字段 设置了 typeHandler 或 jdbcType 的情况 + * + * @since 3.1.2 + */ + boolean autoResultMap() default false; + + /** + * 需要排除的属性名 + * + * @since 3.3.1 + */ + String[] excludeProperty() default {}; +} +``` + +- @TableId 主键注解 + + 看看其源码: + +```java +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE}) +public @interface TableId { + + /** + * 字段值(驼峰命名方式,该值可无) + */ + String value() default \"\"; + + /** + * 主键ID + * {@link IdType} + */ + IdType type() default IdType.NONE; +} +``` + + 其中 IdType 很重要: + + | 名称 | 描述 | + | ----------- | -------------------------------------------------------------------- | + | AUTO | 数据库自增 ID | + | NONE | 该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT) | + | INPUT | 用户自己设置的 ID | + | ASSIGN_ID | 当用户传入为空时,自动分配类型为 Number 或 String 的主键(雪花算法) | + | ASSIGN_UUID | 当用户传入为空时,自动分配类型为 String 的主键 | - 关于其他的属性,我不太推荐使用,用得越多,越容易蒙圈。可以通过wapper查询去设置。 +- @TableFiled 表字段标识 -## 2.2 CRUD -mybatis-plus封装好了一条接口供我们直接调用。关于内部的具体方法,在使用时候自己体会吧,此处不列举了。 + 下面看看其主要常用属性: + + | 名称 | 描述 | + | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | + | value | 数据库字段名 | + | condition | 字段  `where`  实体查询比较条件,通过`SqlCondition`设置
    如果未设置条件,则按照正常相等来查询
    若设置则按照以下规则:
    等于:`EQUAL = "%s=#{%s}"`;
    不等于:`NOT_EQUAL = "%s<>#{%s}";`
    左右模糊:`LIKE = "%s LIKE CONCAT('%%',#{%s},'%%')";`
    oracle 左右模糊 `ORACLE_LIKE = "%s LIKE CONCAT(CONCAT('%%',#{%s}),'%%')";`
    左模糊:`LIKE_LEFT = "%s LIKE CONCAT('%%',#{%s})";`
    右模糊:`LIKE_RIGHT = "%s LIKE CONCAT(#{%s},'%%')";` | + | fill | 自动填充策略,通过`FieldFill`设置
    不处理:FieldFill.DEFAULT
    插入时填充字段:FieldFill.INSERT
    更新时填充字段:FieldFill.UPDATE
    插入或新增时填充字段:FieldFill.INSERT_UPDATE | + + 关于其他的属性,我不太推荐使用,用得越多,越容易蒙圈。可以通过 wapper 查询去设置。 + +### 2.2 CRUD + +mybatis-plus 封装好了一条接口供我们直接调用。关于内部的具体方法,在使用时候自己体会吧,此处不列举了。 + +#### 2.2.1 Service 层 CRUD + +我们使用的时候,需要在自己定义的 service 接口当中继承`IService`接口: -### 2.2.1 Service层CRUD -我们使用的时候,需要在自己定义的service接口当中继承`IService`接口: ```java import com.baomidou.mybatisplus.extension.service.IService; import com.wjbgn.user.entity.UserDO; @@ -266,7 +283,9 @@ import com.wjbgn.user.entity.UserDO; public interface IUserService extends IService { } ``` -同时要在我们的接口实现impl当中继承`ServiceImpl`,实现自己的接口: + +同时要在我们的接口实现 impl 当中继承`ServiceImpl`,实现自己的接口: + ```java import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.wjbgn.user.entity.UserDO; @@ -285,8 +304,11 @@ public class UserServiceImpl extends ServiceImpl implements ``` 所以我们的 -### 2.2.2 Mapper层CRUD -mybatis-plus将常用的CRUD接口封装成了`BaseMapper`接口,我们只需要在自己的Mapper中继承它就可以了: + +#### 2.2.2 Mapper 层 CRUD + +mybatis-plus 将常用的 CRUD 接口封装成了`BaseMapper`接口,我们只需要在自己的 Mapper 中继承它就可以了: + ```java /** * @description: 用户mapper @@ -299,8 +321,10 @@ public interface UserMapper extends BaseMapper { } ``` -## 2.3 分页 +### 2.3 分页 + 使用分页话需要增加分页插件的配置: + ```java import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; @@ -322,14 +346,17 @@ public class MybatisPlusConfig { } ``` + 如上配置后,我们直接使用分页方法就行。 -## 2.4 逻辑删除配置 +### 2.4 逻辑删除配置 + 很多情况下我们的系统都需要逻辑删除,方便恢复查找误删除的数据。 -通过mybatis-plus可以通过全局配置的方式,而不需要再去手动处理。针对更新和查询操作有效,新增不做限制。 +通过 mybatis-plus 可以通过全局配置的方式,而不需要再去手动处理。针对更新和查询操作有效,新增不做限制。 通常以我的习惯逻辑删除字段通常定义为`is_delete`,在实体类当中就是`isDelete`。那么在配置文件中就可以有如下的配置: + ```yml mybatis-plus: global-config: @@ -340,150 +367,159 @@ mybatis-plus: ``` 或者通过注解`@TableLogic` + ```java @TableLogic private Integer isDelete; ``` -## 2.5 通用枚举配置 +### 2.5 通用枚举配置 + 相信后端的同学都经历过一个情况,比如`性别`这个字段,分别值和名称对应`1男`、`2女`,这个字段在数据库时是数值类型,而前端展示则是展示字符串的名称。有几种常见实现方案呢? -* 数据库查询sql通过case判断,返回名称,以前oracle经常这么做 -* 数据库返回的值,重新遍历赋值进去,这时候还需要判断这个值到底是男是女。 -* 前端写死,返回1就是男,返回2就是女。 -相信无论哪种方法都有其缺点,所以我们可以使用mybatis-plus提供的方式。我们在返回给前端时: -* 只需要在遍历时get这个枚举,直接赋值其名称,不需要再次判断。 -* 直接返回给前端,让前端去去枚举的name +- 数据库查询 sql 通过 case 判断,返回名称,以前 oracle 经常这么做 +- 数据库返回的值,重新遍历赋值进去,这时候还需要判断这个值到底是男是女。 +- 前端写死,返回 1 就是男,返回 2 就是女。 -这样大家都不需要写死这个值。 +相信无论哪种方法都有其缺点,所以我们可以使用 mybatis-plus 提供的方式。我们在返回给前端时: -下面看看如何实现这个功能: -* 兴义枚举,实现IEnum接口: +- 只需要在遍历时 get 这个枚举,直接赋值其名称,不需要再次判断。 +- 直接返回给前端,让前端去去枚举的 name - ```java - import com.baomidou.mybatisplus.annotation.IEnum; - import com.fasterxml.jackson.annotation.JsonFormat; +这样大家都不需要写死这个值。 - /** - * @description: 性别枚举 - * @author:weirx - * @date:2022/1/17 16:26 - * @version:3.0 - */ - @JsonFormat(shape = JsonFormat.Shape.OBJECT) - public enum SexEnum implements IEnum { - MAN(1, \"男\"), - WOMAN(2, \"女\"); - private Integer code; - private String name; - - SexEnum(Integer code, String name) { - this.code = code; - this.name = name; - } +下面看看如何实现这个功能: - @Override - public Integer getValue() { - return code; - } +- 兴义枚举,实现 IEnum 接口: - public String getName() { - return name; - } +```java +import com.baomidou.mybatisplus.annotation.IEnum; +import com.fasterxml.jackson.annotation.JsonFormat; +/** + * @description: 性别枚举 + * @author:weirx + * @date:2022/1/17 16:26 + * @version:3.0 + */ +@JsonFormat(shape = JsonFormat.Shape.OBJECT) +public enum SexEnum implements IEnum { + MAN(1, \"男\"), + WOMAN(2, \"女\"); + private Integer code; + private String name; + + SexEnum(Integer code, String name) { + this.code = code; + this.name = name; } - ``` - @JsonFormat注解为了解决枚举类返回前端只展示构造器名称的问题。 - -* 实体类性别字段 - ```java - @TableName(value = \"user\") - public class UserDO { - - /** - * 主键 - */ - @TableId(value = \"id\", type = IdType.AUTO) - private Long id; - - /** - * 昵称 - */ - @TableField(value = \"nickname\",condition = SqlCondition.EQUAL) - private String nickname; - - /** - * 性别 - */ - @TableField(value = \"sex\") - private SexEnum sex; - - /** - * 版本 - */ - @TableField(value = \"version\",update = \"%s+1\") - private Integer version; - - /** - * 时间字段,自动添加 - */ - @TableField(value = \"create_time\",fill = FieldFill.INSERT) - private LocalDateTime createTime; - } - ``` -* 配置文件扫描枚举 - ```yml - mybatis-plus: - # 支持统配符 * 或者 ; 分割 - typeEnumsPackage: com.wjbgn.*.enums - ``` - -* 定义配置文件 - ```java - @Bean - public MybatisPlusPropertiesCustomizer mybatisPlusPropertiesCustomizer() { - return properties -> { - GlobalConfig globalConfig = properties.getGlobalConfig(); - globalConfig.setBanner(false); - MybatisConfiguration configuration = new MybatisConfiguration(); - configuration.setDefaultEnumTypeHandler(MybatisEnumTypeHandler.class); - properties.setConfiguration(configuration); - }; + + @Override + public Integer getValue() { + return code; } - ``` -* 序列化枚举值为数据库值 - - 以下我是使用的fastjson: - - * 全局(添加在前面的配置文件中): - - ```java - @Bean - public MybatisPlusPropertiesCustomizer mybatisPlusPropertiesCustomizer() { - // 序列化枚举值为数据库存储值 - FastJsonConfig config = new FastJsonConfig(); - config.setSerializerFeatures(SerializerFeature.WriteEnumUsingToString); - - return properties -> { - GlobalConfig globalConfig = properties.getGlobalConfig(); - globalConfig.setBanner(false); - MybatisConfiguration configuration = new MybatisConfiguration(); - configuration.setDefaultEnumTypeHandler(MybatisEnumTypeHandler.class); - properties.setConfiguration(configuration); - }; + public String getName() { + return name; } - ``` - * 局部 - - ```java - @JSONField(serialzeFeatures= SerializerFeature.WriteEnumUsingToString) + +} +``` + + @JsonFormat 注解为了解决枚举类返回前端只展示构造器名称的问题。 + +- 实体类性别字段 + +```java +@TableName(value = \"user\") +public class UserDO { + + /** + * 主键 + */ + @TableId(value = \"id\", type = IdType.AUTO) + private Long id; + + /** + * 昵称 + */ + @TableField(value = \"nickname\",condition = SqlCondition.EQUAL) + private String nickname; + + /** + * 性别 + */ + @TableField(value = \"sex\") private SexEnum sex; - ``` - -## 2.6 自动填充 -还记得前面提到的实体类当中的注解`@TableFeild`吗?当中有个属性叫做fill,通过`FieldFill`设置属性,这个就是做自动填充用的。 + /** + * 版本 + */ + @TableField(value = \"version\",update = \"%s+1\") + private Integer version; + + /** + * 时间字段,自动添加 + */ + @TableField(value = \"create_time\",fill = FieldFill.INSERT) + private LocalDateTime createTime; +} +``` + +- 配置文件扫描枚举 +```yml +mybatis-plus: + # 支持统配符 * 或者 ; 分割 + typeEnumsPackage: com.wjbgn.*.enums +``` +- 定义配置文件 + +```java +@Bean +public MybatisPlusPropertiesCustomizer mybatisPlusPropertiesCustomizer() { + return properties -> { + GlobalConfig globalConfig = properties.getGlobalConfig(); + globalConfig.setBanner(false); + MybatisConfiguration configuration = new MybatisConfiguration(); + configuration.setDefaultEnumTypeHandler(MybatisEnumTypeHandler.class); + properties.setConfiguration(configuration); + }; +} +``` + +- 序列化枚举值为数据库值 + + 以下我是使用的 fastjson: + + - 全局(添加在前面的配置文件中): + +```java + @Bean + public MybatisPlusPropertiesCustomizer mybatisPlusPropertiesCustomizer() { + // 序列化枚举值为数据库存储值 + FastJsonConfig config = new FastJsonConfig(); + config.setSerializerFeatures(SerializerFeature.WriteEnumUsingToString); + + return properties -> { + GlobalConfig globalConfig = properties.getGlobalConfig(); + globalConfig.setBanner(false); + MybatisConfiguration configuration = new MybatisConfiguration(); + configuration.setDefaultEnumTypeHandler(MybatisEnumTypeHandler.class); + properties.setConfiguration(configuration); + }; + } +``` + + - 局部 + +```java + @JSONField(serialzeFeatures= SerializerFeature.WriteEnumUsingToString) + private SexEnum sex; +``` + +### 2.6 自动填充 + +还记得前面提到的实体类当中的注解`@TableFeild`吗?当中有个属性叫做 fill,通过`FieldFill`设置属性,这个就是做自动填充用的。 ```java public enum FieldFill { @@ -506,7 +542,7 @@ public enum FieldFill { } ``` -但是这个直接是不能使用的,需要通过实现mybatis-plus提供的接口,增加如下配置: +但是这个直接是不能使用的,需要通过实现 mybatis-plus 提供的接口,增加如下配置: ```java import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; @@ -538,7 +574,9 @@ public class MyMetaObjectHandler implements MetaObjectHandler { } } ``` + 字段如下: + ```java /** * 时间字段,自动添加 @@ -547,10 +585,12 @@ public class MyMetaObjectHandler implements MetaObjectHandler { private LocalDateTime createTime; ``` -## 2.7 多数据源 -前面提到过,配置文件当中配置了主从的方式,其实mybatis-plus还支持更多的方式: +### 2.7 多数据源 + +前面提到过,配置文件当中配置了主从的方式,其实 mybatis-plus 还支持更多的方式: + +- 多主多从 -* 多主多从 ```yaml spring: datasource: @@ -564,7 +604,9 @@ spring: slave_2: slave_3: ``` -* 多种数据库 + +- 多种数据库 + ```yaml spring: datasource: @@ -579,7 +621,8 @@ spring: sqlserver: ``` -* 混合配置 +- 混合配置 + ```yaml spring: datasource: @@ -596,7 +639,7 @@ spring: 上面的三种方式,除了混合配置,我觉得都有肯能出现的吧。 -* @DS注解 +- @DS 注解 可以注解在方法上或类上,**同时存在就近原则 【方法上注解】 优先于 【类上注解】**: @@ -624,10 +667,12 @@ public class UserServiceImpl extends ServiceImpl implements } ``` -# 三、测试 +## 三、测试 + 经过上面的配置,下面开始进入测试验证阶段。 建立一张表: + ```sql CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, @@ -730,13 +775,12 @@ public class UserController { 记过上面的接口验证,功能没有问题,集成成功。 +上文基本覆盖了 mybatis-plus 常用的特性,有同学说自动代码生成没说?[二哥的编程星球](https://mp.weixin.qq.com/s/3RVsFZ17F0JzoHCLKbQgGw)里《编程喵🐱实战项目笔记》里有专门的文章说明`mybatis-plus-generator` + `velocity`整合的文章,从后台代码,到前端代码全部一键生成。 ->上文基本覆盖了mybatis-plus常用的特性,有同学说自动代码生成没说?后面会有专门的章节去讲解` ->mybatis-plus-generator` + `velocity`整合的文章,从后台代码,到前端代码全部一键生成。 +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/haibukuaiyongmybatisplusshoubashoujiaoni-385444a5-ff07-4020-a62f-4fda0142e835.png) ->本文项目代码gitee地址: [https://gitee.com/wei_rong_xin/rob-necessities.git](https://gitee.com/wei_rong_xin/rob-necessities.git) +> 本文项目代码 gitee 地址: [https://gitee.com/wei_rong_xin/rob-necessities.git](https://gitee.com/wei_rong_xin/rob-necessities.git) -![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2196a876b9554168ae9586ebf6f50b6e~tplv-k3u1fbpfcp-watermark.image?) diff --git a/docs/nice-article/weixin/chengxyspnhxagzl.md b/docs/nice-article/weixin/chengxyspnhxagzl.md new file mode 100644 index 0000000000000000000000000000000000000000..3599ba5888d5c1be1cd3b6f7f47be5398b7f0547 --- /dev/null +++ b/docs/nice-article/weixin/chengxyspnhxagzl.md @@ -0,0 +1,263 @@ +--- +title: 程序员深漂 6 年,回西安工作了 +shortTitle: 离开苏州了! +author: 七哥呀 +category: + - 微信公众号 +--- + +>[二哥的编程星球](https://mp.weixin.qq.com/s/3RVsFZ17F0JzoHCLKbQgGw)已经有 **470 多名** 小伙伴加入了,如果你也需要一个良好的学习氛围,[戳链接](https://mp.weixin.qq.com/s/3RVsFZ17F0JzoHCLKbQgGw)加入我们吧!这是一个 Java 学习指南 + 编程实战的私密圈子,你可以向二哥提问、帮你制定学习计划、跟着二哥一起做项目、刷力扣,冲冲冲。 + +大家好,我是二哥呀。 + +熟悉二哥的读者朋友们应该都知道,我是在 2014 年离开苏州的,很不舍,舍不得相处的非常愉快的同事,舍不得那片一边出太阳一边下雨的天地,舍不得刚签了三年的新合同。 + +舍不得同事,因为一起相处了三年半,有感情了,大家都是真心相待,没有勾心斗角,尤其是我们一起进来的那一批,十几个人,吃睡玩工作几乎都在一起。 + +舍不得那片天地,因为治好了我多年难愈的青春痘😂,可能是气候比河南更湿润更温和一些,雨水多嘛。真的是一方水土一方人。 + +舍不得新合同,毕竟薪资待遇涨了不少,大学毕业那会签的是一份外包合同,这份是提前转正后的第一份新合同,由于实习的一年,加上正式工作的两年,表现得到了领导的认可,这份新合同里的薪资也是同一批进入公司里面的小伙伴当中最高的。 + +但有更重要的事情需要我离开,木得办法。 + +所以现在,一听到有朋友说离开北京了,离开上海了,离开杭州了,总还是能想起来我离开苏州时候的一幕幕。 + +记得离开的前一天,领导请我们几个相处得比较好的同事为我送行,在一家很温馨的餐馆,那是我第一次喝女儿红这种黄酒,温一温下肚,舒服香醇。那一夜,太多的故事。 + +好了好了,不煽情了。今天看到一个朋友从深圳离开回到西安的故事,不由得想起了从前,和我非常相似。对于我们互联网打工人来说,从一线城市回到二三线城市,是一种非常普遍的选择,其中的滋味恐怕只有我们最清楚。 + +下面👇🏻是朋友的分享和面经,希望能给公众号的读者朋友们(尤其是打算回二三线城市的)一点点启发和参考😄。 + +------- + +如果从我第一次踏上深圳这片土地算起,我已经深漂了整整 6 年了。今年留深过年期间,我们经过家庭会议的讨论,决定老家西安。 + +回来的这个想法其实之前一直也有过,但是呢,在深圳工作久了之后,做出这个决定还是挺需要勇气的。 + +这些年虽然努力,但是还是买不起深圳的房子,也没有魄力贷款几百万,还款 30 年。那样的压力实在也太大了! + +我还是喜欢安逸一些,人生嘛,挣钱也不是最重要的,每个月还款 2w 左右 ,还款 30 年,真受不了😖。再说,西安的 IT 互联网环境这几年也不错,找工作没啥问题。 + +最近两周也在陆陆续续的面试,下面是一些公司的面试题,有的我记录了,有的忘记了。 + +## 丰疆智能 + +西安研发中心,是深圳或者北京的面试官来面。 + +**一面** + +主要问了项目和一些基础技术,项目细节问的非常细。 + +**线上笔试** + +在线笔试,主要是考察设计和代码能力。 + +**二面** + +面试体验很差,不停的 PUA,让给自己打分、笔试打分,反正就是咄咄逼人的感觉,语气也很不耐烦,互相不合适吧~ + +1. 平时如何评估工作量的? +2. Java 中的集合有哪些?ArrayList 的扩容机制?无参构建方法初始为空数组,添加第一个元素时,创建长度为 10;当插入元素不够用时,扩容 1.5 倍,如果还不够用就扩容为需要的大小。 +3. HashMap 什么场景下是线程不安全的?会导致什么后果? +4. 线程池有哪些参数?并介绍参数在什么场景下使用? +5. 当核心线程数未满,继续提交任务,是复用已经空闲的线程还是继续创建? +6. Netty 中的核心组件有哪些?ByteBuf 有哪些方法?Netty 如何实现拆包?自定义拆包处理器是怎么实现的? +7. Spring 中有两个同样类型的 Bean,如何注入?如何实现全局统一注入? +8. AQS 了解吗?里面都有哪些属性?AQS 有几种模式?你对 AQS 还有哪些理解? +9. AQS 的公平锁和非公平锁原理是什么?读写锁的互斥规则? +10. Mybatis 如何实现结果绑定到 Java 对象的? +11. Spring 为什么要有三级缓存? +12. RocketMQ 怎么保证消息不丢,同步发送的 API 是啥?消费者如何保证消息不丢,除了 ACK 机制还有什么方法可以保证? +13. JVM 的内存模型?哪个区域不会发生 OOM? +14. JVM 都可以调优哪些参数,JDK8 默认的垃圾回收器是啥?都有哪些常见的垃圾回收算法?CMS 垃圾回收卡顿暂停了怎么处理? + +还有一些问题,没听明白要问啥,就询问能具体解释一下?他直接说他知道了,下一个问题。。直接就想挂电话了~~ + +## 西安移动研究院 + +> 俩小时 三个人轮流问 + +**一面** + +1. 介绍项目&细节&自己负责了哪些编码工作; +2. 包装类是深拷贝还是浅拷贝? +3. Redis 的 key 删除策略? +4. 写代码时常见的内存泄漏? +5. MySQL 索引的结构和搜索数据的过程? +6. 数据库的隔离级别?分别解决什么问题? +7. 常用的 Spring Boot Starter 有哪些? +8. Netty 为什么这么快?Epoll 的实现原理? +9. 线程 A 中创建线程 B,线程 B 如何捕获线程 A 的异常? +10. K8s 中 config 中环境变量修改,Docker 能否获取?配置文件修改 Docker 能否获取? +11. 接口限流如何实现,Guava 和 Redis 如何在项目中结合使用的? +12. HashMap 能否直接遍历删除?CurrentHashMap 能否遍历删除? +13. 有哪些让线程阻塞的方法?有哪些类使用了 LockSupport? +14. 泛型中 Extends 和 Super 关键字的区别?在 put 操作多时用哪种,为什么?在 get 操作多时用哪种,为什么? + +* 频繁往外读取内容的,适合用上界 Extends,因为读取的元素都可以用上界定义的类型来接收,父类可以指向子类,如果用 Super,只能用 Object 类型类接收了,因为不确定是哪个父类; +* 经常往里插入的,适合用下界 Super。相当于放松了插入元素的类型,既然元素是 Fruit 的基类,那往里存粒度比 Fruit 小的都可以,如果是 extends,则插入时会报错,因为集合中不确定到底存的是哪个子类,会报错; + +15. SpringBoot 自动装配原理? +16. 项目中如何做统一异常处理? +17. Spring 中配置文件的加载顺序? +18. 项目中如何做监控?JVM 进程如何监控?机器资源如何监控? + +**在线测评** + +完成了在线测评,提交了薪资流水。 + +> 过了一周多了,没有后续消息。 + +## 西安卓派 + +**一面** + +> 耗时 1 小时 + +1. Java 反射中 Super 和 Extend 有啥区别? +2. 线程池的运行原理?线程池大小如何设置?项目中如何使用线程池?如何防止线程池滥用?线程池中阻塞队列的意义是啥? +3. Spring 中的 ApplicationEvent 使用过? +4. ThreadLocal 的数据结构?平时怎么使用? +5. Spring 中的@Transactional 是如何使用 ThreadLocal? +6. ThreadLocal 的 key 为什么要设置为弱引用而不用一般引用?是基于啥考虑的? +7. 多线程中能否使用 ThreadLocal?如果要使用该怎么做? +8. Spring 事务的隔离级别了解?你们怎么用?你怎么理解隔离级别? +9. Spring 中的 AOP 切面,如果定义了多个增强执行顺序是怎么样的? +10. DDD 了解吗?是否知道其中的术语? +11. 设计模式了解哪些?如何使用的?具体的使用请求参数以及响应参数是怎么设计? +12. 设计原则中的单一职责是啥意思?给一个场景问是否应该设计成单一职责? +13. Tomcat 的线程池大小如何配置? +14. HTTP 协议的无状态你怎么理解?无状态有啥好处和坏处? +15. 介绍项目,特别仔细的聊项目细节; +16. 如何实现一个分布式会议室预定系统?保证同一时间段会议室只能被预定成功一次? +17. 是否愿意学英语? + +**二面** + +> 20 多分钟。 + +hr 面,聊聊做了什么项目,遇到了最复杂的问题是啥?如何解决的?介绍公司和项目,以及相关福利。 + +**三面** + +> 2 个半小时 + +上机写代码,两道算法题,一道系统设计题。 + +面试过程中面试官非常耐心,写的不好的题会给你引导优化方向,整个过程非常愉快。 + +**经验:**上机考察代码能力,如果题目没看懂一定要积极主动询问,然后就是如果不会解题,可以和面试官交流自己的思路,避免低头不语,写了半天也没有答案; + +**四面** + +三面之后紧接着是老板面,西安这边的研发负责人。 + +主要是聊项目、对于项目设计的理解,什么是好的代码?什么是好的设计? + +给出一些具体设计场景,让做选择,反正也没有唯一答案,说清楚自己的考虑点和原因即可,这种就是设计模式作为理论依据,然后结合平时自己的编码实践谈谈感受。 + +同时也聊到未来的一个发展方向,我正好最近对于后面走技术路线还是技术管理非常困惑,从聊的过程中这个问题也启发很大。 + +**个人感受** + +卓派是一家外企,内部技术氛围很棒,从整个面试过程中都能感受到面试官非常有包容性,虽然我对于敏捷这块很多专业名词不太了解,也很细心的说了中文对应的翻译,面试的同时也涨了很多见识。 + +公司的业务发展目前只能算一般,毕竟西安团队只有 30 人,今年预计扩充到 50 人,卡车司机接单也是属于一个细分小众领域,所以公司业务发展这块存在一定的风险。 + +福利待遇也很不错,全额最高公积金,配置新款 mbp,人体工程学椅,每月团建,我觉得这种公司因为年终奖不高的缘故,肯定不会卷,不像大公司年底绩效决定了一年是否白干,也就不存在拼命卷的情况。 + +## 西安腾讯云 + +**一面** + +> 耗时:50 分钟 + +1. 自我介绍&介绍项目; +2. 死磕项目实现过程和个人做了哪些事情,优化了什么,问的很细很细; +3. 关于线程池的一个场景题; +4. Future 实现阻塞等待获取结果的原理? +5. 谈谈什么是零拷贝? +6. 一共有几种 IO 模型?NIO 和多路复用的区别? +7. JDK 中的 NIO 使用的是什么模型? +8. 可重入锁和 Synchronized 的区别?Synchronized 的原理?可重入锁的实现原理? +9. 乐观锁和悲观锁, 让你来写你怎么实现? +10. ReentrantLock 的实现原理?Synchronized 是个悲观锁还是乐观锁? +11. Paxos 算法了解?并发提案时如何处理? +12. B+树聊一下?B+树是不是有序的,怎么样的顺序? +13. TCP 怎么实现拥塞控制?怎么实现重传(说了思路不过具体的实现回答的不准确)? +14. 看你有 JVM 调优经验,介绍下如何调优的; +15. 算法题:给一个无序的数组,选一个随机数,实现左边比它大,左右比大小,要求保持原有相对顺序不变;给出的实现方法时间复杂度是多少? + +**二面** + +> 25 分钟 + +1. 两个有序数组求交集; +2. 项目介绍; +3. Paxos 协议了解?工作流程是怎么样的? +4. 时隔太久忘记了。。 + +**三面** + +> 25 分钟 + +1. 两个有序数组求交集; +2. 二叉树如何持久化存储; +3. 自我介绍; +4. 前几次跳槽的原因? +5. 介绍一个有技术挑战的项目?追问项目细节,问到有点招架不住了; +6. 数据库分库分表的缺点是啥? +7. 分布式事务如何解决?TCC 了解? +8. 介绍下你们微服务项目的整体架构?服务间通信的原理?同步还是异步? +9. RocketMQ 如何保证消息的准确性和安全性? + +## 翼支付 + +**一面** + +1. 自我介绍&项目介绍; +2. 介绍项目中如何使用微服务架构,个人主要负责了哪些模块,用到了哪些技术; +3. HashMap 如何减少 hash 碰撞; +4. 什么场景需要重写 HashCode 方法; +5. 项目是否用到了线程池?怎么用的?常用参数有哪些? +6. 当线程池核心大小满了,队列满了,在提交任务会创建线程还是排队? +7. 谈谈对于数据库索引和约束的理解; +8. 数据库的时间类型(DataTime&TimeStamp)?有啥区别?分别占用多大的存储空间? +9. Kafka 如何实高可用、可扩展; +10. 定时任务框架了解哪些(Elastic-Job、Xxl-Job、Quartz),实现原理是啥,优缺点对比; +11. Apollo 实现热部署的原理; +12. Redis 热 Key 如何解决?只回答了打散这一种方案 +13. Redis 的过期删除策略是什么? +14. Spring 如何同时注入多态 Bean?提出了通过工厂获取多态实例的方案,面试官说可以集合注入; + +**二面** + +> 群面,一共 5 个候选者一起参加,每人一两个问题。 + +1. 问项目,以及设计如何优化? +2. 是否带过团队?如何帮助他们? + +----- + +面经就分享这些吧,有一些公司的面经没有及时记录(京东、趣联网络科技、中兴、民生科技、BYD 等),也记不清了,反正每一家都大差不差,对于八股文的复习大家可以看二哥的《**Java 程序员进阶之路**》,面渣逆袭部分基本上把所有面试题都覆盖到了。 + +然后就是项目方面,大家一定要梳理明白你简历上的项目,因为稍微正规一点的大厂面试官,面试时注意力都是很集中的,而且思维也能跟上,你讲的项目时的每一句话他都会思考后给出问题,跟你深入讨论。 + +而且建议你的项目尽量复杂一些,有难度、有挑战一些,这样才有深入讨论交流的话题,不要太老实,就写自己平时增删改查之类的业务功能。就算项目不是你一个人做的,但是你了解同事是怎么做的,遇到了哪些问题,如何思考解决的,这样对你个人来说也是经验和成长。 + +至于回老家后是啥情况,会不会后悔,后面会再跟大家分享,起码目前是满怀期待,回去后有家人、有朋友,应该还可以。也打算工作之余带着家人去周边逛一逛、吃一吃。我小时候爱钓鱼,回去感觉也可以安排上了,哈哈 😄~ + +没有什么使我停留——除了目的,纵然岸旁有玫瑰、有绿荫、有宁静的港湾,我是不系之舟。 + +**推荐阅读**: + +- [没必要为实习碰的头破血流](https://mp.weixin.qq.com/s/KxUMq2YmlIBMbAeRwUm8JA) +- [网站挣了 200 美刀后的感触](https://mp.weixin.qq.com/s/PxgZkuA_SnAgG7xfwlKLgw) +- [在 IDEA 里下五子棋不过分吧?](https://mp.weixin.qq.com/s/R13FkPipfEMKjqNaCL3UoA) +- [顺利入职了](https://mp.weixin.qq.com/s/oBLUSnHOmzoVpCP1sacNbA) + + +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-rumrabbitmqzypjdg-53717e59-63c9-44bd-99d3-dd2c26fe68bb.png) + + +>转载链接:[https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247524803&idx=1&sn=c5df3564f67d3c66d1c5357face5b001&chksm=cea12e08f9d6a71e6889f615918cef6232dea7045a7dee56530f6c86a3c979880dda266cbae2#rd](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247524803&idx=1&sn=c5df3564f67d3c66d1c5357face5b001&chksm=cea12e08f9d6a71e6889f615918cef6232dea7045a7dee56530f6c86a3c979880dda266cbae2#rd),出处:JavaGuide,整理:沉默王二 diff --git a/docs/nice-article/weixin/ruhcnddelpsp.md b/docs/nice-article/weixin/ruhcnddelpsp.md deleted file mode 100644 index e7e6842b75f451611361c62522f2f40291d92b57..0000000000000000000000000000000000000000 --- a/docs/nice-article/weixin/ruhcnddelpsp.md +++ /dev/null @@ -1,207 +0,0 @@ ---- -title: 如何才能达到阿里 P7 水平 ? -shortTitle: 如何才能达到阿里 P7 水平 ? -description: 如何才能达到阿里 P7 水平,技术、业务和软技能,三者缺一不可,本文告诉你如何卷! -author: 楼仔 -category: - - 微信公众号 -head: - - - meta - - name: description - content: 如何才能达到阿里 P7 水平,技术、业务和软技能,三者缺一不可,本文告诉你如何卷! ---- - -大家好,我是楼仔! - -前几天二哥找我,问我阿里 P7 是怎样的水平,在面试中如何才能拿到阿里 P7 的职级。 - -![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-ruhcnddelpsp-d2c078cb-7cdb-4e79-a7aa-184f228b56cd.jpg) - -楼仔是我的兄弟,那基本上我的所有要求他都会在第一时间响应,真的非常感谢,这篇内容价值百万,先分享给球友们看一看。 - - -![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-ruhcnddelpsp-9e26b3ec-b6bb-4564-8c09-91542ef87cd1.png) - - -下面我们就从**阿里的职级体系、P7 薪资、P7 能力栈、如何才能达到 P7 水平**,全面给大家剖析。 - -下面我们就从**阿里的职级体系、P7 薪资、P7 能力栈、如何才能达到 P7 水平**,全面给大家剖析。 - -阿里 P7 有多香? ----------- - -大家谈到阿里 P7,第一反应可能就是年薪百万,我们先看一下阿里的职级体系。 - -![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-ruhcnddelpsp-a9536271-b820-4b5b-8bb3-10d79e3056fa.jpg) - -P7 一般薪水在 70-100 万之间,超过 100 万的屈指可数,除非你是阿里 5 年以上的老 P7,薪资构成包括现金和股票: - -* 现金部分:大概的月薪应该是 35K-45K 左右,年薪在 50-60W 左右; -* 股票部分:平均每年能拿到 25 万左右,不过最近股票行情下跌,所以对这部分收入影响较大。 - -**所以一个新阿里 P7 的平均年收入是 80 W 左右**,如果是待了 5 年以上老 P7,妥妥年薪 100 万往上,这金钱的味道,真 TM 香! - -![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-ruhcnddelpsp-4c4f4d0e-50ab-4a2d-b1fd-e8a452b8f5e1.jpg) - -既然聊 P6 到 P7 这道坎,我们先看一下两者的要求。 - -阿里 P6/P7 要求 ------------ - -P6(高级工程师,工作 3~5 年,团队核心)其实是熟练的执行者,单兵作战强,能按照上级分拆的任务高效完成;P6+ 表示已经具备非常熟练的任务执行能力,**但还稍微缺乏对于复杂项目的阅读能力与大局观。** - -P7(技术专家,工作 5~10 年,团队核心 or TL)可能带人,也可能不带,取决于是业务导向还是技术导向,如果是技术导向,其专业方向在业界横向对比中要有较深见解,对困难的技术任务有较强的**攻坚能力**;如果是业务导向,则具备较好的团队正向影响力,能将中等规模的项目梳理与分解,**协调若干 P6 一起高效完成工作。** - -假如我们接到一个紧急项目,需要在 1 个月时间内上线一个电商网站。 - -P6 的要求就是能独立完成某个业务模块的设计与实现,比如登录注册这样的用户模块,按时完成,不出致命 bug 并避免性能、安全、运维风险。 - -P7 的要求就是可以把整个电商各业务模块合理拆分和研发人力分工排期,并且考虑到可能的**性能、安全、运维**风险,也考虑到哪些模块可能有**项目进度**问题,hold 得住**业务需求评审**,能担当整个项目的技术 Owner,**沟通与协调**能力达标,能和产品、运营、设计、测试、运维等部门以共同步调去**拿结果**。 - -简单来说,P7 就是在 P6 的基础上,除了**技术更过硬**,还需加上**团队管理能力、项目管理能力、业务意识能力。** - -下面附上 P6 和 P7 的要求(仅供参考): - -``` -P6(高级工程师,工作 -3~5 -年,团队核心): -- 公司:大厂工作经验/或者有一定知名度的公司; -- 学校:985/211 -加分项; -- 专业技能:语言基础、使用过的技术框架和中间件,架构设计,技术选型; -- 具备专业领域能力,深入的知识和技能,能够培训和教导初级员工; -- 对自己所从事的职业具备一定前瞻性了了解; -- 基于分析思考,有效执行,领导非跨部门项目; -- 掌握分析解决问题的能力,主动协调资源,独⽴处理复杂问题,拿到有效结果。 -P7(技术专家,工作 -5~10 年,团队核心 -or -TL): -- 公司:最好有大厂工作经验/或者有一定知名度的公司; -- 学校:985/211 -加分项; -- 专业技能:语言基础、使用过的技术框架和中间件,架构设计,技术选型,架构认知; -- 专业领域的丰富积累,具备专业影响力,是他人的咨询对象; -- 在专业领域中,对公司内外及业界的相关资源及水平⽐较了解; -- 基于分析思考,有效执行,领导跨部门项⽬,培养新员工; -- 良好的分析解决问题的能力,有创新想法,能改进业务流程; -- 团队协作中具备利他精神,能以积极正⾯的心态去影响团队; -- ⾏业外或公司内培养周期较⻓。 -``` - -如何能达到阿里 P7 ? ------------- - -对于 P7 这个级别来说,需要的是综合能力的体现。 - -**技术能力**(广度、深度、领域)+ **业务能力**(懂行) + **管理能力**(带项目、管团队)+ **沟通协调**(会扯皮、会来事)+ 工作态度(皮实、肯加班) - -![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-ruhcnddelpsp-b69bc6a2-4478-44d6-8d68-a9b1edf75d01.jpg) - -#### 1\. 技术 - -需要构建自己的**技术体系,有广度、有深度**,下能撸代码、改 Bug,上能**出方案、搞架构**。 - -1)负责一个领域的技术,能定义该领域的核心问题,有思考规划能力和迭代演进,提供具有行业竞争力的技术方案落地,具备一定的技术深度和广度。 - -什么是技术体系,举个例子,你说自己会 MySQL 数据库,但是除了日常写 SQL 进行 CURD 的业务开发外,你还知道 MySQL 的运行架构、主流存储引擎的区别、主从同步、主从复制、锁机制、分表分库、事务控制,围绕着技术名称的中心点,能像蜘蛛网那样扩散出去,**形成有条理的网状结构,就形成了自己的技术体系。** - -然后是技术广度 + 深度。 - -**广度**上要熟悉 MySQL、Redis、JVM 原理、并发编程、高并发、设计模式、Spring 全家桶系列、消息队列、分布式原理、配置中心、注册中心、微服务、RPC 等等,甚至还需要了解架构设计三原则(合适、简单和演化原则),DDD,CAP 理论,Docker,K8S 等等。 - -**深度**方面比如 JVM,各种垃圾收集算法得了解吧,遇到 outofmemory 、YGC、 FullGC,可以速度定位到问题吧。遇到某一个 SQL 忽然不走索引了,得能分析出具体什么原因吧。应用服务器 CPU Load 突然增长到原来的几十倍,你需要具备第一时间定位问题的能力。 - -2)跨 1 到 2 个技术栈开发和**方案设计能力**,具备良好的技术自驱力和业务思考力。了解整个系统,具备核心模块的编码能力,有很强的**分析和解决问题的能力**,是关键问题终结者。 - -这里其实主要体现对系统的方案设计上,具备多领域知识,我们解决问题的思路和办法会开阔很多,然后我们负责的系统所有的技术难题都可以快速解决,妥妥的扛把子! - -3)能针对复杂问题和多样环境设计良好的技术架构,对于一类业务/技术问题具备良好的扩展性和鲁棒性,具备很强的数据分析意识。 - -这里就体现较强的**系统、架构设计能力**,比如抽象共性、化繁为简、模块和组件化等。小的来说,缓存集群挂了系统不会挂,立马故障转移;大的来说,一个机房挂了,照样不影响正常业务运转。 - -4)业务发展背后思考技术策略和洞察技术需求,形成对应的技术产品支撑系统,**驱动业务**的持续发展。 - -据我所知,绝大多数的公司技术都是支撑公司业务发展,技术驱动公司业务是极少数。 - -#### 2\. 业务 - -所有开发者都要记住一点,开发者具有双重的行业属性:软件行业属性和软件所要**解决的问题所属行业的属性。** - -软件所要解决的问题,就是我们通常所说的业务,比如支付宝的业务是支付问题,京东商城的业务是电子商务问题。 - -精通业务,可以成为开发者的优势,这是开发者在问题域构建起来的优势,即所谓的**懂行**。 - -如果业务积累方面的优势和开发技能方面的优势能叠加起来,那这位开发者,就具有秒杀 85% 以上开发者的能力。 - -P7 需要具备的业务技能: - -* 具有协调多条业务的技术 PM 能力,有较强的技术整合能力,对负责业务上下游链路技术风险具备识别能力,**且能提供合理的满足各方诉求的总体解决方案,并且能拿到业务结果。** -* **有结构化思维和定义指标能力**,贴合业务大目标清晰地理解自己团队的定位和核心发力点,具有架构演进和规划落地的能力,并取得有数据佐证的业务结果。 -* 负责一小型业务线,能够拆解产品需求并进行平台化沉淀,同时具有**反向驱动业务发展**。 - -#### 3\. 软技能 - -![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-ruhcnddelpsp-e7ec543f-7cbb-46be-929e-b1b70f682c80.jpg) - -这个金字塔能力模型,适用于绝大多数软件开发工程师,该模型提出的作者说: - -> 大多数时候所谓的“技术之玻璃天花板”,其实只是缺乏软技能而已,这些技能可以学到,缺乏的知识可以通过决定改变的努力来弥补。 - -我们每位开发者都要牢记这句话,要清醒地意识到: - -* 不深入钻研,缺乏开发技术等硬技能,你很难在初期的职业生涯(0 ~ 5 年)中做出成绩占据重要位置; -* 没有沟通、协商、领导力、语境切换、创新、认知等软技能,你很难在职场上走出高度。 - -所以,在你掌握了一定的开发技术可以搞定一些任务后,就要并行地去培养自己的软技能,**只有软技能匹配你的硬技能,你才能更好的发展。** - -P7 需要具备的软技能: - -* **跨部门领导项目**:P6 需要在一定的指导下才能跨部门领导项目,但是 P7 需要完全具备跨部门领导项目的能力; -* **构建团队的能力**:具备从 0 到 1 搭建一个 10 人左右团队的能力,或已经有 3/4 个老员工将团队搭建到 10 人左右的能力。 -* **带领小型团队的能力**:可以根据业务的战略方向,给团队指明技术方向,同时可以带领团队完成既定的目标,同时可以根据每个下属的特点给予指导,让下属工作的同时还可以提升自己的能力; -* **有培养技术骨干的能力**:我见过非常多的技术经理或者 P7 的朋友,他们可以自己把一件很难的工作做好,但是对于培养新人,培养技术骨干(P6)缺乏经验,但是作为 P7,这项能力是必须具备的。 - -#### 4\. 小节 - -最后我们把阿里 P7 的胜任细则,再简单归纳一下: - -* 技术广度和深度,绝对的扛把子(占比偏大) -* 架构设计能力,方案(占比偏大) -* 跨部门的推动,通过多部门的联合推动(占比偏大) -* 主导业务的能力,领导力(占比最大) -* 业务效果,上层业务的挖掘,为业务带来了哪些撬动点(占比偏大) -* Coding(占比最低) - -这就是为什么 P6 到 P7 是一个槛,有没有 “世界虐我千百遍,我待世界如初恋” 的 gan juo ? - -![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-ruhcnddelpsp-54d526bd-e4cd-45b0-aa24-04e271650a9c.jpg) - -写在最后 ----- - -虽然阿里 P7 职称叫**专家**,但估计大家都不会把这个专家二字当真吧,在我心中起码阿里 P9 或者腾讯 T4 这种级别才能真正称得上行业中的专家。 - -所以阿里 P7,其实还不能称之为严格意义的专家,但即使不是真正的专家,那也是相当牛逼了! - -阿里 P7 是行业的硬通货,也是一般程序员工作 5~10 年后的职业发展分水岭,如果是在阿里工作过几年没有得过低绩效的 P7,起码是**技术靠谱、勤奋度高**的工程师了,如果**情商**也 ok 的话,去中小公司做技术经理、架构师都是绰绰有余。 - -最后回到二哥开头给我提的问题,面试如何才能拿到阿里 P7 的职级 ? - -这个不能只看面试,主要在于**平时的积累**,如果你只有 P6 的水平,无论掌握哪些面试技巧,都不可能达到 P7 的高度,但是可以通过一定的面试技巧,让自己的能力水平看起来要高一些。 - -比如你的能力值只有 70 分,你最多只能发挥到 80 分的水平,或者发挥失常,掉到 60 分的档位,但是基本不太可能跨越这个区间。 - -阿里 P7 很香,有能力又多金,谁都羡慕,但是我们只能一步一个脚印,干掉 95% 的同学,你也能成为那 5% 的精英。 - -* * * - -##### 推荐阅读: - -* [新来个技术总监,把 RabbitMQ 讲的那叫一个透彻,佩服!](https://mp.weixin.qq.com/s?__biz=Mzg3OTU5NzQ1Mw==&mid=2247489667&idx=1&sn=910f851309f822b9473d08e7f6d4ab6c&chksm=cf035a61f874d377dd08c9303655afe7a385e9dc2421fe61a377ac156d62a01bfb48bb12ec1b&token=331236693&lang=zh_CN&scene=21#wechat_redirect) -* [全网最强 JVM 来袭!](https://mp.weixin.qq.com/s?__biz=Mzg3OTU5NzQ1Mw==&mid=2247488282&idx=1&sn=a5247ff61e8379790b509e49b24199f2&chksm=cf0351f8f874d8ee21f2f6d7f8d4e3e96915cc028adf27caf6ff0be1747b11168e07e434b2e3&scene=21#wechat_redirect) -* [一文搞懂消息队列原理和选型](https://mp.weixin.qq.com/s?__biz=Mzg3OTU5NzQ1Mw==&mid=2247487669&idx=1&sn=7225d0d5613e33a1b1daf18083d04ca1&chksm=cf035257f874db411cb301382934d73bb1e4b8fc2f584335d65535e9c173f7e5e7e1d6b5286b&token=861276582&lang=zh_CN&scene=21#wechat_redirect) -* [肝了一个月的 DDD,一文带你掌握!](https://mp.weixin.qq.com/s?__biz=Mzg3OTU5NzQ1Mw==&mid=2247487929&idx=1&sn=e33ca9ee496ef4ecb159a719223259a7&chksm=cf03535bf874da4d72568c2e6235be9084bcd71227f33d588f1019e4a1a4f10e482d44e7bbb1&scene=21#wechat_redirect) - -![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-ruhcnddelpsp-54bb895a-36b8-4ac3-9830-faac6fefb374.jpg) \ No newline at end of file diff --git a/docs/nice-article/weixin/ruhfzcfxd.md b/docs/nice-article/weixin/ruhfzcfxd.md new file mode 100644 index 0000000000000000000000000000000000000000..8a6a54ced556fd97bca108b2460b1ca1a8886014 --- /dev/null +++ b/docs/nice-article/weixin/ruhfzcfxd.md @@ -0,0 +1,132 @@ +--- +title: 如何防止重复下单? +shortTitle: 如何防止重复下单? +description: 这一篇继续聊聊如何防止重复下单! +author: 老三 +category: + - 微信公众号 +head: + - - meta + - name: description + content: 这一篇继续聊聊如何防止重复下单! +--- + +大家好,我是老三,上一篇我们聊了 [如何防止订单重复支付](https://mp.weixin.qq.com/s?__biz=MzkwODE5ODM0Ng==&mid=2247491980&idx=1&sn=22c357da998773d57115d71c3f5708c3&scene=21#wechat_redirect) 这篇和大家聊聊如何防止重复下单,文章很短,大概只需要几分钟阅读。 + +> 关注公众号「**三分恶**」,回复「**666**」,领取七百多页独家原创的面试手册! +> +> ![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-ruhfzcfxd-2e3b7041-d317-4e79-9241-3dc098828a02.jpg) +> +> 面渣逆袭手册 + +# 用户下单流程 + +我们从用户浏览商品开始,看看用户下单的简要过程: + +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-ruhfzcfxd-85e704f6-c73f-4881-8d30-ac80057f7bc3.jpg) + +用户下单简要过程 + +* 浏览商品:用户查看商品详情 +* 加购/结算:用户可以选择直接购买商品,也可以先加入购物车,用户购买的这一步就是结算 +* 确认下单:结算完成,就进入了下单页面,`提交订单`,这一步就会生成一个订单,然后进入付款页面 + +我们可以看到,下单是发生在结算之后,下单之后,会生成唯一的订单号,接下来,客户端需要用这个订单号去完成支付。 + +那接下来先看看,为什么发生重复下单? + +# 为什么会重复下单 + +为什么会重复下单,对于订单服务而言,就是接到了多个下单的请求,原因可能有很多,最常见的是这两种: + +* 用户重复提交 +* 网络原因导致的超时重试 + +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-ruhfzcfxd-99b57ecc-6aa5-4100-ba56-63565c2dc037.jpg) + +重复下单原因 + +# 如何防止重复下单 + +防止用户提交,最常规的做法,就是客户端点击下单之后,在收到服务端响应之前,按钮置灰。 + +当然,防止重复下单,肯定不能只依靠客户端,可能会因为一些网络的抖动,导致仍然有重复的请求到达服务端,所以还是要在服务端做防重/幂等的处理。 + +PS:这里额外插入一点我对防重和幂等的理解:防重指的是防止重复提交,幂等指的是多次请求如一次,简单说,就是防重可以给对重复请求抛异常,幂等是对重复的请求响应第一次的结果,在我们讨论的这个场景里,幂等就是响应唯一的订单号。 + +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-ruhfzcfxd-5a6a4a1d-66df-4fe7-80e9-fc6e2bd27bfd.jpg) + +防重和幂等 + +防重第一步,需要识别请求是否重复,这一步,需要客户端配合实现。 + +为什么呢?大家想一下,下单的时候,服务端怎么去判断这个下单请求是否唯一呢?金额?商品?优惠券?……万一用户就是喜欢,又下了一个一模一样的单呢? + +所以,需要客户端在请求下单接口的时候,需要生成一个唯一的请求号:`requestId`,服务端拿这个请求号,判断是否重复请求。 + +那么,接下来,压力就给到服务端了,看看服务端怎么实现防重/幂等吧! + +## 利用数据库实现幂等 + +可以在订单表`t_order`里添加一个字段:`requestId`,添加唯一索引: + +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-ruhfzcfxd-0f6232ba-4e09-4ef8-a56a-076de147575f.jpg) + +唯一请求字段 + +这样一来,如果是重复的请求,在落库的时候就会报错,为了保证幂等性,我们可以catch住这个异常,根据requestId获取订单号,然后向客户端响应订单号。 + +大概的代码如下: + +``` +PlaceOrderResVO placeOrder(PlaceOrderReqVO reqVO) {   try {     //下单业务逻辑     ……     //生成订单号     String oid=generateOid();     ……     //订单落库     Order order = orderMapper.saveOrder(orderDO);      //响应订单     resVO.setOid(order.getOid());     return resVO;   } catch(UniqueKeyViolationException e) {     // 发生了重复异常     // 根据请求号获取订单     Order order = getOrderByRequestId(reqVO.getRequestId());     resVO.setOid(order.getOid());     return resVO;   } catch (Exception e) {   } } +``` + +当然,这里不太好的地方是,拿异常来做业务判断。 + +## 利用Redis防重 + +另外一个办法,就是下单请求的时候要加锁了,通常我们的服务都是集群部署,所以一般都是用Redis实现分布式锁。 + +大概的逻辑: + +* 就是以`requestId`为维度,进行加锁,如果获取锁失败,就抛一个自定义的重复下单异常。 +* 如果获取到锁,先check一下,是否已经下单,为了提高性能,下单完成后,也把下单的结果放在Redis缓存里。 + +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-ruhfzcfxd-0083bd11-12c2-4894-bb81-d0cd4cd82148.jpg) + +redis防重逻辑 + +大概的代码如下: + +``` +    public PlaceOrderResVO placeOrder(PlaceOrderReqVO reqVO) {         //加锁         RLock orderLock = redissonClient.getLock(RedisConstant.PLACE_ORDER_LOCK_KEY + reqVO.getRequestId());         //获取锁失败,抛出重复下单异常         if(orderLock.isExistes){           throw new OrderRepeatException();         }         // 加锁         orderLock.lock();         try {             //检查是否已经下单             RBucket orderCache = redissonClient.getBucket(RedisConstant.PLACE_ORDER_LOCK_KEY+reqVO.getRequestId());             if(orderCache.isExistes){                 return orderCache.get();             }             //下单业务逻辑             ……             //落库             //订单落库             Order order = orderMapper.saveOrder(orderDO);              ……             //缓存结果             orderCache.put(resVO);             return resVO;         }          } catch (Exception e) {             //……         } finally {             orderLock.unlock();         }         return resVO;     } +``` + +这里再说明一下: + +* 为什么获取不到锁的时候要抛异常呢? + +因为下单里面其实还有一些其它的业务流程,比如锁库存、清优惠券……而此时,获取到锁的请求的下单流程还没有结束,下单的结果还获取不到,没法完成响应,也就没办法做幂等。 + +客户端,也可以根据响应的状态码,进行特殊处理,比如这个异常先不提示,但是允许用户再次点击下单按钮,来提升用户的体验。 + +* * * + +好了,这一篇简单的小文章就这样结束了。 + +最近工作实在太忙了,基本上每天都是九点多、十点多下班,写新的文章,还有《面渣逆袭手册》的维护,都会努力抽时间去做,谢谢大家的理解和支持! + + + +* * * + +**参考:** + +\[1\]. 重复下单:https://blog.csdn.net/yangguosb/article/details/106095858) + +\[2\]. 用幂等防止重复订单:https://cloud.tencent.com/developer/article/1121727 + +\- END - + +>转载链接:[https://mp.weixin.qq.com/s/Dc_4taB6Boojdw_0mngroQ](https://mp.weixin.qq.com/s/Dc_4taB6Boojdw_0mngroQ),出处:三分恶,整理:沉默王二 diff --git a/docs/nice-article/weixin/ruhfzddcfzf.md b/docs/nice-article/weixin/ruhfzddcfzf.md index 0ec02b52bb45e26e089c743aeefb548d9b429bc5..ccdb102f6ffefaa9c8f8bc222a8411df4a30fbd5 100644 --- a/docs/nice-article/weixin/ruhfzddcfzf.md +++ b/docs/nice-article/weixin/ruhfzddcfzf.md @@ -1,29 +1,23 @@ --- title: 如何防止订单重复支付? shortTitle: 如何防止订单重复支付? -description: 如何防止订单重复支付,说说我的想法。 +description: 想必大家对在线支付都不陌生,今天和大家聊聊如何防止订单重复支付。 author: 老三 category: - 微信公众号 head: - - meta - name: description - content: 如何防止订单重复支付,说说我的想法。 + content: 想必大家对在线支付都不陌生,今天和大家聊聊如何防止订单重复支付。 --- -大家好,我是老三,想必大家对在线支付都不陌生,今天和大家聊聊如何防止订单重复支付。 +想必大家对在线支付都不陌生,今天和大家聊聊如何防止订单重复支付。 -> 关注公众号「**三分恶**」,回复「**666**」,领取七百多页独家原创面试手册! -> -> ![](https://mmbiz.qpic.cn/mmbiz_png/PMZOEonJxWfQR77RSX8KYHZVdw3JG3C4d9CLy6Pdh3lLwrvZjLYibEz4g8UWa8gH1cBCFcbha8ibAZvuncz7shQA/640?wx_fmt=png) -> -> 面渣逆袭手册 - -## 看看订单支付流程 +# 看看订单支付流程 我们来看看,电商订单支付的简要流程: -![](https://mmbiz.qpic.cn/mmbiz_png/PMZOEonJxWfQR77RSX8KYHZVdw3JG3C4kzbXmk7uBfSGPdPfibqBW5WqvRZyWiapibPibAVPKfH7kPoXsCjDDY43uA/640?wx_fmt=png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-ruhfzddcfzf-d214715e-611b-40bd-be68-e4646e91cdcc.jpg) 订单钱包支付流程 @@ -34,7 +28,7 @@ head: 3. **发起支付**:支付服务调用三方支付,通常这种钱包类的支付,在发起支付这一步,会响应一些支付的链接,客户端会对链接进行对应的处理。 4. **钱包支付**:用户进行支付,通常是通过对应的钱包进行的,大家可以回忆一下自己在购物中,支付的过程,不同的端,对钱包支付的处理是不太一样的: -![](https://mmbiz.qpic.cn/mmbiz_png/PMZOEonJxWfQR77RSX8KYHZVdw3JG3C4p98ZhlTib2N8huRibwslsxx4Xp9PVic0G4GnVkoYS4N5MjBL43aQQiav6w/640?wx_fmt=png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-ruhfzddcfzf-9b6deba3-d86b-45d3-b17f-b3f4aa615505.jpg) 京东PC端支付页 @@ -42,12 +36,12 @@ head: APP端的钱包支付,我们应该都非常熟悉,一般是拉起钱包,支付。 -![](https://mmbiz.qpic.cn/mmbiz_png/PMZOEonJxWfQR77RSX8KYHZVdw3JG3C4qqz9BaMCjTVqMpNFj8KFdR1jCww9onmG9R9eHwZUhxiaCwpLdH8u5zw/640?wx_fmt=png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-ruhfzddcfzf-9a63af83-39e5-463b-9a69-8e5238e4a49e.jpg) APP支付 * `WAP端`:手机的网页站,WAP端的支付一般是直接拉起对应的钱包,如果拉起钱包失败,就跳转界面 -![](https://mmbiz.qpic.cn/mmbiz_png/PMZOEonJxWfQR77RSX8KYHZVdw3JG3C431spDsyYyGQKeKPWrJruaPmVqdftL7X2upQRWUX2nAGf1rdUcbUcqQ/640?wx_fmt=png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-ruhfzddcfzf-ba7c4a9a-27cb-43d3-8624-30f3b451ba26.jpg) 京东支付WAP端 * `PC端`:PC端,通常是打开收银台,展示一个二维码,通过钱包扫码支付,下面是京东的微信支付扫码页 @@ -57,7 +51,7 @@ APP支付 我们再从支付流水的角度看一下支付状态的变化: -![](https://mmbiz.qpic.cn/mmbiz_png/PMZOEonJxWfQR77RSX8KYHZVdw3JG3C4n3EXJ5FORcmaQJtrzcSQFy4RR7N3VRCvfl3Dat0nbhVEILf8J0Pu5w/640?wx_fmt=png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-ruhfzddcfzf-a1c0390b-e070-4b77-a913-d28ce971b0af.jpg) 支付状态变化 @@ -66,17 +60,17 @@ APP支付 为什么要花这么多篇幅来讲支付的业务流程、交互过程呢?因为我认为,防止订单的重复支付,不止是技术上的问题,也是业务和产品上的问题。 -## 为什么订单会重复支付 +# 为什么订单会重复支付 -### 未防重导致的重复支付 +## 未防重导致的重复支付 我们可以看到PC端支付,是扫描二维码,这些二维码,就是对应相应的支付流水,假如用户重复点击支付,如果不做防重的的话,会生成两笔支付流水,也就是两个不同的二维码,要是用户分别扫了两个不同的支付码,那么毫无疑问,就会产生重复支付。 -### 掉单导致的重复支付 +## 掉单导致的重复支付 “我明明付款了,为什么我的订单还没支付呢?” -![](https://mmbiz.qpic.cn/mmbiz_png/PMZOEonJxWfQR77RSX8KYHZVdw3JG3C4nbHMZ6LyrMuT2LZQoVV2beLaQFKauPvL1WwFVjpIJ2QvEXhORlQfKQ/640?wx_fmt=png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-ruhfzddcfzf-85a3662c-ccc6-4bea-8fd6-78374ad64fd5.jpg) 黑我钱是吧 @@ -87,7 +81,7 @@ APP支付 用户一看,自己付了款,结果商城里订单还未付款,但是又特别想要,可能就会再下一单,这样就重复支付了。 -### 多渠道导致的重复支付 +## 多渠道导致的重复支付 我们国内支付的体验还是非常快捷的,大家可能没有感觉,如果了解过海外支付的可能了解,很多支付的渠道,消耗的时间非常长。 @@ -95,33 +89,33 @@ APP支付 这种情况大家可能很少遇到,我们可以用`美团`下一个单,先打开微信支付,不要支付啊,接着回到美团,打开支付宝,用支付宝支付完成后,用微信接着支付,大家猜猜,两笔支付是不是都能成功?答案是可以。 -![](https://mmbiz.qpic.cn/mmbiz_png/PMZOEonJxWfQR77RSX8KYHZVdw3JG3C4nibNPKJD5R1dAVsAaXxhpTd72iasRGib8HV7aibUpA6H8cxgLicLVema8aQ/640?wx_fmt=png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-ruhfzddcfzf-10584c11-08ca-4fb8-a6fb-a68155aeb21e.jpg) 美团多渠道支付 -## 如何防止订单重复支付 +# 如何防止订单重复支付 -### 加锁 +## 加锁 不管是`3.申请支付`、还是`5.支付回调`,都应该以订单维度加锁,防止并发下的重复操作。 加锁,毫无疑问,也是分布式锁,通常我们会选择Redis分布式锁。 -![](https://mmbiz.qpic.cn/mmbiz_png/PMZOEonJxWfQR77RSX8KYHZVdw3JG3C49NibUZaTJQu3Lvow1XGgIHdFTCLMNCys8xaN39Jq6H7ghObwhwTDosA/640?wx_fmt=png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-ruhfzddcfzf-31a82d77-598a-4b1e-bf73-0c2b5ac490f1.jpg) 加锁 -### 缓存结果 +## 缓存结果 申请支付成功,支付回调成功,都应该缓存结果。 再申请支付,收到成功回调的时候,都应该先去检查支付的状态。 -![](https://mmbiz.qpic.cn/mmbiz_png/PMZOEonJxWfQR77RSX8KYHZVdw3JG3C4l8pea9uNy7F14AUrcDjjovibAnzWVQibFCuribqAcsvDCI8bUobt7bKzA/640?wx_fmt=png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-ruhfzddcfzf-4dbb0659-7401-47be-b009-f266cca2d732.jpg) 在这里插入图片描述 -### 支付中流水取消 +## 支付中流水取消 假如说,用户重复支付了,再次申请支付的时候,如果已经申请支付成功了,那么这笔支付肯定是要拒绝的。 @@ -129,31 +123,31 @@ APP支付 所以,我们可以取消掉正在支付中的流水,再进行支付。 -![](https://mmbiz.qpic.cn/mmbiz_png/PMZOEonJxWfQR77RSX8KYHZVdw3JG3C4vpGliafKZrWasEYB9IYXqJF2YyH9Itic3qmiamHOlHS92H5AEnzgPoqAw/640?wx_fmt=png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-ruhfzddcfzf-ef5a814e-e1fa-4851-b976-325c2ffc6b68.jpg) 支付中流水取消 -### 已支付流水退款 +## 已支付流水退款 现在又有新的问题了,假如发起支付的时候,有流水正在支付中,如果第三方支付平台不支持取消支付,或者用户新的支付是通过不同的渠道,我们希望尽可能提高用户的支付成功率,怎么办呢? 我们可以在发起支付的时候,订单还在支付中的情况下,允许用户发起多笔支付,在支付回调的时候,检查用户是否已经有成功流水,对后来的流水进行退款处理。 -![](https://mmbiz.qpic.cn/mmbiz_png/PMZOEonJxWfQR77RSX8KYHZVdw3JG3C4IdntmfsrhMkubl7icUtlKFfia9Iiaj700ZLKK5kuC8a3JZlxtTePBfZAQ/640?wx_fmt=png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-ruhfzddcfzf-41a23c59-9701-4f6e-808f-64b0c856327c.jpg) 支付回调 当然,退款是个很危险的操作,毕竟钱退了,可就很难追回来,一定要做好风险的控制。 -### 主动轮询&重试防止掉单 +## 主动轮询&重试防止掉单 -#### 主动轮询防止外部掉单 +### 主动轮询防止外部掉单 如果因为故障没有收到回调,或者没有及时收到回调,就可能会发生所谓的外部掉单。 防止外部掉单的关键,就在于,不能傻傻地只等三方的回调通知,而要主动去查询,用户发起支付的3s之后,就可以发起轮询了,直到拿到支付流水的最终状态,主动轮询,一般可以这么实现: -![](https://mmbiz.qpic.cn/mmbiz_png/PMZOEonJxWfQR77RSX8KYHZVdw3JG3C4mKoSobbGPcjI2icic5DDMuUEd40oraqcxiahibwPZcTQCvsJybcGFY3sMw/640?wx_fmt=png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-ruhfzddcfzf-4fe79ee5-b65f-4992-bec0-3f521aa7362d.jpg) 轮询 @@ -170,7 +164,7 @@ APP支付 另外一种方式就是使用延时消息,用户发起支付之后,发送一个延时消息,消费到延时消息之后,查询流水支付状态,没有拿到最终状态,就再发一个延时消息。延时消息的好处是对数据库的压力没有那么大,轮询的梯度也可以进行控制,缺点是实现起来复杂一些,而且要维护消息队列。 -#### 同步+异步防止内部掉单 +### 同步+异步防止内部掉单 支付服务在收到异步通知回调、或者主动轮询到流水的最终状态后,要通知订单服务支付流水的变化,订单服务同步更新订单的状态,这个过程要尽可能保证通知成功,可以采用同步+异步的方式。 @@ -184,11 +178,11 @@ APP支付 * 拉:很简单,就是客户端在用户跳回订单状态页的时候,轮询一会,如果用户完成支付,通常很短时间就能获取到状态的变更,当然这种方式对客户端的性能会有一些影响,而且很出现状态同步“漏网之鱼”的情况。 * 推:推的实现有些麻烦,Web通常是用Websocket,对APP端的推送,一般采用第三方的推送平台。 -### 客户端支付尽可能不外跳 +## 客户端支付尽可能不外跳 不管从产品的角度,还是技术的角度,客户端发起支付这一步,其实应该尽可能地不要外跳,PC端使用支付服务生成的支付码,而不是跳转;移动端网页、APP在应用内展示支付页,当然这个是由第三方支付平台决定的。 -![](https://mmbiz.qpic.cn/mmbiz_png/PMZOEonJxWfQR77RSX8KYHZVdw3JG3C4Qquj9JOlQ8mUmicktGt4KqhWJnMicvBiaVicDicibAsd9X3DDFiapHzvUIFcA/640?wx_fmt=png) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-ruhfzddcfzf-0a29dff7-75eb-43b9-a2e4-fd04e9b299e6.jpg) 在UC内内嵌支付宝 @@ -196,41 +190,25 @@ APP支付 * * * -好了,关于如何防止重复支付,就讲到这里。对于支付,老三也只是初窥门径,希望各位大佬不吝指教。 - - - -* * * - -**参考:** - -\[1\]. 服务端如何防止重复支付 - - - -* * * +**微信8.0将好友放开到了一万,小伙伴可以加我大号了,先到先得,再满就真没了** -**⭐面渣逆袭系列:** +**扫描下方二维码即可加我微信啦,`2022,抱团取暖,一起牛逼。`** -* [面渣逆袭:Java基础五十三问](https://mp.weixin.qq.com/s?__biz=MzkwODE5ODM0Ng==&mid=2247489569&idx=1&sn=6d0ff8b376e35d68f272248b3e3927b2&scene=21#wechat_redirect) -* [面渣逆袭:Java集合连环三十问](https://mp.weixin.qq.com/s?__biz=MzkwODE5ODM0Ng==&mid=2247488788&idx=1&sn=01875e3e45515c2d57593cb7a01d0b6b&scene=21#wechat_redirect) -* [面渣逆袭:JVM经典五十问,这下面试稳了!](https://mp.weixin.qq.com/s?__biz=MzkwODE5ODM0Ng==&mid=2247489004&idx=1&sn=8cba55cb769e271f031ce866de2be249&scene=21#wechat_redirect) -* [面渣逆袭:Java并发六十问](https://mp.weixin.qq.com/s?__biz=MzkwODE5ODM0Ng==&mid=2247489245&idx=1&sn=bc52281ebc85372e19513d663beb2d2d&chksm=c0ccfe78f7bb776e2c6396fe26aca84d0cd96f407e6fe0bf6eb068aed638ba9491bce8fc5b4c&scene=21&cur_album_id=2041709347461709827#wechat_redirect) -* [面渣逆袭:Spring三十五问,四万字+五十图详解!](https://mp.weixin.qq.com/s?__biz=MzkwODE5ODM0Ng==&mid=2247491359&idx=1&sn=7a0c3f5fc04b2e45a3cfba638941f663&chksm=c0ccf7baf7bb7eaccba3e29d2a768710af8a16c87574c3a8f4b24c8dee814b296ff56e3bd6e3&scene=21&cur_album_id=2041709347461709827#wechat_redirect) -* [面渣逆袭:二十二图、八千字、二十问,彻底搞定MyBatis!](https://mp.weixin.qq.com/s?__biz=MzkwODE5ODM0Ng==&mid=2247490612&idx=1&sn=e37c69a7875ce54a28c9918ea6a24a73&chksm=c0ccf491f7bb7d87bcc6f49a04a3e3a175f382cfdba3305151861988caa86b0feff1e5578e54&scene=21&cur_album_id=2041709347461709827#wechat_redirect) -* [面渣逆袭:计算机网络六十二问,三万字图文详解!速收藏!](https://mp.weixin.qq.com/s?__biz=MzkwODE5ODM0Ng==&mid=2247489885&idx=1&sn=1a4cb15c40c07e18f180df6fda8f472f&chksm=c0ccf1f8f7bb78eef66f067d63e2abdf1092847eba6372b6e4c15185a6d6ce7d407278c83e6f&scene=21&cur_album_id=2041709347461709827#wechat_redirect) -* [面试字节,被操作系统问挂了](https://mp.weixin.qq.com/s?__biz=MzkwODE5ODM0Ng==&mid=2247488406&idx=1&sn=93e2435b319c42497a4efa966ddc9237&scene=21#wechat_redirect) -* [面渣逆袭:RocketMQ二十三问](https://mp.weixin.qq.com/s?__biz=MzkwODE5ODM0Ng==&mid=2247490996&idx=1&sn=ba9558574d71979aa689a710c28c7e0e&scene=21#wechat_redirect) -* [面渣逆袭:Redis连环五十二问!三万字+八十图详解!](https://mp.weixin.qq.com/s?__biz=MzkwODE5ODM0Ng==&mid=2247491521&idx=1&sn=dcefc00c23d0821990f62dc3749141bb&chksm=c0ccf764f7bb7e72defbc937a72e9d2ec766b8de1574def67a4650c80c8329694127b01405d4&scene=21&cur_album_id=2041709347461709827#wechat_redirect) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-ruhfzddcfzf-30d252e2-ef82-4f4a-bbe8-45ca12c12ef4.jpg) - +## 推荐阅读 -添加个人微信「**ThirdFighter**」,技术交流,加群讨论! +* [程序员买房前后对比,太TM真实了!](https://mp.weixin.qq.com/s?__biz=MzU1Nzg4NjgyMw==&mid=2247501513&idx=1&sn=7c6b6152c836922a2a5edec461634a99&scene=21#wechat_redirect) +* [在IDEA里下五子棋不过分吧?](https://mp.weixin.qq.com/s?__biz=MzU1Nzg4NjgyMw==&mid=2247501430&idx=1&sn=9a9ee5545ab2221463ffeff640e7d9a2&scene=21#wechat_redirect) +* [阿里 P6 和 P7 的主要区别是什么?](https://mp.weixin.qq.com/s?__biz=MzU1Nzg4NjgyMw==&mid=2247501423&idx=1&sn=67066ada93102045be7291a1bf7b5ee2&scene=21#wechat_redirect) +* [仅需一个依赖给Swagger换上新皮肤,既简单又炫酷!](https://mp.weixin.qq.com/s?__biz=MzU1Nzg4NjgyMw==&mid=2247501400&idx=1&sn=ddafc938b05816cc6871d912f94e913d&scene=21#wechat_redirect) +* [HTTP 3.0 彻底放弃TCP,TCP到底做错了什么?](https://mp.weixin.qq.com/s?__biz=MzU1Nzg4NjgyMw==&mid=2247501374&idx=1&sn=98c26361a97d7bf894a6de089318943b&scene=21#wechat_redirect) +* [Mall电商实战项目全面升级!支持最新版SpringBoot,干掉循环依赖...](https://mp.weixin.qq.com/s?__biz=MzU1Nzg4NjgyMw==&mid=2247500820&idx=1&sn=9895bd4c39b90d45eb2a10efedb236ac&scene=21#wechat_redirect) +* [重磅更新!Mall实战教程全面升级,瞬间高大上了!](https://mp.weixin.qq.com/s?__biz=MzU1Nzg4NjgyMw==&mid=2247499376&idx=1&sn=3ed28795cdd35fbaa3506e74a56703b0&scene=21#wechat_redirect) +* [40K+Star!Mall电商实战项目开源回忆录!](https://mp.weixin.qq.com/s?__biz=MzU1Nzg4NjgyMw==&mid=2247486684&idx=1&sn=807fd808adac8019eb2095ba088efe54&scene=21#wechat_redirect) -![](https://mmbiz.qpic.cn/mmbiz_jpg/PMZOEonJxWeNacvtL2JaV9oyHhSVP7CAOVRdvOo9pRk4Js4kVtJblIcP4fBSf7Za5Nw0dwq2QHSYRJXvxxN6bw/640?wx_fmt=jpeg) -[](https://mp.weixin.qq.com/s?__biz=MzkwODE5ODM0Ng==&mid=2247491521&idx=1&sn=dcefc00c23d0821990f62dc3749141bb&chksm=c0ccf764f7bb7e72defbc937a72e9d2ec766b8de1574def67a4650c80c8329694127b01405d4&scene=21&cur_album_id=2041709347461709827#wechat_redirect) -[](https://mp.weixin.qq.com/s?__biz=MzkwODE5ODM0Ng==&mid=2247491521&idx=1&sn=dcefc00c23d0821990f62dc3749141bb&chksm=c0ccf764f7bb7e72defbc937a72e9d2ec766b8de1574def67a4650c80c8329694127b01405d4&scene=21&cur_album_id=2041709347461709827#wechat_redirect) +![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-ruhfzddcfzf-da664b36-ac4c-4d16-a345-fc710462b515.jpg) -\- END - \ No newline at end of file +>转载链接:[https://mp.weixin.qq.com/s/yieSqKZbVvpe7R_DhRNVoA](https://mp.weixin.qq.com/s/yieSqKZbVvpe7R_DhRNVoA),出处:macrozheng,整理:沉默王二 diff --git a/images/nice-article/haibukuaiyongmybatisplusshoubashoujiaoni-385444a5-ff07-4020-a62f-4fda0142e835.png b/images/nice-article/haibukuaiyongmybatisplusshoubashoujiaoni-385444a5-ff07-4020-a62f-4fda0142e835.png new file mode 100644 index 0000000000000000000000000000000000000000..bfd35fce33a3a8a6016896d33943a1de770fe562 Binary files /dev/null and b/images/nice-article/haibukuaiyongmybatisplusshoubashoujiaoni-385444a5-ff07-4020-a62f-4fda0142e835.png differ diff --git a/images/nice-article/haibukuaiyongmybatisplusshoubashoujiaoni-9cf4609a-722e-47ab-a89c-64fe31a849fd.jpg b/images/nice-article/haibukuaiyongmybatisplusshoubashoujiaoni-9cf4609a-722e-47ab-a89c-64fe31a849fd.jpg new file mode 100644 index 0000000000000000000000000000000000000000..095c36e8960691843235108f345e122c1d35b8dd Binary files /dev/null and b/images/nice-article/haibukuaiyongmybatisplusshoubashoujiaoni-9cf4609a-722e-47ab-a89c-64fe31a849fd.jpg differ diff --git a/images/nice-article/haibukuaiyongmybatisplusshoubashoujiaoni-e649b313-4ad3-459d-bb22-59725d57fe8c.jpg b/images/nice-article/haibukuaiyongmybatisplusshoubashoujiaoni-e649b313-4ad3-459d-bb22-59725d57fe8c.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e762f23ce4350d212c08ab18cff0dc36303f2567 Binary files /dev/null and b/images/nice-article/haibukuaiyongmybatisplusshoubashoujiaoni-e649b313-4ad3-459d-bb22-59725d57fe8c.jpg differ diff --git a/images/nice-article/haibukuaiyongmybatisplusshoubashoujiaoni-ed20d595-a4a8-42af-bcbe-7afa578ad065.jpg b/images/nice-article/haibukuaiyongmybatisplusshoubashoujiaoni-ed20d595-a4a8-42af-bcbe-7afa578ad065.jpg new file mode 100644 index 0000000000000000000000000000000000000000..928797d12b0bd66f23b41e00421dcfde6e563f3d Binary files /dev/null and b/images/nice-article/haibukuaiyongmybatisplusshoubashoujiaoni-ed20d595-a4a8-42af-bcbe-7afa578ad065.jpg differ diff --git a/images/nice-article/weixin-chengxyspnhxagzl.jpg b/images/nice-article/weixin-chengxyspnhxagzl.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6d12ad79bb2d8e87f6611ac769c7fdd2bd66a66f Binary files /dev/null and b/images/nice-article/weixin-chengxyspnhxagzl.jpg differ diff --git a/images/nice-article/weixin-ruhfzcfxd-0083bd11-12c2-4894-bb81-d0cd4cd82148.jpg b/images/nice-article/weixin-ruhfzcfxd-0083bd11-12c2-4894-bb81-d0cd4cd82148.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5433095786a5a14e38eea4189d11c8e6fd915ec4 Binary files /dev/null and b/images/nice-article/weixin-ruhfzcfxd-0083bd11-12c2-4894-bb81-d0cd4cd82148.jpg differ diff --git a/images/nice-article/weixin-ruhfzcfxd-0f6232ba-4e09-4ef8-a56a-076de147575f.jpg b/images/nice-article/weixin-ruhfzcfxd-0f6232ba-4e09-4ef8-a56a-076de147575f.jpg new file mode 100644 index 0000000000000000000000000000000000000000..72cb259c3a59a0ecd6a86bb06ee82f8314271ae9 Binary files /dev/null and b/images/nice-article/weixin-ruhfzcfxd-0f6232ba-4e09-4ef8-a56a-076de147575f.jpg differ diff --git a/images/nice-article/weixin-ruhfzcfxd-2e3b7041-d317-4e79-9241-3dc098828a02.jpg b/images/nice-article/weixin-ruhfzcfxd-2e3b7041-d317-4e79-9241-3dc098828a02.jpg new file mode 100644 index 0000000000000000000000000000000000000000..50514ca8ee0f1494020a0f1e780eb6542637c9ab Binary files /dev/null and b/images/nice-article/weixin-ruhfzcfxd-2e3b7041-d317-4e79-9241-3dc098828a02.jpg differ diff --git a/images/nice-article/weixin-ruhfzcfxd-5a6a4a1d-66df-4fe7-80e9-fc6e2bd27bfd.jpg b/images/nice-article/weixin-ruhfzcfxd-5a6a4a1d-66df-4fe7-80e9-fc6e2bd27bfd.jpg new file mode 100644 index 0000000000000000000000000000000000000000..67f455ba75e4f56d087f276ea5447429c72d0a51 Binary files /dev/null and b/images/nice-article/weixin-ruhfzcfxd-5a6a4a1d-66df-4fe7-80e9-fc6e2bd27bfd.jpg differ diff --git a/images/nice-article/weixin-ruhfzcfxd-85e704f6-c73f-4881-8d30-ac80057f7bc3.jpg b/images/nice-article/weixin-ruhfzcfxd-85e704f6-c73f-4881-8d30-ac80057f7bc3.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a1041eeb9f471e8bfb3fe8a5a9cd51b912a2ff19 Binary files /dev/null and b/images/nice-article/weixin-ruhfzcfxd-85e704f6-c73f-4881-8d30-ac80057f7bc3.jpg differ diff --git a/images/nice-article/weixin-ruhfzcfxd-99b57ecc-6aa5-4100-ba56-63565c2dc037.jpg b/images/nice-article/weixin-ruhfzcfxd-99b57ecc-6aa5-4100-ba56-63565c2dc037.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0d8e840aef101e644ef2343c68cbeba3806ba367 Binary files /dev/null and b/images/nice-article/weixin-ruhfzcfxd-99b57ecc-6aa5-4100-ba56-63565c2dc037.jpg differ diff --git a/images/nice-article/weixin-ruhfzcfxd.jpg b/images/nice-article/weixin-ruhfzcfxd.jpg new file mode 100644 index 0000000000000000000000000000000000000000..079dc8d11e361c302b6a9f63ea8ca327e97052eb Binary files /dev/null and b/images/nice-article/weixin-ruhfzcfxd.jpg differ diff --git a/images/nice-article/weixin-ruhfzddcfzf-0a29dff7-75eb-43b9-a2e4-fd04e9b299e6.jpg b/images/nice-article/weixin-ruhfzddcfzf-0a29dff7-75eb-43b9-a2e4-fd04e9b299e6.jpg new file mode 100644 index 0000000000000000000000000000000000000000..aab85ffb8aadfd36aa2280e6d6fef778b65ea9be Binary files /dev/null and b/images/nice-article/weixin-ruhfzddcfzf-0a29dff7-75eb-43b9-a2e4-fd04e9b299e6.jpg differ diff --git a/images/nice-article/weixin-ruhfzddcfzf-10584c11-08ca-4fb8-a6fb-a68155aeb21e.jpg b/images/nice-article/weixin-ruhfzddcfzf-10584c11-08ca-4fb8-a6fb-a68155aeb21e.jpg new file mode 100644 index 0000000000000000000000000000000000000000..bf5646257f341ae13d9f4eb150bf70e59026e05d Binary files /dev/null and b/images/nice-article/weixin-ruhfzddcfzf-10584c11-08ca-4fb8-a6fb-a68155aeb21e.jpg differ diff --git a/images/nice-article/weixin-ruhfzddcfzf-30d252e2-ef82-4f4a-bbe8-45ca12c12ef4.jpg b/images/nice-article/weixin-ruhfzddcfzf-30d252e2-ef82-4f4a-bbe8-45ca12c12ef4.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b1a56a1fc4217809204f12019e47f0e1c1c0a823 Binary files /dev/null and b/images/nice-article/weixin-ruhfzddcfzf-30d252e2-ef82-4f4a-bbe8-45ca12c12ef4.jpg differ diff --git a/images/nice-article/weixin-ruhfzddcfzf-31a82d77-598a-4b1e-bf73-0c2b5ac490f1.jpg b/images/nice-article/weixin-ruhfzddcfzf-31a82d77-598a-4b1e-bf73-0c2b5ac490f1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..37bc6185c02aace0fbdf3c1ee0eb599bff1b5961 Binary files /dev/null and b/images/nice-article/weixin-ruhfzddcfzf-31a82d77-598a-4b1e-bf73-0c2b5ac490f1.jpg differ diff --git a/images/nice-article/weixin-ruhfzddcfzf-41a23c59-9701-4f6e-808f-64b0c856327c.jpg b/images/nice-article/weixin-ruhfzddcfzf-41a23c59-9701-4f6e-808f-64b0c856327c.jpg new file mode 100644 index 0000000000000000000000000000000000000000..38c3cd7dea74166f250de3f371b0a0b707a249c7 Binary files /dev/null and b/images/nice-article/weixin-ruhfzddcfzf-41a23c59-9701-4f6e-808f-64b0c856327c.jpg differ diff --git a/images/nice-article/weixin-ruhfzddcfzf-4dbb0659-7401-47be-b009-f266cca2d732.jpg b/images/nice-article/weixin-ruhfzddcfzf-4dbb0659-7401-47be-b009-f266cca2d732.jpg new file mode 100644 index 0000000000000000000000000000000000000000..649a27db83ca4f7781ce079cdc059b58752bdab3 Binary files /dev/null and b/images/nice-article/weixin-ruhfzddcfzf-4dbb0659-7401-47be-b009-f266cca2d732.jpg differ diff --git a/images/nice-article/weixin-ruhfzddcfzf-4fe79ee5-b65f-4992-bec0-3f521aa7362d.jpg b/images/nice-article/weixin-ruhfzddcfzf-4fe79ee5-b65f-4992-bec0-3f521aa7362d.jpg new file mode 100644 index 0000000000000000000000000000000000000000..96cfa1bd891ddff0858764390f709b521563a82a Binary files /dev/null and b/images/nice-article/weixin-ruhfzddcfzf-4fe79ee5-b65f-4992-bec0-3f521aa7362d.jpg differ diff --git a/images/nice-article/weixin-ruhfzddcfzf-85a3662c-ccc6-4bea-8fd6-78374ad64fd5.jpg b/images/nice-article/weixin-ruhfzddcfzf-85a3662c-ccc6-4bea-8fd6-78374ad64fd5.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fbd074423f37e7fb97ac44addc9bee65cb2fcad6 Binary files /dev/null and b/images/nice-article/weixin-ruhfzddcfzf-85a3662c-ccc6-4bea-8fd6-78374ad64fd5.jpg differ diff --git a/images/nice-article/weixin-ruhfzddcfzf-9a63af83-39e5-463b-9a69-8e5238e4a49e.jpg b/images/nice-article/weixin-ruhfzddcfzf-9a63af83-39e5-463b-9a69-8e5238e4a49e.jpg new file mode 100644 index 0000000000000000000000000000000000000000..168dd1cc178f789be843b14a65c7585f15b61b59 Binary files /dev/null and b/images/nice-article/weixin-ruhfzddcfzf-9a63af83-39e5-463b-9a69-8e5238e4a49e.jpg differ diff --git a/images/nice-article/weixin-ruhfzddcfzf-9b6deba3-d86b-45d3-b17f-b3f4aa615505.jpg b/images/nice-article/weixin-ruhfzddcfzf-9b6deba3-d86b-45d3-b17f-b3f4aa615505.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d6e102eec26b1b649923204b7c4306cb35987260 Binary files /dev/null and b/images/nice-article/weixin-ruhfzddcfzf-9b6deba3-d86b-45d3-b17f-b3f4aa615505.jpg differ diff --git a/images/nice-article/weixin-ruhfzddcfzf-a1c0390b-e070-4b77-a913-d28ce971b0af.jpg b/images/nice-article/weixin-ruhfzddcfzf-a1c0390b-e070-4b77-a913-d28ce971b0af.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f045fba0314a4667b5ceb330eb112a8f4178b5ed Binary files /dev/null and b/images/nice-article/weixin-ruhfzddcfzf-a1c0390b-e070-4b77-a913-d28ce971b0af.jpg differ diff --git a/images/nice-article/weixin-ruhfzddcfzf-ba7c4a9a-27cb-43d3-8624-30f3b451ba26.jpg b/images/nice-article/weixin-ruhfzddcfzf-ba7c4a9a-27cb-43d3-8624-30f3b451ba26.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d3a33e8e1b9383b377cfc1676f3a629bbab45402 Binary files /dev/null and b/images/nice-article/weixin-ruhfzddcfzf-ba7c4a9a-27cb-43d3-8624-30f3b451ba26.jpg differ diff --git a/images/nice-article/weixin-ruhfzddcfzf-d214715e-611b-40bd-be68-e4646e91cdcc.jpg b/images/nice-article/weixin-ruhfzddcfzf-d214715e-611b-40bd-be68-e4646e91cdcc.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fb3e577a8cefbe478ffaad63fca344d52bc7f830 Binary files /dev/null and b/images/nice-article/weixin-ruhfzddcfzf-d214715e-611b-40bd-be68-e4646e91cdcc.jpg differ diff --git a/images/nice-article/weixin-ruhfzddcfzf-da664b36-ac4c-4d16-a345-fc710462b515.jpg b/images/nice-article/weixin-ruhfzddcfzf-da664b36-ac4c-4d16-a345-fc710462b515.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ba8eb34cbe51a9ec4b393dee9cb21703c81d4590 Binary files /dev/null and b/images/nice-article/weixin-ruhfzddcfzf-da664b36-ac4c-4d16-a345-fc710462b515.jpg differ diff --git a/images/nice-article/weixin-ruhfzddcfzf-ef5a814e-e1fa-4851-b976-325c2ffc6b68.jpg b/images/nice-article/weixin-ruhfzddcfzf-ef5a814e-e1fa-4851-b976-325c2ffc6b68.jpg new file mode 100644 index 0000000000000000000000000000000000000000..469fd1c83bfac395160098592e90b221e03567e9 Binary files /dev/null and b/images/nice-article/weixin-ruhfzddcfzf-ef5a814e-e1fa-4851-b976-325c2ffc6b68.jpg differ diff --git a/images/nice-article/weixin-ruhfzddcfzf.jpg b/images/nice-article/weixin-ruhfzddcfzf.jpg index 5ab48928bef17501569c642d0fd957bfa3ac0f69..9b03f90f2b1995e352dabc3296b5011e081413bb 100644 Binary files a/images/nice-article/weixin-ruhfzddcfzf.jpg and b/images/nice-article/weixin-ruhfzddcfzf.jpg differ