Mybatis单元测试,不使用spring

平时开发过程中需要对mybatis的Mapper类做单元测试,主要是验证语法是否正确,尤其是一些复杂的动态sql,一般项目都集成了spring或springboot,当项比较大时,每次单元测试启动相当慢,可能需要好几分钟,因此写了一个纯mybatis的单元测试基类,实现单元测试的秒级启动。

单元测试基类MybatisBaseTest类主要完成如下工作:

1.加载mybatis配置文件
在MybatisBaseTest.init()方法实现,
该动作在整个单元测试生命周期只执行一次,并且在启动前执行 ,
因此使用junit的@BeforeClass注解标注,表示该动作在单元测试启动前执行。

2.打开session
在MybatisBaseTest.openSession()方法实现,
该方法获取一个mybatis的SqlSession,并将SqlSession存入到线程本地变量中,
使用junit的@Before注解标注,表示在每一个单元测试方法运行前都执行该动作。

3.获取mapper对象
在MybatisBaseTest提供getMapper(Class mapperClass)方法供单元测试子类使用,用于获取具体的Mapper代理对象做测试。

4.关闭session
在MybatisBaseTest.closeSession()方法实现,
从线程本地变量中获取SqlSession对象,完成事务的回滚(单元测试一般不提交事务)和connection的关闭等逻辑。
使用junit的@After注解标注,表示该动作在每一个单元测试方法运行完成后执行。

源码地址: mybatis测试基类

整体包结构如下:
在这里插入图片描述

需要的Maven依赖如下

<!-- mybatis依赖 -->
<dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.5</version>
</dependency>
<!-- 单元测试junit包 -->
<dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13</version>
</dependency>
<!-- 用到spring的FileSystemXmlApplicationContext工具类来加载配置 -->
<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.8.RELEASE</version>
</dependency>

MybatisBasetTest类的代码如下:

