Mybatis-Plus由于其强大又简单的特性,被越来越多用于企业生产开发中,下面将Mybatis-Plus简称为MP
先简单看下其是如何使用的,假设已经新建了一张用户表,一个Spring Boot项目
① maven中引入MP的start jar包
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.1.0</version> </dependency>
②在 Spring Boot 启动类中添加 @MapperScan 注解,扫描 Mapper 文件夹(路径修改成自己项目中的路径)
"com.baomidou.mybatisplus.samples.quickstart.mapper") (publicclassApplication { publicstaticvoidmain(String[] args) { SpringApplication.run(QuickStartApplication.class, args); } }
③编写bean,Mapper
用户Bean类略
Mapper类继承MP的BaseMapper
publicinterfaceUserMapperextendsBaseMapper<User> { }
④测试
SpringRunner.class) (publicclassSampleTest { privateUserMapperuserMapper; publicvoidtestSelect() { System.out.println(("----- selectAll method test ------")); List<User>userList=userMapper.selectList(null); Assert.assertEquals(5, userList.size()); userList.forEach(System.out::println); } }
这里没有写一句Sql,至于UserMapper.xml文件里面当然也是空的
那MP是如何实现的? 后面主要说下这个问题。
先看一下BaseMapper类,里面定义了一些CRUD方法,如果你搜索BaseMapper.xml,这个文件也是不存在的。
/**public interface BaseMapper<T> {/*** 插入一条记录* @param entity 实体对象*/intinsert(Tentity); /*** 根据 ID 删除* @param id 主键ID*/intdeleteById(Serializableid); //其他省略..........
为了简化开发,我们都会使用自动注入的方式,而不会在Spring.xml中配置,更不会使用原始的在mybatis config xml中进行mapper映射配置
这是spring.xml
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> </bean> <bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean"> <property name="mapperInterface" value="cn.edu.nuc.dao.UserMapper" /> <property name="sqlSessionFactory" ref="sqlSessionFactory" /> </bean>
因为我们配置了@MapperScan,Spring 会通过MapperScannerRegistrar类进行扫描注册Bean,Mapper类的工厂bean会在Spring AbstractApplicationContext类finishBeanFactoryInitialization方法中进行实例化,由于调用链较长,可以自己在MapperFactoryBean类checkDaoConfig中进行Debug
在checkDaoConfig方法中,会对Mapper类进行注册放入MybatisConfiguration(继承了Configuration)中
在MybatisMapperRegistry类(继承自MapperRegistry)的addMapper方法中调用MybatisMapperAnnotationBuilder的parse方法,在该方法中有这么一段代码。
//TODO 注入 CURD 动态 SQL (应该在注解之前注入)if (GlobalConfigUtils.getSuperMapperClass(configuration).isAssignableFrom(type)) { GlobalConfigUtils.getSqlInjector(configuration).inspectInject(assistant, type); }
GlobalConfigUtils.getSqlInjector(configuration) 获取SQL注入器,如果没有指定,使用DefaultSqlInjector默认注入器,所有的注入器都继承自AbstractSqlInjector类
inspectInject方法中就会对CRUD等一些语句进行注入,并生成SqlSource
//循环注入methodList.forEach(m->m.inject(builderAssistant, mapperClass, modelClass, tableInfo));
AbstractMethod.java
/*** 注入自定义方法*/publicvoidinject(MapperBuilderAssistantbuilderAssistant, Class<?>mapperClass, Class<?>modelClass, TableInfotableInfo) { this.configuration=builderAssistant.getConfiguration(); this.builderAssistant=builderAssistant; this.languageDriver=configuration.getDefaultScriptingLanguageInstance(); /* 注入自定义方法 */injectMappedStatement(mapperClass, modelClass, tableInfo); }
injectMappedStatement是一个抽象方法会根据不同的method对象调用不同的injectMappedStatement方法
本例会调用SelectList类
publicclassSelectListextendsAbstractMethod { publicMappedStatementinjectMappedStatement(Class<?>mapperClass, Class<?>modelClass, TableInfotableInfo) { //获取selectList sqlSqlMethodsqlMethod=SqlMethod.SELECT_LIST; //格式化Sql 语句Stringsql=String.format(sqlMethod.getSql(), sqlSelectColumns(tableInfo, true), tableInfo.getTableName(), this.sqlWhereEntityWrapper(true, tableInfo)); //创建SqlSourceSqlSourcesqlSource=languageDriver.createSqlSource(configuration, sql, modelClass); //添加MappedStatement 到 Mybatis 容器returnthis.addSelectMappedStatement(mapperClass, sqlMethod.getMethod(), sqlSource, modelClass, tableInfo); } }
如上是Mp对selectList的一个加载过程,其他CRUD类似
后面具体执行的时候会通过MapperProxy动态代理Mapper接口,执行过程图如下,图较大