day02-自己实现Mybatis底层机制-01
自己实现Mybatis底层机制-01
主要实现:封装SqlSession到执行器+Mapper接口和Mapper.xml+MapperBean+动态代理Mapper的方法
1.Mybatis整体架构分析

对上图的解读:
1)mybatis 的核心配置文件
mybatis-config.xml:进行全局配置,全局只能有一个这样的配置文件
XxxMapper.xml 配置多个SQL,可以有多个 XxxMapper.xml 配置文件
2)通过 mybatis-config.xml 配置文件得到 SqlSessionFactory
3)通过 SqlSessionFactory 得到 SqlSession,用 SqlSession 就可以操作数据了
4)SqlSession 底层是 Executor(执行器),有两个重要的实现类

5)MappedStatement 是通过 XxxMapper.xml 来定义的,用来生成 statement 对象
6)参数输入执行并输出结果集,无需动手判断参数类型和参数下标位置,且自动将结果集映射为Java对象
2.搭建开发环境
(1)创建maven项目


(2)在pom.xml 中引入必要的依赖
<!--指定编译器/source/target的版本-->
<properties>
<project.build.sourdeEncoding>UTF-8</project.build.sourdeEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<java.version>1.8</java.version>
</properties>
<!--引入必要的依赖-->
<dependencies>
<!--dom4j-->
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
<!--lombok-简化entity/javabean/pojo 的开发-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.4</version>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
(3)创建数据库和表
-- 创建数据库
CREATE DATABASE `li_mybatis`;
USE `li_mybatis`;
-- 创建monster表
CREATE TABLE `monster`(
`id` INT NOT NULL AUTO_INCREMENT,
`age` INT NOT NULL,
`birthday` DATE DEFAULT NULL,
`email` VARCHAR(255) NOT NULL,
`gender` TINYINT NOT NULL,-- 1 male,0 female
`name` VARCHAR(255) NOT NULL,
`salary` DOUBLE NOT NULL,
PRIMARY KEY(`id`)
)CHARSET=utf8
-- insert
INSERT INTO `monster` VALUES(NULL,200,'2000-11-11','nmw@qq.com',1,'牛魔王',8888);

3.设计思路

解读:
传统的方式操作数据库
1)得到 MySession 对象
2)调用 MyExecutor 的方法完成操作
3)MyExecutor 的连接是从 MyConfiguration 获取Mybatis 操作数据库的方式
1)得到 MySession 对象
2)不直接调用 MyExecutor 的方法
3)而是通过 MyMapperProxy 获取 Mapper 对象
4)调用 Mapper 的方法,完成对数据库的操作
5)Mapper 最终还是动态代理方式,使用 MyExecutor 的方法完成操作
6)这里比较麻烦的就是 MyMapperProxy 的动态代理机制如何实现
4.任务阶段1
阶段1任务:通过配置文件,获取数据库连接
4.1分析

4.2代码实现
(1)在src 的 resources目录下创建 my-config.xml,模拟原生的 mybatis 配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<database>
<!--配置连接数据库的信息-->
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/li_mybatis?
useSSL=true&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</database>
(2)创建 MyConfiguration 类,用来读取xml文件,建立连接
因为这里重点是实现 Mybatis 的底层机制,为了简化操作,就不使用数据库连接池了,直接使用原生的connection 连接
package com.li.limybatis.sqlsession;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
/**
* @author 李
* @version 1.0
* 用来读取xml文件,建立连接
*/
public class MyConfiguration {
//属性-类的加载器
private static ClassLoader loader = ClassLoader.getSystemClassLoader();
//读取xml文件并处理
public Connection build(String resource) {
Connection connection = null;
try {
//先加载配置文件 my-config.xml,获取对应的InputStream
InputStream stream = loader.getResourceAsStream(resource);
//解析 my-config.xml文件
SAXReader reader = new SAXReader();
Document document = reader.read(stream);
//获取 xml文件的根元素 <database>
Element root = document.getRootElement();
System.out.println("root=" + root);
//根据root解析,获取Connection
connection = evalDataSource(root);
} catch (Exception e) {
e.printStackTrace();
}
return connection;
}
//解析 my-config.xml 的信息,并返回 Connection
private Connection evalDataSource(Element node) {
if (!"database".equals(node.getName())) {
throw new RuntimeException("root节点应该是<database>");
}
//连接DB的必要参数
String driverClassName = null;
String url = null;
String username = null;
String password = null;
//遍历node下的子节点,获取其属性值
for (Object item : node.elements("property")) {
//i就是对应的 property节点
Element i = (Element) item;
//property节点的 name属性的值
String name = i.attributeValue("name");
//property节点的 value属性的值
String value = i.attributeValue("value");
//判断值是否为空
if (name == null || value == null) {
throw new RuntimeException("property节点没有设置name或value属性!");
}
switch (name) {
case "url":
url = value;
break;
case "username":
username = value;
break;
case "driverClassName":
driverClassName = value;
break;
case "password":
password = value;
break;
default:
throw new RuntimeException("属性名没有匹配到..");
}
}
//获取连接
Connection connection = null;
try {
Class.forName(driverClassName);
connection = DriverManager.getConnection(url, username, password);
} catch (Exception e) {
e.printStackTrace();
}
return connection;
}
}
5.任务阶段2
阶段2任务:通过实现执行器机制,对数据表进行操作
5.1分析
我们把对数据库的操作封装到一套Executor机制中,程序具有更好的拓展性,结构更加清晰。这里我们先实现传统的方式连接数据库,即通过MyExecutor直接操作数据库。

