新增:《初识MyBatis | 快速入门》
@@ -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/'
|
||||
}
|
||||
|
@@ -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.将第一个侧边栏分组的标题展开
|
||||
|
255
repos/courses/mybatis/01.MyBatis基础/01.快速入门.md
Normal file
@@ -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
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<!-- namespace:命名空间,在同一个项目中保持唯一 -->
|
||||
<mapper namespace="userMapper">
|
||||
<!-- SQL 操作(根据 CRUD 的不同选择不同的标签编写 SQL 操作)
|
||||
id:SQL 操作标识
|
||||
resultType: 结果集类型(全类名)
|
||||
下方设定等价于编写了一个方法:List<User> selectList();
|
||||
-->
|
||||
<select id="selectList" resultType="com.example.pojo.User">
|
||||
SELECT * FROM `user`
|
||||
</select>
|
||||
</mapper>
|
||||
```
|
||||
|
||||
::: tip 笔者说
|
||||
SQL 映射文件的命名风格为:POJO类名Mapper.xml,就像命名以前的 Dao接口 一样,你可以将 SQL 映射文件理解为是以前的 Dao 实现类。
|
||||
:::
|
||||
|
||||
## 创建核心配置文件
|
||||
|
||||
MyBatis 为我们简化了非常多的操作,但是一些必须由我们自定义的配置还是少不了的。在 classpath 下创建一个核心配置文件命名为:`mybatis-config.xml`。
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE configuration
|
||||
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-config.dtd">
|
||||
<configuration>
|
||||
<!-- 环境配置:
|
||||
可以配置多个,但生效的只有一个
|
||||
default属性:指定生效的环境的id
|
||||
-->
|
||||
<environments default="develop">
|
||||
<!-- 单个环境配置 -->
|
||||
<environment id="develop">
|
||||
<!-- 事务管理配置 -->
|
||||
<transactionManager type="JDBC"/>
|
||||
<!-- 数据源配置 -->
|
||||
<dataSource type="POOLED">
|
||||
<property name="driver" value="com.mysql.jdbc.Driver"/>
|
||||
<property name="url" value="jdbc:mysql://localhost:3306/mybatis_demo_db"/>
|
||||
<property name="username" value="root"/>
|
||||
<property name="password" value="root"/>
|
||||
</dataSource>
|
||||
</environment>
|
||||
</environments>
|
||||
|
||||
<!-- 指定要加载的SQL映射文件 -->
|
||||
<mappers>
|
||||
<!-- 注意:该地址不是全类名!!! -->
|
||||
<mapper resource="com/example/mapper/UserMapper.xml"/>
|
||||
</mappers>
|
||||
</configuration>
|
||||
```
|
||||
|
||||
## 添加日志配置文件
|
||||
|
||||
在 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<User> 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 和 配置让自己能用起来,最后熟练使用的基础上,在空闲时尝试阅读它的源码让自己能够洞彻它的运行机制,部分问题出现的原因,同时借鉴这些技术实现来提升自己的代码高度。
|
||||
|
||||
所以在笔者的文章中,前期基本都是小白文,仅仅穿插很少量的源码研究。当然等小白文更新多了,你们还依然喜欢,后期会不定时专门对部分技术的源码进行解析。
|
||||
:::
|
127
repos/courses/mybatis/index.md
Normal file
@@ -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<User> findByMap(Map<String, Object> params) throws Exception {
|
||||
// 动态拼接SQL语句
|
||||
StringBuffer sqlBuffer = new StringBuffer();
|
||||
// 动态拼接SQL占位符参数
|
||||
List<Object> paramsList = new ArrayList<>();
|
||||
|
||||
sqlBuffer.append(" select ");
|
||||
sqlBuffer.append(" * ");
|
||||
sqlBuffer.append(" from ");
|
||||
sqlBuffer.append(" user ");
|
||||
sqlBuffer.append(" where 1 = 1 ");
|
||||
|
||||
// 根据用户名模糊查询
|
||||
Object name = params.get("name");
|
||||
if (EmptyUtils.isNotEmpty(name)) {
|
||||
sqlBuffer.append(" and name like CONCAT('%',?,'%') ");
|
||||
paramsList.add(name);
|
||||
}
|
||||
|
||||
// 根据年龄查询
|
||||
Integer age = (Integer) params.get("age");
|
||||
if (EmptyUtils.isNotEmpty(age)) {
|
||||
sqlBuffer.append(" and age = ? ");
|
||||
paramsList.add(age);
|
||||
}
|
||||
|
||||
return this.selectList(sqlBuffer.toString(), paramsList.toArray(), User.class);
|
||||
}
|
||||
|
||||
// 保存用户
|
||||
@Override
|
||||
public int save(User user) throws Exception {
|
||||
StringBuffer sqlBuffer = new StringBuffer();
|
||||
|
||||
sqlBuffer.append(" insert into user ");
|
||||
sqlBuffer.append(" (name, age, email) ");
|
||||
sqlBuffer.append(" values(?, ?, ?) ");
|
||||
|
||||
Object[] params = {user.getName(), user.getAge(), user.getEmail()};
|
||||
|
||||
return this.insert(sqlBuffer.toString(), params);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
有问题存在,就不缺解决问题的人。在行业内随之诞生了大量的持久层解决方案,除了各自公司自研的方案之外,比较有名的通用开源方案有:Hibernate、MyBatis等,各有各的优劣势,在此我先不谈论它们的区别,只说一个现象:在国内来讲MyBatis应用相对广泛。
|
||||
|
||||

|
||||
|
||||
所以那没什么好说的了,直接学吧?
|
||||
|
||||
开玩笑的,其实MyBatis在国内应用广泛的原因是因为相对于Hibernate等,它可以更加灵活的编写SQL语句,对于需求变动比较频繁,业务比较复杂,高并发要求较高的应用,优势显而易见。国内这两年互联网发展的挺快,互联网用户群体基数大,而国外人群规模相对较少,相比于国内开发者,他们更关注实现效率而非极致的性能。
|
||||
|
||||
## 简介
|
||||
|
||||
::: tip 笔者说
|
||||
MyBatis读音是:[mai'bətɪs](买杯涕死),原是 Apache 软件基金会的一个[开源项目](https://baike.baidu.com/item/开源项目/3406069) iBatis , 2010年这个项目由 Apache 软件基金会迁移到了 Google Code 平台,并且改名为 MyBatis 。2013年11月再迁移到 [GitHub](https://baike.baidu.com/item/Github/10145341)。
|
||||
|
||||
MyBatis 是一款优秀的 **半自动的持久层ORM框架** ,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
|
||||
|
||||
当前,最新版本是MyBatis 3.5.6 ,其发布时间是2020年10月6日。[1]
|
||||

|
||||
:::
|
||||
|
||||
::: details **1.什么是 ORM ?** [2]
|
||||
ORM,对象关系映射(Object Relation Mapping,简称ORM,或O/RM,或O/R Mapping),它是随着面向对象的软件开发方法发展而产生的。
|
||||
|
||||
面向对象的开发方法是当今企业级应用开发环境中的主流开发方法,关系数据库是企业级应用环境中永久存放数据的主流数据存储系统。对象和关系数据是业务实体的两种表现形式,业务实体在内存中表现为对象,在数据库中表现为关系数据。内存中的对象之间存在关联和继承关系,而在数据库中,关系数据无法直接表达多对多关联和继承关系。因此,对象-关系映射( ORM )系统一般以中间件的形式存在,主要实现程序对象到关系数据库数据的映射。
|
||||

|
||||
|
||||
面向对象是从软件工程基本原则(如耦合、聚合、封装)的基础上发展起来的,而关系数据库则是从数学理论发展而来的,两套理论存在显著的区别。为了解决这个不匹配的现象,对象关系映射技术应运而生。
|
||||
|
||||
**它可以有效解决数据库与程序间的异构性,实现面向对象编程语言里不同类型系统的数据之间的转换。**
|
||||
|
||||
**大白话:** 一个完整的ORM框架,可以使得我们在具体的操作业务对象的时候,不需要再去和复杂的SQL语句之流打交道,只需简单的操作对象的属性即可。例如:在指定好对象和数据库映射之后,要保存一个用户数据,只需要创建好用户对象,然后调用ORM解决方案,就会自动将对象数据持久化到数据库中。(无需关心SQL,ORM自动生成SQL) ;要更新一个用户信息,只需要在程序中对该用户对象的属性进行更新,ORM解决方案就会自动将更改后的结果持久化到数据库中。
|
||||
:::
|
||||
|
||||
::: details **2.为什么说 MyBatis 是半自动 ORM ?** [2]
|
||||
Mybatis 在查询关联对象或关联集合对象时,需要手动编写 SQL 来完成,所以称之为半自动 ORM 映射工具。也可以说 MyBatis 是一个业务实体对象和 SQL 之间的映射工具。
|
||||
:::
|
||||
|
||||
## 特点
|
||||
|
||||
**C:** 笔者其实不愿意先给你介绍一个技术的特点,因为没有用过,光靠说是不行的。只有在我们实际体验之后,它的优缺点才可以感受到了,不过提前先了解一下,也更有学习动力,千万记得学习的过程中及时思考和理解这些特点。
|
||||
|
||||
**优势:**
|
||||
|
||||
- **简单易学:** 框架规模小,学习门槛低(官方文档简单详细),与 JDBC 相比,减少了50%以上的代码量,消除了 JDBC 大量冗余的代码,不需要手动开关连接。
|
||||
- **灵活度高:** 半自动化 ORM,程序员直接编写原生态 SQL ,可严格控制 SQL 执行性能,灵活度高,非常适合对关系数据模型要求不高的软件开发,例如:互联网软件、企业运营类软件等,因为这类软件需求变化频繁,一但需求变化要求成果输出迅速。
|
||||
- **解除 SQL 与程序代码的耦合:** SQL 代码从程序代码中彻底分离,写在XML里,可重用。
|
||||
- **提供映射标签,支持对象与数据库的 ORM 字段映射**
|
||||
- **提供 XML 标签,支持编写动态 SQL :** 在使用 JDBC 的过程中, 根据条件进行 SQL 的拼接是很麻烦且很容易出错的。 MyBatis 动态 SQL 的出现, 解决了这个麻烦。
|
||||
|
||||
**劣势:**
|
||||
|
||||
- **SQL语句的编写工作量较大**:尤其当字段多,关联表多时,对开发人员编写SQL语句的功底有一定要求。
|
||||
- **数据库无关性、移植性差:** SQL语句依赖数据库,导致数据库移植性差,不能随意更换数据库。[3]
|
||||
|
||||
## 参考文献
|
||||
|
||||
[1]MyBatis 官网. MyBatis 简介[EB/OL]. https://mybatis.org/mybatis-3/zh/index.html. 2020-12-25
|
||||
|
||||
[2]laouei. 理解 ORM 和数据持久化[EB/OL]. https://blog.csdn.net/u012585964/article/details/52412520. 2016-09-02
|
||||
|
||||
[3]W3CSchool. MyBatis 教程[EB/OL]. https://www.w3cschool.cn/mybatis/. 2020-12-25
|
BIN
repos/public/img/2021/11/202111232219533.png
Normal file
After Width: | Height: | Size: 113 KiB |
BIN
repos/public/img/2021/11/202111232221059.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
repos/public/img/2021/11/202111232221734.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
repos/public/img/2021/11/202111232222518.png
Normal file
After Width: | Height: | Size: 99 KiB |
BIN
repos/public/img/2021/11/202111232222738.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
repos/public/img/2021/11/202111232222812.png
Normal file
After Width: | Height: | Size: 8.8 KiB |
BIN
repos/public/img/2021/11/202111232223067.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
repos/public/img/2021/11/202111232223708.png
Normal file
After Width: | Height: | Size: 44 KiB |