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

2024-05-04 18:44

本文主要是介绍手撸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

这篇关于手撸Mybatis(三)——收敛SQL操作到SqlSession的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/959857

相关文章

MySQL MCP 服务器安装配置最佳实践

《MySQLMCP服务器安装配置最佳实践》本文介绍MySQLMCP服务器的安装配置方法,本文结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下... 目录mysql MCP 服务器安装配置指南简介功能特点安装方法数据库配置使用MCP Inspector进行调试开发指

mysql中insert into的基本用法和一些示例

《mysql中insertinto的基本用法和一些示例》INSERTINTO用于向MySQL表插入新行,支持单行/多行及部分列插入,下面给大家介绍mysql中insertinto的基本用法和一些示例... 目录基本语法插入单行数据插入多行数据插入部分列的数据插入默认值注意事项在mysql中,INSERT I

一文详解MySQL如何设置自动备份任务

《一文详解MySQL如何设置自动备份任务》设置自动备份任务可以确保你的数据库定期备份,防止数据丢失,下面我们就来详细介绍一下如何使用Bash脚本和Cron任务在Linux系统上设置MySQL数据库的自... 目录1. 编写备份脚本1.1 创建并编辑备份脚本1.2 给予脚本执行权限2. 设置 Cron 任务2

SQL Server修改数据库名及物理数据文件名操作步骤

《SQLServer修改数据库名及物理数据文件名操作步骤》在SQLServer中重命名数据库是一个常见的操作,但需要确保用户具有足够的权限来执行此操作,:本文主要介绍SQLServer修改数据... 目录一、背景介绍二、操作步骤2.1 设置为单用户模式(断开连接)2.2 修改数据库名称2.3 查找逻辑文件名

SQL Server数据库死锁处理超详细攻略

《SQLServer数据库死锁处理超详细攻略》SQLServer作为主流数据库管理系统,在高并发场景下可能面临死锁问题,影响系统性能和稳定性,这篇文章主要给大家介绍了关于SQLServer数据库死... 目录一、引言二、查询 Sqlserver 中造成死锁的 SPID三、用内置函数查询执行信息1. sp_w

canal实现mysql数据同步的详细过程

《canal实现mysql数据同步的详细过程》:本文主要介绍canal实现mysql数据同步的详细过程,本文通过实例图文相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的... 目录1、canal下载2、mysql同步用户创建和授权3、canal admin安装和启动4、canal

SQL中JOIN操作的条件使用总结与实践

《SQL中JOIN操作的条件使用总结与实践》在SQL查询中,JOIN操作是多表关联的核心工具,本文将从原理,场景和最佳实践三个方面总结JOIN条件的使用规则,希望可以帮助开发者精准控制查询逻辑... 目录一、ON与WHERE的本质区别二、场景化条件使用规则三、最佳实践建议1.优先使用ON条件2.WHERE用

MySQL存储过程之循环遍历查询的结果集详解

《MySQL存储过程之循环遍历查询的结果集详解》:本文主要介绍MySQL存储过程之循环遍历查询的结果集,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录前言1. 表结构2. 存储过程3. 关于存储过程的SQL补充总结前言近来碰到这样一个问题:在生产上导入的数据发现

MySQL 衍生表(Derived Tables)的使用

《MySQL衍生表(DerivedTables)的使用》本文主要介绍了MySQL衍生表(DerivedTables)的使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学... 目录一、衍生表简介1.1 衍生表基本用法1.2 自定义列名1.3 衍生表的局限在SQL的查询语句select

MySQL 横向衍生表(Lateral Derived Tables)的实现

《MySQL横向衍生表(LateralDerivedTables)的实现》横向衍生表适用于在需要通过子查询获取中间结果集的场景,相对于普通衍生表,横向衍生表可以引用在其之前出现过的表名,本文就来... 目录一、横向衍生表用法示例1.1 用法示例1.2 使用建议前面我们介绍过mysql中的衍生表(From子句