MyBatis核心机制

devtools/2024/9/25 17:18:36/

实现MyBatis核心机制环境搭建

1.核心框架示意图

img

2.模块搭建

1.创建maven项目

CleanShot 2024-08-08 at 15.29.52@2x

2.引入依赖
<?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><parent><groupId>com.sunxiansheng</groupId><artifactId>core_mechanisms</artifactId><version>1.0-SNAPSHOT</version></parent><artifactId>sun-mybatis</artifactId><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><!-- 解析xml --><dependency><groupId>dom4j</groupId><artifactId>dom4j</artifactId><version>1.6.1</version></dependency><!-- mysql --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.22</version></dependency><!-- lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.30</version></dependency><!-- junit --><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version></dependency></dependencies></project>
3.连接数据库
4.数据库表设计
CREATE DATABASE `sun_mybatis`;
USE `sun_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` (`age`, `birthday`, `email`, `gender`, `name`, `salary`)
VALUES (25, '1998-01-15', 'example@example.com', 1, 'John Doe', 50000.00);

3.设计图

img

读取配置文件,得到数据库连接

1.目录

CleanShot 2024-08-08 at 16.57.51@2x

2.sun-config.xml 配置文件

<?xml version="1.0" encoding="utf-8" ?>
<!-- 配置数据库连接信息 -->
<database><!-- 驱动 --><property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/><!-- 连接地址 --><property name="url" value="jdbc:mysql://bj--grp-.sql.tencentcdb.com:24169/sun_mybatis?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>    <!-- 用户名 --><!-- 用户名 --><property name="username" value=""/><!-- 密码 --><property name="password" value=""/>
</database>

3.SunConfiguration.java 读取配置文件获取数据库连接

