网站后台编辑器编辑内容无法显示,wordpress采集审核,网站投放广告教程,建设有限公司平时开发过程中需要对mybatis的Mapper类做单元测试#xff0c;主要是验证语法是否正确#xff0c;尤其是一些复杂的动态sql#xff0c;一般项目都集成了spring或springboot#xff0c;当项比较大时#xff0c;每次单元测试启动相当慢#xff0c;可能需要好几分钟#xf…平时开发过程中需要对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依赖 --
dependencygroupIdorg.mybatis/groupIdartifactIdmybatis/artifactIdversion3.5.5/version
/dependency
!-- 单元测试junit包 --
dependencygroupIdjunit/groupIdartifactIdjunit/artifactIdversion4.13/version
/dependency
!-- 用到spring的FileSystemXmlApplicationContext工具类来加载配置 --
dependencygroupIdorg.springframework/groupIdartifactIdspring-context/artifactIdversion5.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 ThreadLocalLocalSession sessionThreadLocal;private static SqlSessionFactory sqlSessionFactory;//配置文件的路径 private final static String configLocation mybatis/mybatis-config-test.xml;private static ListLocalSession 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(ClassT 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(ClassT 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 version1.0 encodingUTF-8 ?
!DOCTYPE configuration PUBLIC -//mybatis.org//DTD Config 3.0//EN http://mybatis.org/dtd/mybatis-3-config.dtd
configurationproperties resourcemybatis/mybatis-db-test.properties/propertiessettings!-- 打印查询语句 --setting namelogImpl valueSTDOUT_LOGGING/!-- 控制全局缓存二级缓存--setting namecacheEnabled valuefalse/!-- 延迟加载的全局开关。当开启时所有关联对象都会延迟加载增加启动效率。默认 false --setting namelazyLoadingEnabled valuetrue/!-- 当开启时任何方法的调用都会加载该对象的所有属性。默认 false可通过select标签的 fetchType来覆盖--setting nameaggressiveLazyLoading valuefalse//settingsenvironments defaultdevelopmentenvironment iddevelopmenttransactionManager typeJDBC/!-- 单独使用时配置成MANAGED没有事务 --dataSource typePOOLEDproperty namedriver value${jdbc.driver}/property nameurl value${jdbc.url}/property nameusername value${jdbc.username}/property namepassword value${jdbc.password}//dataSource/environment/environments/configuration
mybatis-db-test.properties配置文件
#扫描mapper.xml的路径多个用英文逗号隔开
mapperLocationsclasspath:mapper/*.xml#是否提交事务单元测试一般不提交设置为false即可
isCommitfalse#数据库连接参数配置
jdbc.drivercom.mysql.jdbc.Driver
jdbc.urljdbc:mysql://localhost:3306/mysql?serverTimezoneUTCuseUnicodetruecharacterEncodingutf8useSSLfalse
jdbc.usernameroot
jdbc.password123456
测试类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);ListCustomerEntity 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);ListCustomerEntity 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都已释放。