手撸Mybatis(三)——收敛SQL操作到SqlSession

本专栏的源码:https://gitee.com/dhi-chen-xiaoyang/yang-mybatis。

引言

在上一章中,我们实现了读取mapper配置并构造相关的mapper代理对象,读取mapper.xml文件中的sql信息等操作,现在,在上一章的基础上,我们接着开始链接数据库,通过封装JDBC,来实现我们数据库操作。

数据库准备

我们创建一个user表,用于后续进行测试,user表的结构如下图所示:
image.png
user表的内容如下:
image.png

添加User类

我们根据表结构,创建对应的user类,user类的结构如下:

package com.yang.mybatis.test;import java.io.Serializable;
import java.util.Date;public class User implements Serializable {private Integer id;private String userName;private String password;private Integer age;private Date createTime;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public Date getCreateTime() {return createTime;}public void setCreateTime(Date createTime) {this.createTime = createTime;}
}
JDBC基础操作

在使用jdbc之前,我们先引入mysql的依赖

<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.27</version></dependency>

jdbc的操作,一般分为下面几个步骤:
1)加载JDBC驱动程序
2)创建数据库链接
3)创建一个preparedStatement
4)执行SQL语句
5)遍历数据集
6)处理异常,关闭JDBC对象资源
示例代码如下:

 public static void main(String[] args) throws SQLException {String url = "jdbc:mysql://localhost:3306/test?useSSL=false";String username = "用户名";String password = "密码";Connection conn = null;try {// 1. 加载驱动Class.forName("com.mysql.cj.jdbc.Driver");// 2. 创建数据库链接conn = DriverManager.getConnection(url, username, password);conn.setAutoCommit(false);// 3. 创建preparedStatementString sql = "select user_name from user where id = ?";PreparedStatement preparedStatement = conn.prepareStatement(sql);preparedStatement.setInt(1, 1);// 4. 执行sqlResultSet resultSet = preparedStatement.executeQuery();// 5. 遍历结果if (resultSet.next()) {System.out.println(resultSet.getString(1));}conn.commit();} catch (SQLException | ClassNotFoundException e) {conn.rollback();e.printStackTrace();} finally {// 6. 释放连接conn.close();}}

执行上述代码,结果如下:
image.png

将sql操作,收敛到SqlSession

在上一章,当我们调用mapper的方法时,最终是通过在MapperProxy中获取对应的MybatisStatement,然后打印出sql信息的,但是如果后续操作数据库是,也在MapperProxy中执行sql的话,不太方便管理。因此,我们添加一个IMybatisSqlSession类,后续对于数据库的操作,收敛到此类进行。
首先,我们添加IMybatisSqlSession:

package com.yang.mybatis.session;public interface IMybatisSqlSession {<T> T execute(String method, Object parameter);<T> T getMapper(Class<T> type);
}

然后添加其默认实现:

package com.yang.mybatis.session;import com.yang.mybatis.proxy.MapperProxyFactory;public class DefaultMybatisSqlSession implements IMybatisSqlSession {private MapperProxyFactory mapperProxyFactory;public DefaultMybatisSqlSession(MapperProxyFactory mapperProxyFactory) {this.mapperProxyFactory = mapperProxyFactory;}@Overridepublic <T> T execute(String method, Object parameter) {return (T)("你被代理了!" + method + ",入参:" + parameter);}@Overridepublic <T> T getMapper(Class<T> type) {return (T) mapperProxyFactory.newInstance(type, this);}
}

添加IMybatisSqlSession的工厂接口

package com.yang.mybatis.session;public interface MybatisSqlSessionFactory {IMybatisSqlSession openSession();
}

添加工厂的默认实现:

package com.yang.mybatis.session;import com.yang.mybatis.proxy.MapperProxyFactory;public class DefaultMybatisSqlSessionFactory implements IMybatisSqlSessionFactory {private MapperProxyFactory mapperProxyFactory;public DefaultMybatisSqlSessionFactory(MapperProxyFactory mapperProxyFactory) {this.mapperProxyFactory = mapperProxyFactory;}@Overridepublic IMybatisSqlSession openSession() {return new DefaultMybatisSqlSession(mapperProxyFactory);}
}

修改MapperProxyFactory,在新建MapperProxy的时候,将imybatisSqlSession传递给MapperProxy。

package com.yang.mybatis.proxy;import com.yang.mybatis.config.MybatisConfiguration;
import com.yang.mybatis.session.IMybatisSqlSession;import java.lang.reflect.Proxy;public class MapperProxyFactory {private MybatisConfiguration mybatisConfiguration;public MapperProxyFactory(MybatisConfiguration mybatisConfiguration) {this.mybatisConfiguration = mybatisConfiguration;}public Object newInstance(Class mapperType, IMybatisSqlSession mybatisSqlSession) {MapperProxy mapperProxy = new MapperProxy(mapperType, mybatisSqlSession);return Proxy.newProxyInstance(mapperType.getClassLoader(),new Class[]{mapperType},mapperProxy);}public MybatisConfiguration getMybatisConfiguration() {return mybatisConfiguration;}public void setMybatisConfiguration(MybatisConfiguration mybatisConfiguration) {this.mybatisConfiguration = mybatisConfiguration;}
}

然后修改MapperProxy,在真正执行的时候,通过iMybatisSqlSession的execute,来执行sql操作,以此实现sql操作由iMybatisSqlSession来统一管理。

package com.yang.mybatis.proxy;import com.yang.mybatis.session.IMybatisSqlSession;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;public class MapperProxy<T> implements InvocationHandler {private Class<T> mapperInterface;private IMybatisSqlSession sqlSession;public MapperProxy(Class<T> mapperInterface, IMybatisSqlSession mybatisSqlSession) {this.mapperInterface = mapperInterface;this.sqlSession = mybatisSqlSession;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if (Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, args);}String methodName = this.mapperInterface.getName() + "." + method.getName();return sqlSession.execute(methodName, args);}
}

最后,我们添加sqlSession工厂类的创建者,通过创建者模式,来创建SqlSession工厂

package com.yang.mybatis.session;import com.yang.mybatis.config.MybatisConfiguration;
import com.yang.mybatis.config.MybatisSqlStatement;
import com.yang.mybatis.config.parser.IMybatisConfigurationParser;
import com.yang.mybatis.config.parser.IMybatisMapperParser;
import com.yang.mybatis.proxy.MapperProxyFactory;import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;public class MybatisSqlSessionFactoryBuilder {private IMybatisConfigurationParser mybatisConfigurationParser;private IMybatisMapperParser mybatisMapperParser;private String configPath;public IMybatisSqlSessionFactory buildSqlSessionFactory() {if (configPath == null || configPath.isEmpty()) {throw new RuntimeException("配置文件路径不合法==========");}if (this.mybatisMapperParser == null || this.mybatisConfigurationParser == null) {throw new RuntimeException("缺少解析器=======");}MybatisConfiguration mybatisConfiguration = mybatisConfigurationParser.parser(configPath);List<String> mapperPaths = mybatisConfiguration.getMapperPaths();for (String mapperPath : mapperPaths) {List<MybatisSqlStatement> mybatisSqlStatements = this.mybatisMapperParser.parseMapper(mapperPath);Map<String, List<MybatisSqlStatement>> mapperNameGroupMap = mybatisSqlStatements.stream().collect(Collectors.groupingBy(MybatisSqlStatement::getNamespace));for (Map.Entry<String, List<MybatisSqlStatement>> entry : mapperNameGroupMap.entrySet()) {String mapperName = entry.getKey();List<MybatisSqlStatement> sqlSessionList = entry.getValue();mybatisConfiguration.putMybatisSqlStatementList(mapperName, sqlSessionList);}}MapperProxyFactory mapperProxyFactory = new MapperProxyFactory(mybatisConfiguration);return new DefaultMybatisSqlSessionFactory(mapperProxyFactory);}public MybatisSqlSessionFactoryBuilder setConfigPath(String configPath) {this.configPath = configPath;return this;}public MybatisSqlSessionFactoryBuilder setMybatisConfigurationParser(IMybatisConfigurationParser iMybatisConfigurationParser) {this.mybatisConfigurationParser = iMybatisConfigurationParser;return this;}public MybatisSqlSessionFactoryBuilder setMybatisMapperParser(IMybatisMapperParser iMybatisMapperParser) {this.mybatisMapperParser = iMybatisMapperParser;return this;}
}

添加测试代码,进行测试:

package com.yang.mybatis.test;import com.yang.mybatis.config.parser.XmlMybatisConfigurationParser;
import com.yang.mybatis.config.parser.XmlMybatisMapperParser;
import com.yang.mybatis.session.IMybatisSqlSession;
import com.yang.mybatis.session.IMybatisSqlSessionFactory;
import com.yang.mybatis.session.MybatisSqlSessionFactoryBuilder;public class Main {public static void main(String[] args) {String configPath = "mybatis-config.xml";IMybatisSqlSessionFactory mybatisSqlSessionFactory = new MybatisSqlSessionFactoryBuilder().setMybatisMapperParser(new XmlMybatisMapperParser()).setMybatisConfigurationParser(new XmlMybatisConfigurationParser()).setConfigPath(configPath).buildSqlSessionFactory();IMybatisSqlSession mybatisSqlSession = mybatisSqlSessionFactory.openSession();IUserMapper userMapper = mybatisSqlSession.getMapper(IUserMapper.class);System.out.println(userMapper.queryUserName(1));}
}

image.png

SqlSession获取执行方法对应的sql语句

首先,修改MybatisConfiguration,之前是将每一个mapper和他所拥有的MybatisSqlStatement列表关联起来,现在感觉粒度太大,因此,该为每一个mapper的方法和对应的MybatisSqlStatement关联。

package com.yang.mybatis.config;import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;public class MybatisConfiguration implements Serializable {private Map<String, MybatisEnvironment> id2EnvironmentMap = new HashMap<>();private MybatisEnvironment defaultMybatisEnvironment;private List<String> mapperPaths = new ArrayList<>();private Map<String, MybatisSqlStatement> mapperMethod2SqlStatementsMap = new HashMap<>();public void putMapperMethod2MybatisSqlStatement(String mapperMethod, MybatisSqlStatement mybatisSqlStatement) {this.mapperMethod2SqlStatementsMap.put(mapperMethod, mybatisSqlStatement);}public MybatisSqlStatement getMybatisSqlStatement(String mapperMethod) {return this.mapperMethod2SqlStatementsMap.get(mapperMethod);}public Map<String, MybatisSqlStatement> getMapperMethod2SqlStatementsMap() {return this.mapperMethod2SqlStatementsMap;}public void addEnvironment(String id, MybatisEnvironment mybatisEnvironment) {this.id2EnvironmentMap.put(id, mybatisEnvironment);}public MybatisEnvironment getEnvironment(String id) {return id2EnvironmentMap.get(id);}public MybatisEnvironment getDefaultMybatisEnvironment() {return defaultMybatisEnvironment;}public void setDefaultMybatisEnvironment(MybatisEnvironment defaultMybatisEnvironment) {this.defaultMybatisEnvironment = defaultMybatisEnvironment;}public void addMapperPath(String mapperPath) {this.mapperPaths.add(mapperPath);}public List<String> getMapperPaths() {return this.mapperPaths;}public List<MybatisEnvironment> getEnvironments() {return new ArrayList<>(id2EnvironmentMap.values());}}

然后修改MybatisSqlSessionFactoryBuilder:

package com.yang.mybatis.session;import com.yang.mybatis.config.MybatisConfiguration;
import com.yang.mybatis.config.MybatisSqlStatement;
import com.yang.mybatis.config.parser.IMybatisConfigurationParser;
import com.yang.mybatis.config.parser.IMybatisMapperParser;
import com.yang.mybatis.proxy.MapperProxyFactory;import java.util.List;public class MybatisSqlSessionFactoryBuilder {private IMybatisConfigurationParser mybatisConfigurationParser;private IMybatisMapperParser mybatisMapperParser;private String configPath;public IMybatisSqlSessionFactory buildSqlSessionFactory() {if (configPath == null || configPath.isEmpty()) {throw new RuntimeException("配置文件路径不合法==========");}if (this.mybatisMapperParser == null || this.mybatisConfigurationParser == null) {throw new RuntimeException("缺少解析器=======");}MybatisConfiguration mybatisConfiguration = mybatisConfigurationParser.parser(configPath);List<String> mapperPaths = mybatisConfiguration.getMapperPaths();for (String mapperPath : mapperPaths) {List<MybatisSqlStatement> mybatisSqlStatements = this.mybatisMapperParser.parseMapper(mapperPath);for (MybatisSqlStatement mybatisSqlStatement : mybatisSqlStatements) {String methodName = mybatisSqlStatement.getNamespace() + "." + mybatisSqlStatement.getId();mybatisConfiguration.putMapperMethod2MybatisSqlStatement(methodName, mybatisSqlStatement);}}MapperProxyFactory mapperProxyFactory = new MapperProxyFactory(mybatisConfiguration);return new DefaultMybatisSqlSessionFactory(mapperProxyFactory);}public MybatisSqlSessionFactoryBuilder setConfigPath(String configPath) {this.configPath = configPath;return this;}public MybatisSqlSessionFactoryBuilder setMybatisConfigurationParser(IMybatisConfigurationParser iMybatisConfigurationParser) {this.mybatisConfigurationParser = iMybatisConfigurationParser;return this;}public MybatisSqlSessionFactoryBuilder setMybatisMapperParser(IMybatisMapperParser iMybatisMapperParser) {this.mybatisMapperParser = iMybatisMapperParser;return this;}
}

修改DefaultMybatisSqlSession类,获取方法对应的MybatisStatement

package com.yang.mybatis.session;import com.yang.mybatis.config.MybatisSqlStatement;
import com.yang.mybatis.proxy.MapperProxyFactory;public class DefaultMybatisSqlSession implements IMybatisSqlSession {private MapperProxyFactory mapperProxyFactory;public DefaultMybatisSqlSession(MapperProxyFactory mapperProxyFactory) {this.mapperProxyFactory = mapperProxyFactory;}@Overridepublic <T> T execute(String method, Object parameter) {MybatisSqlStatement mybatisSqlStatement = this.mapperProxyFactory.getMybatisConfiguration().getMybatisSqlStatement(method);return (T)("method:" + method + "sql:"+ mybatisSqlStatement.getSql() + ",入参:" + parameter);}@Overridepublic <T> T getMapper(Class<T> type) {return (T) mapperProxyFactory.newInstance(type, this);}
}

添加测试方法进行测试:

 public static void main(String[] args) {String configPath = "mybatis-config.xml";MybatisSqlSessionFactory mybatisSqlSessionFactory = new MybatisSqlSessionFactoryBuilder().setMybatisMapperParser(new XmlMybatisMapperParser()).setMybatisConfigurationParser(new XmlMybatisConfigurationParser()).setConfigPath(configPath).buildSqlSessionFactory();IMybatisSqlSession mybatisSqlSession = mybatisSqlSessionFactory.openSession();IUserMapper userMapper = mybatisSqlSession.getMapper(IUserMapper.class);System.out.println(userMapper.queryUserAge(1));}

测试结果如下:
image.png

参考文章

https://blog.csdn.net/weixin_43520450/article/details/107230205

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/6246.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深度学习:基于TensorFlow、Keras,使用长短期记忆神经网络模型(LSTM)对Microsoft股票进行预测分析

前言 系列专栏&#xff1a;机器学习&#xff1a;高级应用与实践【项目实战100】【2024】✨︎ 在本专栏中不仅包含一些适合初学者的最新机器学习项目&#xff0c;每个项目都处理一组不同的问题&#xff0c;包括监督和无监督学习、分类、回归和聚类&#xff0c;而且涉及创建深度学…

分享我的github仓库

这是一个由现代C编写的小型、学习性质的服务器框架&#xff0c;包含压缩&#xff0c;序列化&#xff0c;IO调度&#xff0c;Socket封装&#xff0c;文件配置&#xff0c;日志库等多个完整自研模块&#xff0c;欢迎到我的仓库阅读代码和安装体验&#xff0c;期待任何的建议和反馈…

Docker 加持的安卓手机:随身携带的知识库(一)

这篇文章聊聊&#xff0c;如何借助 Docker &#xff0c;尝试将一台五年前的手机&#xff0c;构建成一个随身携带的、本地化的知识库。 写在前面 本篇文章&#xff0c;我使用了一台去年从二手平台购入的五年前的手机&#xff0c;K20 Pro。 为了让它能够稳定持续的运行&#xf…

如何从Mac电脑恢复任何删除的视频

Microsoft Office是包括Mac用户在内的人们在世界各地创建文档时使用的最佳软件之一。该软件允许您创建任何类型的文件&#xff0c;如演示文稿、帐户文件和书面文件。您可以使用 MS Office 来完成。所有Microsoft文档都可以在Mac上使用。大多数情况下&#xff0c;您处理文档&…

手搓堆(C语言)

Heap.h #pragma once#include <stdio.h> #include <stdlib.h> #include <assert.h> #include <stdbool.h> #include <string.h> typedef int HPDataType; typedef struct Heap {HPDataType* a;int size;int capacity; }Heap;//初始化 void Heap…

软件工程习题答案2024最新版

习题一答案 一、选择题 软件的主要特性是(A B C)。 A) **无形 **B) 高成本 C) **包括程序和文档 ** D) 可独立构成计算机系统 软件工程三要素是(B)。 A) 技术、方法和工具 B) 方法、工具和过程 C) 方法、对象和类 D) 过程、模型、方法 包含风险分析的软件工程模型是(A)…