package com.sunxiansheng.mybatis.sqlsession;import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.List;/*** Description: 读取xml配置文件** @Author sun* @Create 2024/8/8 16:25* @Version 1.0*/
public class SunConfiguration {private static final Logger log = LoggerFactory.getLogger(SunConfiguration.class);private static ClassLoader loader = ClassLoader.getSystemClassLoader();/*** 读取xml文件** @param resource 这个必须是类路径下的文件路径,不用带第一个斜杠,sun-config.xml就可以* @return*/public Connection build(String resource) {InputStream resourceAsStream = loader.getResourceAsStream(resource);// 使用dom4j解析配置文件SAXReader saxReader = new SAXReader();try {// 获取文档Document document = saxReader.read(resourceAsStream);// 根元素Element rootElement = document.getRootElement();// 根元素下的所有property元素List<Element> property = rootElement.elements("property");// 初始化连接信息String driverClassName = null;String url = null;String username = null;String password = null;// 遍历property元素获取连接信息for (Element element : property) {String name = element.attributeValue("name");String value = element.attributeValue("value");switch (name) {case "driverClassName":driverClassName = value;log.info("加载驱动类:{}", value);break;case "url":url = value;log.info("加载url:{}", value);break;case "username":username = value;log.info("加载用户名:{}", value);break;case "password":password = value;log.info("加载密码:{}", value);break;default:throw new RuntimeException("未知的property属性名");}}// 类加载驱动类Class.forName(driverClassName);return DriverManager.getConnection(url, username, password);} catch (DocumentException | ClassNotFoundException | SQLException e) {log.error("解析xml配置文件失败", e);throw new RuntimeException(e);}}}

4.SunConfigurationTest.java 测试

package com.sunxiansheng.mybatis.sqlsession;import org.junit.Test;import java.sql.Connection;/*** Description: 读取xml配置文件** @Author sun* @Create 2024/8/8 16:25* @Version 1.0*/
public class SunConfigurationTest {@Testpublic void build() {SunConfiguration sunConfiguration = new SunConfiguration();Connection build = sunConfiguration.build("sun-config.xml");System.out.println("build = " + build);}}

使用SunExecutor来执行SQL

1.目录

CleanShot 2024-08-09 at 13.02.47@2x

2.Monster.java

package com.sunxiansheng.mybatis.entity;import lombok.Data;import java.io.Serializable;
import java.util.Date;@Data
public class Monster implements Serializable {private Integer id;private Integer age;private Date birthday;private String email;private Byte gender;private String name;private Double salary;private static final long serialVersionUID = 1L;
}

3.Executor.java

package com.sunxiansheng.mybatis.sqlsession;/*** Description: 执行器接口** @Author sun* @Create 2024/8/9 12:37* @Version 1.0*/
public interface Executor {public <T> T query(String sql, Object parameter);}

4.SunExecutor.java 具体的执行器

package com.sunxiansheng.mybatis.sqlsession;import com.sunxiansheng.mybatis.entity.Monster;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;/*** Description: 执行器** @Author sun* @Create 2024/8/9 12:39* @Version 1.0*/
public class SunExecutor implements Executor{public static final SunConfiguration configuration = new SunConfiguration();private static final Logger log = LoggerFactory.getLogger(SunExecutor.class);@Overridepublic <T> T query(String sql, Object parameter) {// 获取DB连接Connection connection = configuration.build("sun-config.xml");log.info("获取连接:{}", connection);ResultSet resultSet = null;PreparedStatement preparedStatement = null;// 实体类Monster monster = new Monster();try {// sql预处理preparedStatement = connection.prepareStatement(sql);// 填参数preparedStatement.setString(1, parameter.toString());// 执行查询resultSet = preparedStatement.executeQuery();// 将结果封装到实体类中(写死了)while (resultSet.next()) {monster.setId(resultSet.getInt("id"));monster.setAge(resultSet.getInt("age"));monster.setBirthday(resultSet.getDate("birthday"));monster.setEmail(resultSet.getString("email"));monster.setGender(resultSet.getByte("gender"));monster.setName(resultSet.getString("name"));monster.setSalary(resultSet.getDouble("salary"));}log.info("查询结果:{}", monster);} catch (Exception e) {throw new RuntimeException(e);} finally {// 关闭资源try {if (resultSet != null) {resultSet.close();}if (preparedStatement != null) {preparedStatement.close();}if (connection != null) {connection.close();}} catch (Exception e) {throw new RuntimeException(e);}}// 这里的T是由类型推断来指定的,也就是使用什么类型接受,就返回什么类型return (T) monster;}}

5.SunConfigurationTest.java 测试

@Test
public void executor() {SunExecutor sunExecutor = new SunExecutor();// 这里是使用的类型推断自动转换类型Monster monster = sunExecutor.query("select * from monster where id = ?", 1);
}

将SqlSession封装到执行器

1.目录

CleanShot 2024-08-09 at 13.17.02@2x

2.SunSqlSession.java

package com.sunxiansheng.mybatis.sqlsession;/*** Description: SqlSession** @Author sun* @Create 2024/8/9 13:08* @Version 1.0*/
public class SunSqlSession {// 执行器(操作db)private Executor executor = new SunExecutor();// 配置(用于获取连接)private SunConfiguration configuration = new SunConfiguration();public <T> T selectOne(String statement, Object parameter) {return executor.query(statement, parameter);}
}

3.测试

@Test
public void selectOne() {SunSqlSession sunSqlSession = new SunSqlSession();Monster monster = sunSqlSession.selectOne("select * from monster where id = ?", 1);System.out.println("monster = " + monster);
}

开发MapperBean和Function

1.目录

CleanShot 2024-08-09 at 13.39.36@2x

2.MonsterMapper.java

package com.sunxiansheng.mapper;import com.sunxiansheng.entity.Monster;/*** Description: MonsterMapper** @Author sun* @Create 2024/8/9 13:21* @Version 1.0*/
public interface MonsterMapper {public Monster getMonsterById(Integer id);}

3.MonsterMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<mapper namespace="com.sunxiansheng.mapper.MonsterMapper"><select id="getMonsterById" resultType="com.sunxiansheng.entity.Monster">select * from monster where id = ?</select>
</mapper>

4.Function.java 记录对应的mapper.xml的方法信息

package com.sunxiansheng.mybatis.config;import lombok.Data;/*** Description: 记录对应的mapper.xml的方法信息** @Author sun* @Create 2024/8/9 13:31* @Version 1.0*/
@Data
public class Function {/*** sql类型*/private String sqlType;/*** 方法名*/private String funcName;/*** SQL语句*/private String sql;/*** 返回类型*/private Object resultType;}

5.MapperBean.java 将mapper接口信息进行封装

package com.sunxiansheng.mybatis.config;import java.util.List;/*** Description: 将mapper接口信息进行封装** @Author sun* @Create 2024/8/9 13:35* @Version 1.0*/
public class MapperBean {/*** 接口全路径*/private String interfaceName;/*** 方法列表*/private List<Function> functions;}

读取xml文件解析MapperBean

1.目录

CleanShot 2024-08-09 at 14.03.00@2x

2.Function.java 加链式调用注解

CleanShot 2024-08-09 at 14.03.31@2x

3.MapperBean.java 加Data注解

CleanShot 2024-08-09 at 14.03.51@2x

4. SunConfiguration.java 增加解析MapperBean的方法

/*** 读取mapper.xml文件,构建MapperBean* @param path* @return*/
public MapperBean readMapper(String path) {// 要返回的结果MapperBean mapperBean = new MapperBean();// 使用类加载器读取配置文件ClassLoader loader = ClassLoader.getSystemClassLoader();InputStream resourceAsStream = loader.getResourceAsStream(path);// 解析xml文件SAXReader saxReader = new SAXReader();try {// 获取文档Document document = saxReader.read(resourceAsStream);// 获取根元素Element rootElement = document.getRootElement();// 获取根元素的属性:namespaceString namespace = rootElement.attributeValue("namespace");// 获取子元素List<Element> elements = rootElement.elements();// 遍历子元素List<Function> functions = elements.stream().map(element -> {Function function = new Function();// 获取sql类型String sqlType = element.getName();// 获取方法名String funcName = element.attributeValue("id");// 获取返回类型String resultType = element.attributeValue("resultType");// 获取sql语句String sql = element.getTextTrim();// 封装到Function对象中function.setSqlType(sqlType).setFuncName(funcName).setResultType(resultType).setSql(sql);return function;}).collect(Collectors.toList());// 构建MapperBeanmapperBean.setInterfaceName(namespace);mapperBean.setFunctions(functions);} catch (DocumentException e) {throw new RuntimeException(e);}// 输出mapperBean的日志log.info("mapperBean:{}", mapperBean);return mapperBean;
}

5.SunConfigurationTest.java 测试