5.2代码实现
(1)生成 entity 类 Monster.java
package com.li.entity;
import lombok.*;
import java.util.Date;
/**
* @author 李
* @version 1.0
* Monster类和 monster有映射关系
*
* 注解说明:
* @Getter 给所有属性生成 getter方法
* @Setter 给所有属性生成 setter方法
* @ToString 生成toString方法
* @NoArgsConstructor 生成一个无参构造器
* @AllArgsConstructor 生成一个全参构造器
* @Data 会生成上述除了无参/全参构造器的所有方法,此外还会生成equals,hashCode等方法
*/
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Monster {
private Integer id;
private Integer age;
private String name;
private String email;
private Date birthday;
private double salary;
private Integer gender;
}
(2)Executor 接口
package com.li.limybatis.sqlsession;
/**
* @author 李
* @version 1.0
*/
public interface Executor {
//泛型方法
public <T> T query(String statement, Object parameter);
}
(3)执行器实现类 MyExecutor.java
package com.li.limybatis.sqlsession;
import com.li.entity.Monster;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* @author 李
* @version 1.0
*/
public class MyExecutor implements Executor {
private MyConfiguration myConfiguration = new MyConfiguration();
/**
* 根据sql,返回查询结果
*
* @param sql
* @param parameter
* @param <T>
* @return
*/
@Override
public <T> T query(String sql, Object parameter) {
//获取连接对象
Connection connection = getConnection();
//查询返回的结果集
ResultSet set = null;
PreparedStatement pre = null;
try {
//构建PreparedStatement对象
pre = connection.prepareStatement(sql);
//设置参数,如果参数多,可以使用数组处理
pre.setString(1, parameter.toString());
//查询返回的结果集
set = pre.executeQuery();
//把结果集的数据封装到对象中-monster
//说明:这里做了简化处理,认为返回的结果就是一个monster记录,完善的写法应该使用反射机制
Monster monster = new Monster();
//遍历结果集,将数据封装到monster对象中
while (set.next()) {
monster.setId(set.getInt("id"));
monster.setName(set.getString("name"));
monster.setEmail(set.getString("email"));
monster.setAge(set.getInt("age"));
monster.setGender(set.getInt("gender"));
monster.setBirthday(set.getDate("birthday"));
monster.setSalary(set.getDouble("salary"));
}
return (T) monster;
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (set != null) {
set.close();
}
if (pre != null) {
pre.close();
}
if (connection != null) {
connection.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
//编写方法,通过myConfiguration对象返回连接
private Connection getConnection() {
Connection connection = myConfiguration.build("my-config.xml");
return connection;
}
}
(4)进行测试
@Test
public void query() {
Executor executor = new MyExecutor();
Monster monster =
(Monster) executor.query("select * from monster where id = ?", 1);
System.out.println("monster--" + monster);
}
测试结果:

6.任务阶段3
阶段3任务:将执行器封装到SqlSession
6.1代码实现
(1)创建 MySqlSession 类,将执行器封装到SqlSession中。
package com.li.limybatis.sqlsession;
/**
* @author 李
* @version 1.0
* MySqlSession:搭建Configuration(连接)和Executor之间的桥梁
*/
public class MySqlSession {
//执行器
private Executor executor = new MyExecutor();
//配置
private MyConfiguration myConfiguration = new MyConfiguration();
//编写方法selectOne,返回一条记录
public <T> T selectOne(String statement,Object parameter){
return executor.query(statement, parameter);
}
}
(2)测试
@Test
public void selectOne() {
MySqlSession mySqlSession = new MySqlSession();
Monster monster =
(Monster) mySqlSession.selectOne("select * from monster where id=?", 1);
System.out.println("monster=" + monster);
}
测试结果:

day02-自己实现Mybatis底层机制-01的更多相关文章
- day05-SpringMVC底层机制简单实现-01
SpringMVC底层机制简单实现-01 主要完成:核心分发控制器+Controller和Service注入容器+对象自动装配+控制器方法获取参数+视图解析+返回JSON格式数据 1.搭建开发环境 创 ...
- 聊聊MyBatis缓存机制【美团-推荐】
聊聊MyBatis缓存机制 2018年01月19日 作者: 凯伦 文章链接 18778字 38分钟阅读 前言 MyBatis是常见的Java数据库访问层框架.在日常工作中,开发人员多数情况下是使用My ...
- 聊聊MyBatis缓存机制
https://tech.meituan.com/mybatis_cache.html 前言 MyBatis是常见的Java数据库访问层框架.在日常工作中,开发人员多数情况下是使用MyBatis的默认 ...
- 【转】MyBatis缓存机制
转载:https://blog.csdn.net/bjweimengshu/article/details/79988252. 本文转载自公众号 美团技术点评 前言 MyBatis是常见的Java数据 ...
- MyBatis缓存机制[NO]
前言 MyBatis是常见的Java数据库访问层框架.在日常工作中,开发人员多数情况下是使用MyBatis的默认缓存配置,但是MyBatis缓存机制有一些不足之处,在使用中容易引起脏数据,形成一些潜在 ...
- mybatis底层源码分析之--配置文件读取和解析
现在企业级开发中ssm是很常见的技术标配,mybatis比hibernate轻量了很多,而且学习成本相对较低,简单易上手. 那么,问题来了,简单好用的mybatis底层到底是如何实现的呢?都使用了什么 ...
- java 反射机制01
// */ // ]]> java反射机制01 Table of Contents 1 反射机制 2 反射成员 2.1 java.lang.Class 2.2 Constructor 2.3 ...
- mybatis源代码分析:mybatis延迟加载机制改进
在上一篇博客<mybatis源代码分析:深入了解mybatis延迟加载机制>讲诉了mybatis延迟加载的具体机制及实现原理. 可以看出,如果查询结果对象中有一个属性是需要延迟加载的,那整 ...
- 《深入理解mybatis原理》 Mybatis初始化机制具体解释
对于不论什么框架而言.在使用前都要进行一系列的初始化,MyBatis也不例外. 本章将通过下面几点具体介绍MyBatis的初始化过程. 1.MyBatis的初始化做了什么 2. MyBatis基于XM ...
- mybatis缓存机制
目录 mybatis缓存机制 Executor和缓存 一级缓存 小结 二级缓存 小结 mybatis缓存机制 mybatis支持一.二级缓存来提高查询效率,能够正确的使用缓存的前提是熟悉mybatis ...
随机推荐
- 每日一道Java面试题:说一说Java中的泛型?
写在开头 今天的每日一道Java面试题聊的是Java中的泛型,泛型在面试的时候偶尔会被提及,频率不是特别高,但在日后的开发工作中,却是是个高频词汇,因此,我们有必要去认真的学习它. 泛型的定义 什么是 ...
- 【链表】双向链表的介绍和基本操作(C语言实现)【保姆级别详细教学】
双向链表 文章目录 前言 双向链表的基本介绍 一些链表的分类 带头双向循环链表的基本结构 双向链表的实现 结点的定义.头指针的创建 开辟结点接口 初始化头结点接口 打印接口 尾插接口 尾删接口 头插接 ...
- 在Java中,对 byte 和 short 类型 进行位操作的时候,严重留意事项
总结:在java中,对byte和short类型的 右移操作 必须先进行 & 0xff 后再右移,避免byte或short是负数的情况下,导致 右移操作前 自动升为int,前面补了很多1,此时右 ...
- 听说有 Hugging Face 陪伴的春节,是这样的…
辞旧迎新春节到,家家户户好热闹.Hugging Face 中国团队成员祝各位社区成员们新春快乐,万事如意! 过去的一年我们持续看到 AI 技术的腾飞和发展,以及诸多机构为开源 AI 作出巨大的贡献.非 ...
- JS leetcode 合并两个有序数组 解题分析
壹 ❀ 引 今天做的一题是前两周博客园一粉丝在面试360时遇到的算法题,题目来自leetcode88. 合并两个有序数组,理解起来可能有些费劲,不过我尽量用图的形式给大家解释它,题目描述如下: 给你两 ...
- JS 这一次彻底理解插入排序
壹 ❀ 引 在前两篇排序文章中,我们分别介绍了冒泡排序与选择排序,趁热打铁,我们接着聊插入排序.老实说,在分析排序过程中头脑很清楚,过后再尝试写出排序代码还有点坎坷...可能是我脑瓜子不太机灵的问题, ...
- NC20277 [SCOI2010]字符串
题目链接 题目 题目描述 lxhgww最近接到了一个生成字符串的任务,任务需要他把n个1和m个0组成字符串,但是任务还要求在组成的字符串中,在任意的前k个字符中,1的个数不能少于0的个数.现在lxhg ...
- Python 中global 关键字理解
Python中的global关键字,你了解吗? 前言 今天来了解下 Python 中的 global 关键字. Python变量的作用域 实战案例演示之前,先要了解下 Python 的作用域. Pyt ...
- Slot 的含义
Slot 解释 1)slot就是槽的意思,是一个资源单位,只有给task分配了一个slot之后,这个task才可以运行.slot分两种,map slot沪蓉reduce slot.另外,slot是一个 ...
- 如何查看当前Ubuntu系统的版本-【转 https://www.cnblogs.com/chenxiaomeng/p/10038492.html】
如何查看当前Ubuntu系统的版本 一.系统位数 在控制台输入:sudo uname --m 如果显示i686,则表示安装了32位操作系统 如果显示 x86_64,则表示安装了64位操作系统 sudo ...