深度学习之基于Matlab BP神经网络烟叶成熟度分类

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 一、项目背景 烟叶的成熟度是评估烟叶品质的重要指标之一&#xff0c;它直接影响着烟叶的口感、香气和理化特性。传…

python基础算法题0502

数字反转 无论是字符串反转还是数字反转&#xff0c;其实都一样。 需求 代码 class Solution:def reverse(self, x: int) -> int:if 0 < x < 2 ** 31 - 1:m str(x)[::-1]if int(m)<2**31-1:return int(m)else:return 0if 0 > x > -2 ** 31:y -xn str(y…

uniapp 监听APP切换前台、后台插件 Ba-Lifecycle

监听APP切换前台、后台 Ba-Lifecycle 简介&#xff08;下载地址&#xff09; Ba-Lifecycle 是一款uniapp监听APP切换前台、后台的插件&#xff0c;简单易用。 截图展示 也可关注博客&#xff0c;实时更新最新插件&#xff1a; uniapp 常用原生插件大全 使用方法 在 script…

华为机考入门python3--(19)牛客19- 简单错误记录

分类&#xff1a;字符串 知识点&#xff1a; 分割字符串 my_str.split(\\) 字符串只保留最后16位字符 my_str[-16:] 列表可以作为队列、栈 添加元素到第一个位置 my_list.insert(0, elem) 增加元素到最后一个位置 my_list.append(elem) 删除第一个 my_list.pop(0)…

