mybatis單元測試(無需啓動容器)

mybatis單元測試(無需啓動容器)

一、淺析相關類

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:

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

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測試的例子

/**
 * @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);
    }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章