diff --git a/repos/.vitepress/config/nav.ts b/repos/.vitepress/config/nav.ts
index f0fdb9d19..38e70634e 100644
--- a/repos/.vitepress/config/nav.ts
+++ b/repos/.vitepress/config/nav.ts
@@ -16,11 +16,18 @@ export const nav: DefaultTheme.Config['nav'] = [
link: '/solutions/index',
activeMatch: '/solutions/'
},
+ {
+ text: '我的小课',
+ items: [
+ { text: 'MyBatis快速入门', link: '/courses/mybatis/index' }
+ ],
+ activeMatch: '/courses/'
+ },
{
text: '关于',
items: [
{ text: '关于知识库', link: '/about/index' },
- { text: '关于笔者', link: '/about/me' }
+ { text: '关于我', link: '/about/me' }
],
activeMatch: '/about/'
}
diff --git a/repos/.vitepress/config/sidebar.ts b/repos/.vitepress/config/sidebar.ts
index 7ac48a7cd..82e64e96c 100644
--- a/repos/.vitepress/config/sidebar.ts
+++ b/repos/.vitepress/config/sidebar.ts
@@ -2,18 +2,21 @@ import DefaultTheme from 'vitepress/theme'
import { sync } from "fast-glob"
export const sidebar: DefaultTheme.Config['sidebar'] = {
- '/issues/': getItems("issues"),
- '/fragments/': getItems("fragments"),
- '/solutions/': getItems("solutions")
+ '/issues/': getItemsByDate("issues"),
+ '/fragments/': getItemsByDate("fragments"),
+ '/solutions/': getItemsByDate("solutions"),
+ '/courses/mybatis/': getItems("courses/mybatis")
}
/**
- * 获取侧边栏分组及分组下标题
+ * 根据 年/月/日.xxmd 的目录格式, 获取侧边栏分组及分组下标题
+ *
+ * /repos/issues/2022/07/20.xxx.md
*
* @param path 扫描基础路径
* @returns {DefaultTheme.SidebarGroup[]}
*/
-function getItems (path: string) {
+function getItemsByDate (path: string) {
// 侧边栏分组数组
let groups: DefaultTheme.SidebarGroup[] = []
// 侧边栏分组下标题数组
@@ -22,23 +25,28 @@ function getItems (path: string) {
// 1.获取所有年份目录
sync(`repos/${path}/*`, {
onlyDirectories: true,
- objectMode: true,
+ objectMode: true
}).forEach(({ name }) => {
let year = name
- for (let i = 1; i <= 12; i++) {
- let month = i < 10 ? `0${i}` : i
- // 2.获取所有月份目录下的文章
+ // 2.获取所有月份目录
+ sync(`repos/${path}/${year}/*`, {
+ onlyDirectories: true,
+ objectMode: true
+ }).forEach(({ name }) => {
+ let month = name
+ // 3.获取月份目录下的所有文章
sync(`repos/${path}/${year}/${month}/*`, {
- objectMode: true,
+ onlyFiles: true,
+ objectMode: true
}).forEach(({ name }) => {
// 向前追加标题
items.unshift({
text: name,
- link: `/${path}/${year}/${month}/${name}`,
+ link: `/${path}/${year}/${month}/${name}`
})
})
- // 3.向前追加分组
+ // 4.向前追加到分组
if (items.length > 0) {
// 去除标题名中的日期前缀和扩展名
for (let i = 0; i < items.length; i++) {
@@ -53,9 +61,65 @@ function getItems (path: string) {
})
}
- // 4.清空侧边栏分组下标题数组
+ // 5.清空侧边栏分组下标题数组
items = []
+ })
+ })
+
+ // 6.将第一个侧边栏分组的标题展开
+ groups[0].collapsed = false
+ return groups
+}
+
+/**
+ * 根据 分组/序号.xxmd 的目录格式, 获取侧边栏分组及分组下标题
+ *
+ * /repos/courses/mybatis/MyBatis基础/20.xxx.md
+ *
+ * @param path 扫描基础路径
+ * @returns {DefaultTheme.SidebarGroup[]}
+ */
+function getItems (path: string) {
+ // 侧边栏分组数组
+ let groups: DefaultTheme.SidebarGroup[] = []
+ // 侧边栏分组下标题数组
+ let items: DefaultTheme.SidebarItem[] = []
+
+ // 1.获取所有分组目录
+ sync(`repos/${path}/*`, {
+ onlyDirectories: true,
+ objectMode: true
+ }).forEach(({ name }) => {
+ let groupName = name
+ // 2.获取分组下的所有文章
+ sync(`repos/${path}/${groupName}/*`, {
+ onlyFiles: true,
+ objectMode: true
+ }).forEach(({ name }) => {
+ // 向前追加标题
+ items.push({
+ text: name,
+ link: `/${path}/${groupName}/${name}`
+ })
+ })
+
+ // 3.向前追加到分组
+ if (items.length > 0) {
+ // 去除标题名中的日期前缀和扩展名
+ for (let i = 0; i < items.length; i++) {
+ let text = items[i].text
+ items[i].text = text.replace('.md', '').substring(text.indexOf('.') + 1)
+ }
+ groups.push({
+ text: `${groupName.substring(groupName.indexOf('.') + 1)} (${items.length}篇)`,
+ collapsible: true,
+ collapsed: true,
+ items: items
+ })
}
+
+ // 4.清空侧边栏分组下标题数组
+ items = []
})
// 5.将第一个侧边栏分组的标题展开
diff --git a/repos/courses/mybatis/01.MyBatis基础/01.快速入门.md b/repos/courses/mybatis/01.MyBatis基础/01.快速入门.md
new file mode 100644
index 000000000..304d6fb97
--- /dev/null
+++ b/repos/courses/mybatis/01.MyBatis基础/01.快速入门.md
@@ -0,0 +1,255 @@
+---
+title: 快速入门
+author: 查尔斯
+date: 2020/12/25 14:49
+categories:
+ - MyBatis快速入门
+tags:
+ - MyBatis
+ - ORM框架
+---
+
+# 快速入门
+
+我们将通过一个简单的 Demo 来阐述 MyBatis 的强大功能,在此之前,笔者假设你已经:
+
+- 拥有 Java 开发环境以及相应 IDE(本 Demo 采用 Eclipse 作为IDE)
+- 熟悉 Java Web 开发流程
+- 熟悉至少一个关系型数据库(本 Demo 采用 MySQL 作为数据库)
+
+## 数据库准备
+
+现有一张 `User` 表,其表结构如下:
+
+| 主键 | 姓名 | 年龄 | 邮箱 |
+| :--: | :----: | :--: | :------------: |
+| 1 | Jone | 18 | Jone@126.com |
+| 2 | Jack | 20 | Jack@126.com |
+| 3 | Tom | 28 | Tom@126.com |
+| 4 | Sandy | 21 | Sandy@126.com |
+| 5 | Billie | 24 | Billie@126.com |
+
+其对应的数据库 结构 脚本如下:
+
+```sql
+-- 创建并切换数据库
+CREATE DATABASE mybatis_demo_db;
+USE mybatis_demo_db;
+
+-- 创建用户数据表
+CREATE TABLE `user` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+ `name` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '姓名',
+ `age` int(11) NULL DEFAULT NULL COMMENT '年龄',
+ `email` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '邮箱',
+ PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '用户表' ROW_FORMAT = Compact;
+```
+
+其对应的数据库 数据 脚本如下:
+
+```sql
+-- 清空用户表数据
+TRUNCATE TABLE user;
+
+-- 向用户表插入测试数据
+INSERT INTO `user` VALUES (1, 'Jone', 18, 'Jone@126.com');
+INSERT INTO `user` VALUES (2, 'Jack', 20, 'Jack@126.com');
+INSERT INTO `user` VALUES (3, 'Tom', 28, 'Tom@126.com');
+INSERT INTO `user` VALUES (4, 'Sandy', 21, 'Sandy@126.com');
+INSERT INTO `user` VALUES (5, 'Billie', 24, 'Billie@126.com');
+```
+
+## 下载依赖
+
+要使用 MyBatis 框架,第一步就是下载好 MyBatis 的 jar 包,我们可以从 [MyBatis](https://github.com/mybatis/mybatis-3/releases) 在 GitHub 上的开源地址下载。
+
+
+
+笔者下载了 MyBatis 的核心压缩包(mybatis-x.x.x.zip)及其源码包(mybatis-x-mybatis-x.x.x.zip)。
+
+
+
+解压开 **mybatis-3.5.6.zip** 压缩包,目录结构如下:
+
+
+
+::: tip 笔者说
+如果 GitHub 下载太慢,可以前往 [FastGit](https://hub.fastgit.org/mybatis/mybatis-3/releases/tag/mybatis-3.5.6) 进行下载,它是 GitHub 的镜像地址,网站界面等各方面与 GitHub 几乎一模一样。
+但是注意它仅仅是一个镜像网站,可以用于克隆或下载 GitHub 资源,但登录之类的功能是不可用的。
+:::
+
+## 创建项目
+
+下载好依赖之后,我们通过 Eclipse,创建一个动态 Web 项目,并将刚才下载的 jar 包和指定数据库驱动包添加到 WebConent\WEB-INF\lib 目录,效果如下:
+
+
+
+::: tip 笔者说
+本次我们不会使用到 Servlet API,所以创建一个普通 Java 工程也没问题。
+:::
+
+## 创建POJO类
+
+在 DAO 模式开发中,第一步就是要创建实体类,而在 MyBatis 项目中,实体类"弱化"为了 POJO,这种类型是专门用于和数据库做映射的 Java 类型,数据表中的列与 POJO 类型的属性一 一对应。
+
+```java
+package com.example.pojo;
+
+/**
+ * 用户POJO类(它是Java和关系数据库表映射的类型)
+ * @author Charles7c
+ */
+public class User {
+ private Long id;
+ private String name;
+ private Integer age;
+ private String email;
+ // 省略getter/setter方法
+ // 省略toString方法
+}
+```
+
+::: tip 笔者说
+POJO(Plain Old Java Objects,普通老式 Java 对象)。一般来讲,将 POJO 简单理解为实体类也无伤大雅。
+:::
+
+## 创建SQL映射文件
+
+在 DAO 模式开发中,实体类创建完之后就是要编写 BaseDao、以及不同实体的 Dao 接口和 Dao 实现类。但这一切的繁琐过程,在现在都被 MyBatis 解决了。
+
+现在,我们只需要按照 MyBatis 的要求创建好一个编写 SQL 的映射文件,在映射文件中编写好数据库的 CRUD 操作即可。
+
+```xml
+
+
+
+
+
+
+
+```
+
+::: tip 笔者说
+SQL 映射文件的命名风格为:POJO类名Mapper.xml,就像命名以前的 Dao接口 一样,你可以将 SQL 映射文件理解为是以前的 Dao 实现类。
+:::
+
+## 创建核心配置文件
+
+MyBatis 为我们简化了非常多的操作,但是一些必须由我们自定义的配置还是少不了的。在 classpath 下创建一个核心配置文件命名为:`mybatis-config.xml`。
+
+```xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+## 添加日志配置文件
+
+在 MyBatis 中,采用的日志框架是 log4j,所以为了能够查看到日志输出,我们需要在 classpath 下添加一个 log4j.properties 文件。
+
+```
+###############################################################
+# 输出到控制台 #
+###############################################################
+# log4j.rootLogger日志输出类别和级别:只输出不低于该级别的日志信息DEBUG < INFO < WARN < ERROR < FATAL
+# DEBUG:日志级别 CONSOLE:输出位置自己定义的一个名字
+log4j.rootLogger=DEBUG,CONSOLE
+# 配置CONSOLE输出到控制台
+log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
+# 配置CONSOLE设置为自定义布局模式
+log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
+# 配置CONSOLE日志的输出格式 [demo] 2019-08-22 22:52:12,000 %r耗费毫秒数 %p日志的优先级 %t线程名 %C所属类名通常为全类名 %L代码中的行号 %x线程相关联的NDC %m日志 %n换行
+log4j.appender.CONSOLE.layout.ConversionPattern=[demo] %d{yyyy-MM-dd HH:mm:ss,SSS} - %-4r %-5p [%t] %C:%L %x - %m%n
+```
+
+## 测试
+
+当一切准备好之后,完整的项目目录结构如下:
+
+
+
+创建好一个单元测试类,测试一下:
+
+```java
+class TestMyBatis {
+
+ @Test
+ void testSelectList() throws IOException {
+ // 1.从classpath加载核心配置文件,构建SqlSession工厂对象
+ InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
+ SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
+
+ // 2.获取SqlSession对象
+ try (SqlSession sqlSession = sqlSessionFactory.openSession()){
+
+ // 3.执行SQL语句 根据要执行的SQL语句选择合适的API
+ // p1:SQL语句唯一地址 (SQL映射文件的namespace值.SQL语句的id值)
+ List userList = sqlSession.selectList("userMapper.selectList");
+
+ // 4.遍历数据
+ userList.forEach(System.out::println);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+}
+```
+
+**控制台输出:**
+
+```
+User [id=1, name=Jone, age=18, email=Jone@126.com]
+User [id=2, name=Jack, age=20, email=Jack@126.com]
+User [id=3, name=Tom, age=28, email=Tom@126.com]
+User [id=4, name=Sandy, age=21, email=Sandy@126.com]
+User [id=5, name=Billie, age=24, email=Billie@126.com]
+```
+
+## 后记
+
+**C:** 好了,与 MyBatis 的第一次约会结束了。怎么样?约会体验如何?使用步骤是不是还挺简单的?
+
+虽然是在学习一个新技术,但是一定要时刻想想当初 DAO 模式你是怎么一个开发步骤,这样对比着会发现 MyBatis 就是在简化、优化原来的每个环节而已。根本上还是那么回事,多想想,脑子里就能留下使用思路。
+
+::: info 笔者说
+对于技术的学习,笔者一贯遵循的步骤是:先用最最简单的 demo 让它跑起来,然后学学它的最最常用 API 和 配置让自己能用起来,最后熟练使用的基础上,在空闲时尝试阅读它的源码让自己能够洞彻它的运行机制,部分问题出现的原因,同时借鉴这些技术实现来提升自己的代码高度。
+
+所以在笔者的文章中,前期基本都是小白文,仅仅穿插很少量的源码研究。当然等小白文更新多了,你们还依然喜欢,后期会不定时专门对部分技术的源码进行解析。
+:::
diff --git a/repos/courses/mybatis/index.md b/repos/courses/mybatis/index.md
new file mode 100644
index 000000000..3b7eb9669
--- /dev/null
+++ b/repos/courses/mybatis/index.md
@@ -0,0 +1,127 @@
+---
+editLink: false
+lastUpdated: false
+aside: false
+---
+
+# MyBatis快速入门
+
+## 前言
+
+**C:** 在 Java Web 开发中,我们通常将后台开发拆分为三层架构,分别是:表现层、业务层、持久层。
+
+在持久层中,最开始我们使用原生 JDBC 来进行数据库的 CRUD,代码繁琐的令人抓狂。后来随着学习深入,我们利用 DAO (Data Access Object) 模式对 JDBC 进行了一定的优化封装。
+
+即便如此,还是要在 Java 代码中编写大量的 SQL 语句,参数判断等,下面是我截取的一段 DAO模式封装后的代码,你简单感受一下。
+
+```java
+// 假设BaseDao已经封装了通用CRUD操作
+public class UserDaoImpl extends BaseDao implements UserDao {
+
+ // 根据条件查询用户列表
+ @Override
+ public List findByMap(Map params) throws Exception {
+ // 动态拼接SQL语句
+ StringBuffer sqlBuffer = new StringBuffer();
+ // 动态拼接SQL占位符参数
+ List