SAP_SD模块-销售批次策略应用记录

一、销售批次查找策略的重要性 批次查找策略允许企业在销售过程中根据预定义的规则自动选择最适合的产品批次。这种策略的实施&#xff0c;对企业尤其是那些涉及到严格产品质量与安全标准的行业&#xff08;如食品、药品及化工产品&#xff09;具有以下几方面的重要意义&#x…

[方法] Unity 实现仿《原神》第三人称跟随相机 v1.0

参考网址&#xff1a;【Unity中文课堂】RPG战斗系统Plus 在Unity游戏引擎中&#xff0c;实现类似《原神》的第三人称跟随相机并非易事&#xff0c;但幸运的是&#xff0c;Unity为我们提供了强大的工具集&#xff0c;其中Cinemachine插件便是实现这一目标的重要工具。Cinemachi…

从键入网址到网页显示,期间发生了什么?

从键入网址到网页显示&#xff0c;期间发生了什么&#xff1f; 孤单小弟【HTTP】真实地址查询【DNS】指南帮手【协议栈】可靠传输【TCP】远程定位【IP】两点传输【MAC】出口【网卡】送别者【交换机】出境大门【路由器】互相扒皮【服务器与客户端】相关问答 不少小伙伴在面试过程…

《21天学通C++》(第十五章)标准模板库简介