package com.zhouyong.practice.mybatis.base;import org.apache.ibatis.builder.xml.XMLConfigBuilder;
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.util.StringUtils;import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;/*** mybatis单元测试基类* @author zhouyong* @date 2023/7/23 9:45 上午*/
public class MybatisBaseTest {private static ThreadLocal<LocalSession> sessionThreadLocal;private static SqlSessionFactory sqlSessionFactory;//配置文件的路径  private final static String configLocation = "mybatis/mybatis-config-test.xml";private static List<LocalSession> sessionPool;/*** 单元测试启动前的初始化动作* 初始化数据库session等相关信息*/@BeforeClasspublic final static void init() throws SQLException, IOException {//解析mybatis全局配置文件Configuration configuration = parseConfiguration();//解析mapper配置parseMapperXmlResource(configuration);//创建SqlSessionFactorysqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);//用于存储所有的sessionsessionPool = new ArrayList<>();//LocalSession的线程本地变量sessionThreadLocal = new ThreadLocal<>();//保底操作,确保异常退出时关闭所有数据库连接Runtime.getRuntime().addShutdownHook(new Thread(()->closeAllSession()));}/*** 启动session* 每一个单元测试方法启动之前会自动执行该方法* 如果子类也有@Before方法,父类的@Before方法先于子类执行*/@Beforepublic final void openSession(){LocalSession localSession = createLocalSession();sessionThreadLocal.set(localSession);sessionPool.add(localSession);}/*** 获取mapper对象* @param mapperClass* @param <T>* @return*/protected final <T> T getMapper(Class<T> mapperClass){return sessionThreadLocal.get().getMapper(mapperClass);}/*** 关闭session* 每一个单元测试执行完之后都会自动执行该方法* 如果子类也有@After方法,则子类的@After方法先于父类执行(于@Before方法相反)*/@Afterpublic final void closeSession(){LocalSession localSession = sessionThreadLocal.get();if(localSession!=null){localSession.close();sessionPool.remove(localSession);sessionThreadLocal.remove();}}/*** 保底操作,异常退出时关闭所有session*/public final static void closeAllSession(){if(sessionPool!=null){for (LocalSession localSession : sessionPool) {localSession.close();}sessionPool.clear();sessionPool = null;}sessionThreadLocal = null;}/*** 解析mybatis全局配置文件* @throws IOException*/private final static Configuration parseConfiguration() throws IOException {InputStream inputStream = Resources.getResourceAsStream(configLocation);XMLConfigBuilder parser = new XMLConfigBuilder(inputStream);Configuration configuration = parser.parse();//驼峰命名自动转换configuration.setMapUnderscoreToCamelCase(true);Properties properties = configuration.getVariables();//如果密码有加密,则此处可以进行解密//String pwd = properties.getProperty("jdbcPassword");//((PooledDataSource)configuration.getEnvironment().getDataSource()).setPassword("解密后的密码");return configuration;}/*** 解析mapper配置文件* @throws IOException*/private final static void parseMapperXmlResource(Configuration configuration) throws IOException {String[] mapperLocations = configuration.getVariables().getProperty("mapperLocations").split(",");//借助spring的FileSystemXmlApplicationContext工具类,根据配置匹配解析出所有路径FileSystemXmlApplicationContext xmlContext = new FileSystemXmlApplicationContext();for (String mapperLocation : mapperLocations) {Resource[] mapperResources = xmlContext.getResources(mapperLocation);for (Resource mapperRes : mapperResources) {XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperRes.getInputStream(),configuration,mapperRes.toString(),configuration.getSqlFragments());xmlMapperBuilder.parse();}}}/*** 创建自定义的LocalSession* @return*/private final LocalSession createLocalSession(){try{String isCommitStr = sqlSessionFactory.getConfiguration().getVariables().getProperty("isCommit");boolean isCommit = StringUtils.isEmpty(isCommitStr) ? false : Boolean.parseBoolean(isCommitStr);SqlSession sqlSession = sqlSessionFactory.openSession(false);Connection connection = sqlSession.getConnection();connection.setAutoCommit(false);return new LocalSession(sqlSession, connection, isCommit);}catch (SQLException e){throw new RuntimeException(e);}}}

LocalSession类对SqlSession做了一层封装

package com.zhouyong.practice.mybatis.base;import org.apache.ibatis.session.SqlSession;import java.sql.Connection;
import java.sql.SQLException;/*** @author zhouyong* @date 2023/7/23 9:52 上午*/
public class LocalSession {/** mybatis 的 session */private SqlSession session;/** sql 的 connection */private Connection connection;/** 是否提交事物,单元测试一般不需要提交事物(直接回滚) */private boolean isCommit;public LocalSession(SqlSession session, Connection connection, boolean isCommit) throws SQLException {this.isCommit = isCommit;this.session = session;this.connection = connection;}/*** 获取mapper对象* @param mapperClass* @param <T>* @return*/public <T> T getMapper(Class<T> mapperClass){return session.getMapper(mapperClass);}/*** 关闭session* @throws SQLException*/public void close(){try{if(isCommit){connection.commit();}else{connection.rollback();}}catch (Exception e) {e.printStackTrace();}finally {try{session.close();}catch (Exception e) {e.printStackTrace();}/*finally {try {if(!connection.isClosed()){connection.close();}} catch (Exception e) {e.printStackTrace();}}*/}}}

mybatis-config-test.xml配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><properties resource="mybatis/mybatis-db-test.properties"></properties><settings><!-- 打印查询语句 --><setting name="logImpl" value="STDOUT_LOGGING"/><!-- 控制全局缓存(二级缓存)--><setting name="cacheEnabled" value="false"/><!-- 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载,增加启动效率。默认 false  --><setting name="lazyLoadingEnabled" value="true"/><!-- 当开启时,任何方法的调用都会加载该对象的所有属性。默认 false,可通过select标签的 fetchType来覆盖--><setting name="aggressiveLazyLoading" value="false"/></settings><environments default="development"><environment id="development"><transactionManager type="JDBC"/><!-- 单独使用时配置成MANAGED没有事务 --><dataSource type="POOLED"><property name="driver" value="${jdbc.driver}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></dataSource></environment></environments></configuration>

mybatis-db-test.properties配置文件

#扫描mapper.xml的路径,多个用英文逗号隔开
mapperLocations=classpath:mapper/*.xml#是否提交事务,单元测试一般不提交设置为false即可
isCommit=false#数据库连接参数配置
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mysql?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false
jdbc.username=root
jdbc.password=123456

测试类CustomerMapperTest继承MybatisBaseTest

package com.zhouyong.practice.mybatis;import com.zhouyong.practice.mybatis.base.MybatisBaseTest;
import com.zhouyong.practice.mybatis.test.CustomerEntity;
import com.zhouyong.practice.mybatis.test.CustomerMapper;
import org.junit.Test;import java.util.List;/*** 测试类继承MybatisBaseTest类* @author zhouyong* @date 2023/7/23 12:32 下午*/
public class CustomerMapperTest extends MybatisBaseTest {@Testpublic void test1(){CustomerMapper mapper = getMapper(CustomerMapper.class);List<CustomerEntity> list = mapper.selectAll();System.out.println("1 list.size()=="+list.size());CustomerEntity entity = new CustomerEntity();entity.setName("李四");entity.setAge(55);entity.setSex("男");mapper.insertMetrics(entity);list = mapper.selectAll();System.out.println("2 list.size()=="+list.size());}@Testpublic void test2(){CustomerMapper mapper = getMapper(CustomerMapper.class);List<CustomerEntity> metricsEntities = mapper.selectAll();System.out.println("3 list.size()=="+metricsEntities.size());CustomerEntity entity = new CustomerEntity();entity.setName("王五");entity.setAge(55);entity.setSex("男");mapper.insertMetrics(entity);metricsEntities = mapper.selectAll();System.out.println("4 list.size()=="+metricsEntities.size());}
}

测试结果符合预期,运行完成后没有提交事务(因为配置中的isCommit设置为false),且单元测试运行完之后所有的connection都已释放。

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

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

相关文章

[Linux] CentOS7 中 pip3 install 可能出现的 ssl 问题

由于解决问题之后, 才写的博客, 所以没有图片记录. 尽量描述清楚一些 今天写代码的时候, 突然发现 文件里用了#define定义宏之后, coc.nvim的coc-clangd补全就用不了 :checkhealth了一下, 发现nvim忘记支持python3了 尝试pip3 install neovim的时候, 发现会警告然后安装失败.…

linux服务器部署

文章目录 一、基本工具安装1.使用vi命令编辑文件 二、安装1.jdk2.读入数据 总结 一、基本工具安装 1.使用vi命令编辑文件 注:如果vi命令没有&#xff0c;可以使用yum -y install vim或者apt-get install vim命令安装。 Linux操作系统第二讲 二、安装 1.jdk 参考 卸载jdk…

Mybatis-Plus插入数据返回主键两种方式(注解或XML)

废话不多说&#xff0c;直接撸代码: <?xml version"1.0" encoding"UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace&qu…

递归排序算法快速排序的实现过程

快速排序(Insertion Sort)也是一种递归排序算法。 快速排序原理&#xff1a;先以列表中的任意一个数为基准(一般选头或尾)&#xff0c;将列表分为左、右两个子列表。 左子列表的数要比基准数小&#xff0c;右子列表的数要比基准数大。然后继续把左子列表和右子列表按同样的方…

(2)前端控制器的扩展配置, 视图解析器类型以及MVC执行流程的概述

SpringMVC入门程序的扩展说明 注册前端控制器的细节 在web.xml文件注册SpringMVC的前端控制器DispatcherServlet时使用url-pattern标签中使用/和/*的区别 /可以匹配.html或.js或.css等方式的请求路径,但不匹配*.jsp的请求路径/*可以匹配所有请求(包括.jsp请求), 例如在过滤器…

IDEA导入微服务项目后自动将微服务展示在service面板中

有时候&#xff0c;不会自动将微服务展示在service面板中。 添加service面板&#xff1a; service面板&#xff1a; 更新所有maven&#xff0c;就可以自动将微服务展示在service面板中。

Opencv 细节补充

1.分辨率的解释 •像素&#xff1a;像素是分辨率的单位。像素是构成位图图像最基本的单元&#xff0c;每个像素都有自己的颜色。 •分辨率&#xff08;解析度&#xff09;&#xff1a; a) 图像分辨率就是单位英寸内的像素点数。单位为PPI(Pixels Per Inch) b) PPI表示的是每英…

git 实操

首先有安装好的git,安装好后,会在任一目录下右键出现git bash和git gui两个选项 打开git bash,设置好全局变量,用户名和邮箱,设置方法为: git config -- global user.name "xxx" git config --global user.email "xxxxxx.com" 1.创建版本库 git init 命…

安装及配置zabbix_agent代理端(监控FTP服务器)

监控agent的linux主机我们在之前的文章里已经做好了 现在直接安装ftp服务即可 [rootagent ~]# yum install -y vsftpd[rootagent ~]# systemctl start vsftpd #启动ftp服务[rootagent ~]# systemctl enable vsftpd #设置ftp服务开机自启 Created symlink fro…

MFC第二十天 数值型关联变量 和单选按钮与复选框的开发应用

文章目录 数值型关联变量数值型关联变量的种类介绍 单选按钮与复选框单选按钮的组内选择原理解析单选按钮和复选框以及应用数值型关联变量的开发CMainDlg.cppCInputDlg.hCInputDlg.cpp 附录 数值型关联变量 数值型关联变量的种类介绍 1、 数值型关联变量&#xff1a; a)控件型…

小程序 user agent stylesheet 覆盖了page下wxss背景色

如下图&#xff1a; login页面的page下的背景色&#xff0c;被&#xff1a;user agent stylesheet覆盖。 分析与解决&#xff1a; 1、user agent stylesheet是浏览器默认样式表&#xff0c;是浏览器默认样式。 2、不同浏览器的默认样式不同个&#xff0c;甚至同种浏览器不同版…

2023年7月杭州/武汉/深圳制造业产品经理NPDP认证招生

产品经理国际资格认证NPDP是新产品开发方面的认证&#xff0c;集理论、方法与实践为一体的全方位的知识体系&#xff0c;为公司组织层级进行规划、决策、执行提供良好的方法体系支撑。 【认证机构】 产品开发与管理协会&#xff08;PDMA&#xff09;成立于1979年&#xff0c;是…

vue2和vue3关于class类的绑定以及style的绑定的区别

本篇为个人笔记 1.对于class类的绑定的区别 vue2:对于vue2而言&#xff0c;所有类的绑定都是基于对象{}来进行的 例如&#xff1a;单个类绑定 <div :class"{active:isActive}"></div> 多个类绑定&#xff1a; <div :class"{active,hasError…

Vue--声明式导航

一、声明式导航-导航链接 1.需求 实现导航高亮效果 如果使用a标签进行跳转的话&#xff0c;需要给当前跳转的导航加样式&#xff0c;同时要移除上一个a标签的样式&#xff0c;太麻烦&#xff01;&#xff01;&#xff01; 2.解决方案 vue-router 提供了一个全局组件 router…

FPGA_学习_13_方差计算小模块

测距器件APD的性能与器件本身的温度、施加在APD的偏置电压息息相关。 在不同的温度下&#xff0c;APD的偏压对测距性能的影响非常大。 要确定一个合适的APD的偏压Vopt&#xff0c;首先你要知道当前温度下&#xff0c;APD的击穿电压Vbr&#xff0c;一般来讲&#xff0c;Vopt Vb…

【性能优化】MySQL百万数据深度分页优化思路分析

业务场景 一般在项目开发中会有很多的统计数据需要进行上报分析&#xff0c;一般在分析过后会在后台展示出来给运营和产品进行分页查看&#xff0c;最常见的一种就是根据日期进行筛选。这种统计数据随着时间的推移数据量会慢慢的变大&#xff0c;达到百万、千万条数据只是时间问…

FPGA学习——实现任意倍分频器(奇数/偶数倍分频器均可实现)

文章目录 一、分频器二、Verilog实现任意倍分频器2.1、Verilog源码2.2、仿真文件 三、仿真波形图 一、分频器 在FPGA&#xff08;可编程逻辑门阵列&#xff09;中&#xff0c;分频器是一种用于将时钟信号的频率降低的电路或模块。它可以根据输入的时钟信号生成一个较低频率的输…

python+unittest+requests+HTMLRunner搭建接口测试框架,执行用例请求多个不同请求方式的接口

问题描述&#xff1a; 搭建接口测试框架&#xff0c;执行用例请求多个不同请求方式的接口 实现步骤&#xff1a; ① 创建配置文件config.ini&#xff0c;写入部分公用参数&#xff0c;如接口的基本url、测试报告文件路径、测试数据文件路径等配置项 1 [DATABASE] 2 data_add…

Llama2开源大模型的新篇章以及在阿里云的实践

Llama一直被誉为AI社区中最强大的开源大模型。然而&#xff0c;由于开源协议的限制&#xff0c;它一直不能被免费用于商业用途。然而&#xff0c;这一切在7月19日发生了改变&#xff0c;当Meta终于发布了大家期待已久的免费商用版本Llama2。Llama2是一个由Meta AI开发的预训练大…

Redis的9种数据类型与数据持久化

系列文章传送门&#xff1a; 【七天入门数据库】第一天 MySQL的安装部署 【七天入门数据库】第二天 数据库理论基础 【七天入门数据库】第三天 MySQL的库表操作 【七天入门数据库】第四天 数据操作语言DML 一、Redis的9种数据类型的基本操作 &#xff08;一&#xff09;k…