mybatis单元测试(无需启动容器)
一、浅析相关类
参考文档:https://blog.csdn.net/weixin_44778952/article/details/109661125
1 Configuration
MyBatis在启动时会取读取所有配置文件,然后加载到内存中,Configuration类就是承载整个配置的类。
SqlSessionFactoryBuilder调用build方法创建SqlSessionFactory,而SqlSessionFactory需要Configuration配置中心提供创建的条件,在build方法中XMLConfigBuilder 将xml文件流进行初始化并parse返回Configuration,返回之前需要通过parseConfiguration来真正为Configuration设置信息,XPathParser负责将节点下的信息转换成XNode对象方便访问。
2 Executor
Executor是跟SqlSession绑定在一起的,每一个SqlSession都拥有一个新的Executor对象,由Configuration创建。
Mybatis中所有的Mapper语句的执行都是通过Executor进行的,Executor是Mybatis的一个核心接口。从其定义的接口方法我们可以看出,对应的增删改语句是通过Executor接口的update方法进行的,查询是通过query方法进行的。虽然Executor接口的实现类有BaseExecutor和CachingExecutor,而BaseExecutor的子类又有SimpleExecutor、ReuseExecutor和BatchExecutor,但BaseExecutor是一个抽象类,其只实现了一些公共的封装,而把真正的核心实现都通过方法抽象出来给子类实现,如doUpdate()、doQuery();CachingExecutor只是在Executor的基础上加入了缓存的功能,底层还是通过Executor调用的,所以真正有作用的Executor只有SimpleExecutor、ReuseExecutor和BatchExecutor。它们都是自己实现的Executor核心功能,没有借助任何其它的Executor实现,它们是实现不同也就注定了它们的功能也是不一样的。
3 XMLMapperBuilder
mapper文件的解析依赖于XMLConfigBuilder的mapperElement方法来解析mapper文件。解析过程中实质是实例化一个XMLMapperBuilder对象,然后调用其parse方法,parse方法调用的configurationElement方法是真正mapper节点解析入口,包括sql解析,缓存,等。
二、单元测试
1 生成mapper实例
运用以上相关类的功能,可以直接生成mapper的类实例。
基于Java的编程思想,设计一个基类:
BaseMapperTest:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | import org.apache.ibatis.binding.MapperProxyFactory; import org.apache.ibatis.builder.xml.XMLMapperBuilder; import org.apache.ibatis.datasource.unpooled.UnpooledDataSource; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.TransactionIsolationLevel; import org.apache.ibatis.session.defaults.DefaultSqlSession; import org.apache.ibatis.transaction.Transaction; import org.apache.ibatis.transaction.jdbc.JdbcTransaction; import org.springframework.core.env.PropertySource; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.core.io.support.ResourcePropertySource; import java.lang.reflect.ParameterizedType; /** * @author: lyx */ public class BaseMapperTest<T> { /** * mapper接口类(持久层接口) */ private T mapper; /** * 数据库连接 */ private SqlSession sqlSession; /** * 执行 */ private static Executor executor; /** * 配置 */ private static Configuration configuration; static { try { //定义一个配置 configuration = new Configuration(); configuration.setCacheEnabled( false ); configuration.setLazyLoadingEnabled( false ); configuration.setAggressiveLazyLoading( true ); configuration.setDefaultStatementTimeout( 20 ); //读取测试环境数据库配置 PropertySource propertySource = new ResourcePropertySource( new ClassPathResource( "testdb.properties" )); //设置数据库链接 UnpooledDataSource dataSource = new UnpooledDataSource(); dataSource.setDriver(propertySource.getProperty( "driverClassName" ).toString()); dataSource.setUrl(propertySource.getProperty( "url" ).toString()); dataSource.setUsername(propertySource.getProperty( "username" ).toString()); dataSource.setPassword(propertySource.getProperty( "password" ).toString()); //设置事务(测试设置事务不提交false) Transaction transaction = new JdbcTransaction(dataSource, TransactionIsolationLevel.READ_UNCOMMITTED, false ); //设置执行 executor = configuration.newExecutor(transaction); } catch (Exception e) { e.printStackTrace(); } } public BaseMapperTest(String mapperName) { try { //解析mapper文件 Resource mapperResource = new ClassPathResource(mapperName); XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperResource.getInputStream(), configuration, mapperResource.toString(), configuration.getSqlFragments()); xmlMapperBuilder.parse(); //直接实例化一个默认的sqlSession //是做单元测试,那么没必要通过SqlSessionFactoryBuilder构造SqlSessionFactory,再来获取SqlSession sqlSession = new DefaultSqlSession(configuration, executor, false ); //将接口实例化成对象 ParameterizedType pt = (ParameterizedType) this .getClass().getGenericSuperclass(); MapperProxyFactory<T> mapperProxyFactory = new MapperProxyFactory<>((Class<T>) pt.getActualTypeArguments()[ 0 ]); mapper = mapperProxyFactory.newInstance(sqlSession); } catch (Exception e) { e.printStackTrace(); } } /** * 返回mapper实例对象 */ public T getMapper() { return mapper; } } |
配置文件:testdb.properties
1 2 3 4 | driverClassName=com.mysql.cj.jdbc.Driver url=jdbc:mysql: //localhost:3306/demo?serverTimezone=GMT&characterEncoding=utf8&useSSL=false&allowMultiQueries=true username=root password=root |
2 demo
junit或者Testng单元测试都可以实现,下面给出一个junit测试的例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | /** * @author lyx * 直接继承BaseMapperTest,并指定待持久层测试的接口即可 */ @RunWith (SpringRunner. class ) public class BaseConfigDaoTest extends BaseMapperTest<BaseConfigDao> { public BaseConfigDaoTest() { super ( "mapper/BaseCodeConfigMapper.xml" ); } @Test public void selectListByCodeTest() { String code = "lyx" ; List<BaseConfigDTO> baseConfigList = super .getMapper().selectListByCode(code); Assert.assertTrue(baseConfigList.size() > 0 ); } } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
2019-07-05 MySQL主从配置
2019-07-05 MyCat清单
2019-07-05 Nginx整合Tomcat