😀前言
本篇博文是学习过程中的笔记和对于MyBatis底层机制的分析思路,希望能够给您带来帮助😊
🏠个人主页:晨犀主页
🧑个人简介:大家好,我是晨犀,希望我的文章可以帮助到大家,您的满意是我的动力😉😉
💕欢迎大家:这里是CSDN,我总结知识的地方,欢迎来到我的博客,感谢大家的观看🥰
如果文章有什么需要改进的地方还请大佬不吝赐教 先在次感谢啦😊
文章目录
- 自己实现MyBatis 底层机制[上]
- MyBatis 整体架构分析
- Mybatis 核心框架示意图
- 核心框架示意图的解读
- 搭建MyBatis 底层机制开发环境
- 1、创建Maven 项目nlc-mybatis
- 2、修改nlc-mybatis\pom.xml
- 3、创建数据库和表
- 4、到此: 项目开发环境搭建完成
- Nlc-Mybatis 的设计思路
- Mybatis 的底层实现设计
- 1. 传统方式操作数据库
- 2.MyBatis操作数据库的方式分析
- 自己实现MyBatis 底层机制
- 实现任务阶段1- 完成读取配置文件,得到数据库连接
- 分析示意图
- 代码实现
- 完成测试
- 测试效果
- 实现任务阶段2- 编写执行器,输入SQL 语句,完成操作
- 分析示意图
- 代码实现
- 完成测试
- 😄总结
自己实现MyBatis 底层机制[上]
MyBatis 整体架构分析
Mybatis 核心框架示意图
核心框架示意图的解读
- mybatis 的核心配置文件
mybatis-config.xml: 进行全局配置,全局只能有一个这样的配置文件
XxxMapper.xml 配置多个SQL,可以有多个XxxMappe.xml 配置文件 - 通过mybatis-config.xml 配置文件得到SqlSessionFactory
- 通过SqlSessionFactory 得到SqlSession,用SqlSession 就可以操作数据了
- SqlSession 底层是Executor(执行器), 有两个重要的实现类基本执行器和带缓存功能的执行器, 有很多方法
- MappedStatement 是通过XxxMapper.xml 中定义, 生成的statement 对象
- 参数输入执行并输出结果集, 无需手动判断参数类型和参数下标位置, 且自动将结果集映射为Java 对象
搭建MyBatis 底层机制开发环境
1、创建Maven 项目nlc-mybatis
前面快速入门有创建步骤,这里不在描述。
2、修改nlc-mybatis\pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.nlc</groupId><artifactId>nlc-mybatis</artifactId><version>1.0-SNAPSHOT</version><!--定义编译器 / source / target 版本即可--><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><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>
</project>
3、创建数据库和表
CREATE DATABASE `nlc_mybatis`;
USE `nlc_mybatis`;
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,`name` VARCHAR(255) NOT NULL,`salary` DOUBLE NOT NULL,PRIMARY KEY (`id`)
) CHARSET=utf8
INSERT INTO `monster` VALUES(NULL, 200, '2000-11-11', 'nmw@sohu.com', 1,'牛魔王', 8888.88)
4、到此: 项目开发环境搭建完成
Nlc-Mybatis 的设计思路
Mybatis 的底层实现设计
1. 传统方式操作数据库
-
得到Session对象
-
调用Executor的方法完成操作
-
Executor的连接是从Configuration获取的
2.MyBatis操作数据库的方式分析
- 得到Session
- 不用直接调用Executor的方法完成操作
- 通过MapperProxy获取Mapper对象
- 调用Mapper的方法,完成数据库的操作
- Mapper最终还是动态代理方式,使用Executor的方法完成操作
- MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
自己实现MyBatis 底层机制
实现任务阶段1- 完成读取配置文件,得到数据库连接
通过配置文件,获取数据库连接。
分析示意图
代码实现
- 创建nlc-mybatis\src\main\resources\nlc_config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<database><!--配置连接数据库的信息--><property name="driverClassName" value="com.mysql.jdbc.Driver"/><!--配置连接mysql-url1. jdbc:mysql 协议2. 127.0.0.1:3306 : 指定连接mysql的ip+port3. mybatis: 连接的DB4. useSSL=true 表示使用安全连接5. & 表示 & 防止解析错误6. useUnicode=true : 使用unicode 作用是防止编码错误7. characterEncoding=UTF-8 指定使用utf-8, 防止中文乱码8. 不要背,直接使用即可--><property name="url" value="jdbc:mysql://localhost:3306/nlc_mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8"/><property name="username" value="root"/><property name="password" value="123456"/>
</database>
- 创建nlc-mybatis\sqlsession\NlcConfiguration.java
public class NlcConfiguration {//属性-类的加载器private static ClassLoader loader =ClassLoader.getSystemClassLoader();//读取xml文件信息,并处理public Connection build(String resource) {Connection connection = null;try {//加载配置nlc_mybatis.xml 获取到对应的InputStreamInputStream stream = loader.getResourceAsStream(resource);SAXReader reader = new SAXReader();Document document = reader.read(stream);//获取到nlc_mybatis.xml 的根元素 <database>Element root = document.getRootElement();System.out.println("root=" + root);//解析root元素,返回Connection connection = evalDataSource(root);} catch (Exception e) {e.printStackTrace();}return connection;}//方法会解析nlc_config.xml 信息,并返回Connection//eval: 评估/解析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")) {Element i = (Element) item;//i 就是 对应property节点String name = i.attributeValue("name");String value = i.attributeValue("value");//判断是否得到name 和 valueif (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 {//要求JVM查找并加载指定的类到内存中,此时将"com.mysql.jdbc.Driver" 当做参数传入,// 就是告诉JVM,去"com.mysql.jdbc"这个路径下找Driver类,将其加载到内存中。Class.forName(driverClassName);connection = DriverManager.getConnection(url,username,password);} catch (Exception e) {e.printStackTrace();}return connection; //返回Connection}}
完成测试
- 编写nlc-mybatis\src\test\java\com\nlc\test\NlcMybatisTest.java
public class NlcMyBatisTest {@Testpublic void build() {NlcConfiguration nlcConfiguration = new NlcConfiguration();Connection connection = nlcConfiguration.build("nlc_mybatis.xml");System.out.println("connection--" + connection);}
}
测试效果
实现任务阶段2- 编写执行器,输入SQL 语句,完成操作
说明:通过实现执行器机制,对数据表操作。
分析示意图
说明:我们把对数据库的操作,会封装到一套Executor 机制中,程序具有更好的扩展性,结构更加清晰.
代码实现
- 创建nlc-mybatis\src\main\java\com\nlc\entity\Monster.java
如果使用@Data注解需要全参构造器可以添加@AllArgsConstructor,但是无参构造器必须要显示调用,否则会被全参构造器覆盖。
/*** Monster 和 monster表有映射关系* @Getter 就会给所有属性 生成对应的getter* @Setter 就会给所有属性 生成对应的setter* @ToString 生成 toString...* @NoArgsConstructor 生成无参构造器* @AllArgsConstructor 生成要给全参构造器* @Data 会生成上面除全参构造器的所有注解* 如何选择主要还是看自己需要*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Monster {private Integer id;private Integer age;private String name;private String email;private Date birthday;private double salary;private Integer gender;}
- 创建nlc-mybatis\src\main\java\com\nlc\nlcmybatis\sqlsession\Executor.java
public interface Executor {//泛型方法public <T> T query(String statement, Object parameter);
}
- 创建nlc-mybatis\src\main\java\com\nlc\nlcmybatis\sqlsession\NlcExecutor.java
public class NlcExecutor implements Executor {//属性private NlcConfiguration nlcConfiguration =new NlcConfiguration();// 根据 sql 查找结果@Overridepublic <T> T query(String sql, Object parameter) {//得到连接ConnectionConnection connection = getConnection();//查询返回的结果集ResultSet set = null;PreparedStatement pre = null;try {pre = connection.prepareStatement(sql);//设置参数, 如果参数多, 可以使用数组处理.pre.setString(1, parameter.toString());set = pre.executeQuery();//把set数据封装到对象-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 throwables) {throwables.printStackTrace();} finally {try {if (set != null) {set.close();}if (pre != null) {pre.close();}if (connection != null) {connection.close();}} catch (Exception throwables) {throwables.printStackTrace();}}return null;}//编写方法,通过NlcConfiguration对象,返回连接private Connection getConnection() {Connection connection = nlcConfiguration.build("nlc_mybatis.xml");return connection;}
}
完成测试
- 修改nlc-mybatis\src\test\java\com\nlc\test\NlcMybatisTest.java , 增加测试方法
@Testpublic void query() {Executor executor = new NlcExecutor();Monster monster =executor.query("select * from monster where id=?", 1);System.out.println("monster-- " + monster);}
- 测试效果
😄总结
- 了解底层的机制可以帮助我们更好的学习,阅读优秀的源码可以增长我们的功力。
- 适当的debug可以解决我们的疑惑,底层是一个非常庞大的集成,把握主干就可以了。
- 过于追根究底只会影响自己的心绪,会耗费大量时间精力。
- 如果自己感兴趣的话,可以多研究一下,会发现其中的乐趣,点到即止。
文章到这里就结束了,如果有什么疑问的地方请指出,诸大佬们一起来评论区一起讨论😁
希望能和诸大佬们一起努力,今后我们一起观看感谢您的阅读🍻
如果帮助到您不妨3连支持一下,创造不易您们的支持是我的动力🤞