本章简单介绍STL容器、迭代器和算法的基本概念&#xff0c;之后几章会分别详述 1.STL容器 STL容器是STL中用于存储集合数据的组件&#xff0c;它们可以被看作是模板类&#xff0c;允许开发者定义特定类型的容器发&#xff0c;这里按照C11标准分为四类&#xff1a;顺序容器、关…

Qt5 框架学习及应用 — 对象树

Qt 对象树 对象树概念Qt为什么使用对象树 &#xff1f;将对象挂到对象树上 对象树概念 对象树&#xff1a;对于树的概念&#xff0c;相信许多学过数据结构的同学应该都不会陌生。在学习数据结构的时候我们所接触的什么二叉树、多叉树、哈夫曼树、AVL树、再到红黑树、B/B树………

vue处理查询框清除后无法查询问题,举例为日期选择

例如 在对应的查询方法添加 //我这里获取的是date&#xff0c;如果是其他参数改为其他的即可 if (query.date && query.date.length > 2) {conditions.noedate query.date[0] || conditions.noedate;//获取开始时间conditions.twodate query.date[1] || conditi…

实验14 MVC

二、实验项目内容&#xff08;实验题目&#xff09; 编写代码&#xff0c;掌握MVC的用法。【参考课本 例1 】 三、源代码以及执行结果截图&#xff1a; example7_1.jsp&#xff1a; <% page contentType"text/html" %> <% page pageEncoding "ut…

PHP医院安全(不良)事件报告系统源码 vue2+element支持11大类不良事件上报、审核处理、分析改进

PHP医院安全&#xff08;不良&#xff09;事件报告系统源码 vue2element支持11大类不良事件上报、审核处理、分析改进 医院安全&#xff08;不良&#xff09;事件管理系统采用无责的、自愿的填报不良事件方式&#xff0c;有效地减轻医护人员的思想压力&#xff0c;实现以事件为…

某招聘网站搜索结果接口之acw_sc__v2逆向

上文链接 某招聘网站搜索结果接口逆向之webpack扣取 分析参数 acw_sc__v2 663449dacb63fbe529520fb21063cf354dbce1cf 既然在Cookie中那么需要借助Hook脚本 (function() {//严谨模式 检查所有错误use strict;var cookieTemp "";Object.defineProperty(document,…

springboot+websocket开发简单的在线群聊聊天web版本

springbootwebsocket开发简单的在线群聊聊天web版本&#xff01;近期在测试websocket插件的群聊功能。下面是一个简单的demo。分享给大家&#xff0c;亲测可以使用的。 1&#xff1a;首先是一个chat.html页面。代码如下&#xff1a; <!DOCTYPE html> <html lang"…