    @Testpublic void readMapper() {SunConfiguration sunConfiguration = new SunConfiguration();sunConfiguration.readMapper("MonsterMapper.xml");}

CleanShot 2024-08-09 at 14.05.29@2x

动态代理Mapper方法

1.目录

CleanShot 2024-08-09 at 14.58.13@2x

2.MapperProxyFactory.java 代理工厂,可以获取接口的代理对象

package com.sunxiansheng.mybatis.sqlsession;import java.lang.reflect.Proxy;/*** Description: 代理工厂** @Author sun* @Create 2024/8/9 14:14* @Version 1.0*/
public class MapperProxyFactory {public static <T> T getMapperProxy(Class<T> clazz, SunSqlSession sunSqlSession, SunConfiguration sunConfiguration) {return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, new SunMapperProxy(sunSqlSession, clazz, sunConfiguration));}}

3.SunMapperProxy.java 代理逻辑

package com.sunxiansheng.mybatis.sqlsession;import com.sunxiansheng.mybatis.config.Function;
import com.sunxiansheng.mybatis.config.MapperBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.List;/*** Description: 代理Mapper接口** @Author sun* @Create 2024/8/9 14:08* @Version 1.0*/
public class SunMapperProxy implements InvocationHandler {private static final Logger log = LoggerFactory.getLogger(SunMapperProxy.class);/*** 用于连接数据库和执行sql*/private SunSqlSession sunSqlSession;/*** mapper文件名*/private String mapperFile;/*** 配置类(可以根据mapper文件名来获取MapperBean)*/private SunConfiguration sunConfiguration;public SunMapperProxy(SunSqlSession sunSqlSession, Class<?> mapperFile, SunConfiguration sunConfiguration) {this.sunSqlSession = sunSqlSession;this.mapperFile = mapperFile.getSimpleName() + ".xml";this.sunConfiguration = sunConfiguration;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 获取方法名String methodName = method.getName();// 获取MapperBeanMapperBean readMapper = sunConfiguration.readMapper(mapperFile);// 获取Function列表,过滤出需要执行的FunctionList<Function> functions = readMapper.getFunctions();Function targetFunction = null;for (Function function : functions) {if (methodName.equals(function.getFuncName())) {targetFunction = function;break;}}// 获取Function的信息String sqlType = targetFunction.getSqlType();String sql = targetFunction.getSql();// 将sql的第一个问号使用参数替换,使用字符串处理// 根据sqlType执行sqlif (sqlType.equals("select")) {Object selected = sunSqlSession.selectOne(sql, args[0]);log.info("invoke查询结果:{}", selected);return selected;}return null;}
}

4.测试

@Test
public void getResult() {MonsterMapper mapperProxy = MapperProxyFactory.getMapperProxy(MonsterMapper.class, new SunSqlSession(), new SunConfiguration());Monster monsterById = mapperProxy.getMonsterById(1);
}

http://www.ppmy.cn/devtools/98674.html

相关文章

C++ 内嵌 python 解释器

AI 提供 #include <Python.h> #include <map> #include <string>int main() {// 初始化 Python 解释器Py_Initialize();// 创建一个 C std::mapstd::map<std::string, int> myMap {{"apple", 3},{"banana", 5},{"orange&quo…

Linux系统性能调优指南-定期维护

目录 定期维护 日志管理 示例 磁盘维护 示例 示例代码 日志管理示例 磁盘维护示例 定期维护 定期维护对于保持Linux系统的稳定性和性能至关重要。这包括日志管理以及磁盘维护等方面。下面详细介绍这些方面的配置和优化方法。 日志管理 日志文件随着时间的积累可能会占用大量的磁…

Lumos学习王佩丰Excel第十二讲:Match与Index

一、函数语法 1、vlookup的局限 举个栗子&#xff0c;VLOOKUP不能做到从右推左&#xff1a; 由此看来&#xff0c;使用vlookup函数&#xff0c;表格范围要遵循从左到右的顺序&#xff0c;左为自变量&#xff0c;右为因变量&#xff1b;而要解决这种场景的弊端&#xff0c;可以…

【Qwen2微调实战】LLaMA-Factory框架对Qwen2-7B模型的微调实践

系列篇章&#x1f4a5; No.文章1【Qwen部署实战】探索Qwen-7B-Chat&#xff1a;阿里云大型语言模型的对话实践2【Qwen2部署实战】Qwen2初体验&#xff1a;用Transformers打造智能聊天机器人3【Qwen2部署实战】探索Qwen2-7B&#xff1a;通过FastApi框架实现API的部署与调用4【Q…

【数据分析】数据的离中趋势之一 - 极差、分位距、平均差

一、极差 未分组或单项分组的数据&#xff1a;极差最大值 - 最小值已分组数据&#xff1a;极差最大组的上限 - 最下组的下限实际应用中&#xff0c;极差可用于检查产品质量的稳定性和进行质量控制。正常生产条件下&#xff0c;极差在一定范围内波动&#xff0c;如出现不正常情…

nodemon学习(一)简介、安装、配置、使用

nodemon用来监视node.js应用程序中的任何更改并自动重启服务,非常适合用在开发环境中。以前&#xff0c;我们开发一个node后端服务时&#xff0c;每次更改文件&#xff0c;均需重启一下&#xff0c;服务才能生效。这使我们的开发效率降低了很多。nodemon的出现&#xff0c;可以…

招聘|头部云厂商招 PG 核心骨干 DBA【上海】

我们的招聘专区又回来了&#xff01;&#x1f3c3; Bytebase 作为先进的数据库 DevOps 团队协同工具 &#x1f527;&#xff0c;用户群里汇聚了 &#x1f497; 业界优秀的 DBA&#xff0c;SRE&#xff0c;运维的同学们 &#x1f31f;。 上周用户群里有小伙伴发招聘信息 &…

Redhat8 搭建Zabbix6(二)优化

前端优化 取消URL后缀 默认URL为: http://serverip/zabbix 修改URL为:http://serverip ##修改路径 nano /etc/httpd/conf/httpd.conf#DocumentRoot "/var/www/html" DocumentRoot "/usr/share/zabbix"##重启web与zabbixserver服务 systemctl restart http…