前面我们搭建好了本地对持久层的单元测试环境。当我们自己实现dao层的一些关级联查询或者批量的写库更新库的操作时,要验证逻辑的正确性,就可以写一些dao层的单元测试来验证。
而大部分情况下针对单表的curd操作,我们可以用mybatis生成器帮我们生成相应的mapper,直接用即可。不多废话,下面开干!
编写gradle脚本
现在我们将编写一个新的gradle脚本来使用生成器插件,并创建相应的生成任务。
在build.gradle
中引入这个脚本文件即可。
...
apply from: 'mbgen.gradle'
下面我们编写生成脚本,首先声明一个生成器的配置:
configurations {
mybatisGenerator
}
基于这个配置类型,我们引入mybatis生成器需要的依赖:
dependencies {
// 生成器工具
mybatisGenerator 'org.mybatis.generator:mybatis-generator-core:1.4.1'
// 生成器连接h2数据库
mybatisGenerator 'com.h2database:h2:1.4.200'
}
这个文件先写到这里,接下来我们关注生成器的相关配置。
抽取生成器配置
对用户来说,用户无需关心生成器本身的配置,而应该是跟项目生成的路径,生成的名称等相关的设置。因此,我们自定义这样的属性配置文件:
# 生成器连接数据源配置
mybatis.generator.dataSource.driverClassName=org.h2.Driver
mybatis.generator.dataSource.username=sa
mybatis.generator.dataSource.password=
mybatis.generator.genUrl=jdbc:h2:tcp://localhost/~/db_juan_mall
# 生成目标,mybatis的特性版本
mybatis.generator.target-runtime=MyBatis3DynamicSql
#mybatis.generator.target-runtime=MyBatis3
# 生成实体类所在的包
mybatis.generator.model.package=com.xiaojuan.boot.dao.generated.model
# 生成mapper所在的包
mybatis.generator.mapper.package=com.xiaojuan.boot.dao.generated.mapper
# 生成xml形式的mapper文件所在的资源包路径
mybatis.generator.mapper.xml=mapper.generated
# 要匹配的表前缀
mybatis.generator.table.prefix=Tb
# 要去除的前缀
mybatis.generator.table.prefix.clear=Tb
友情提示
在properties文件中出现中文,idea默认的编码不支持,我们把properties文件全局设为UTF-8格式
然后我们再把生成器核心的配置文件放进来:
这个文件内容如下,其中需要外部提供配置的地方我们采用了${...}
的形式,这个文件对用户来说是不用关心的,有特殊需要再修改这个文件里的配置:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!-- MyBatis3、MyBatis3DynamicSql -->
<context id="default" targetRuntime="${mybatis.generator.target-runtime}">
<!-- optional,旨在创建class时,对注释进行控制 -->
<commentGenerator>
<property name="suppressDate" value="true"/>
<property name="suppressAllComments" value="true"/>
</commentGenerator>
<!--jdbc的数据库连接 -->
<jdbcConnection
driverClass="${mybatis.generator.dataSource.driverClassName}"
connectionURL="${mybatis.generator.genUrl}"
userId="${mybatis.generator.dataSource.username}"
password="${mybatis.generator.dataSource.password}">
</jdbcConnection>
<!-- 非必需,类型处理器,在数据库类型和java类型之间的转换控制-->
<javaTypeResolver type="org.mybatis.generator.internal.types.JavaTypeResolverDefaultImpl">
<property name="forceBigDecimals" value="false"/>
</javaTypeResolver>
<!-- Model模型生成器,用来生成含有主键key的类,记录类 以及查询Example类
targetPackage 指定生成的model生成所在的包名
targetProject 指定在该项目下所在的路径
-->
<!--<javaModelGenerator targetPackage="com.mmall.pojo" targetProject=".\src\main\java">-->
<javaModelGenerator targetPackage="${mybatis.generator.model.package}" targetProject="./src/main/java">
<!-- 是否允许子包,即targetPackage.schemaName.tableName -->
<property name="enableSubPackages" value="false"/>
<!-- 是否对model添加 构造函数 -->
<property name="constructorBased" value="true"/>
<!-- 是否对类CHAR类型的列的数据进行trim操作 -->
<property name="trimStrings" value="true"/>
<!-- 建立的Model对象是否 不可改变 即生成的Model对象不会有 setter方法,只有构造方法 -->
<property name="immutable" value="false"/>
</javaModelGenerator>
<!--mapper映射文件生成所在的目录 为每一个数据库的表生成对应的SqlMap文件 -->
<!--<sqlMapGenerator targetPackage="mappers" targetProject=".\src\main\resources">-->
<sqlMapGenerator targetPackage="${mybatis.generator.mapper.xml}" targetProject="./src/main/resources">
<property name="enableSubPackages" value="false"/>
</sqlMapGenerator>
<!-- 客户端代码,生成易于使用的针对Model对象和XML配置文件 的代码
type="ANNOTATEDMAPPER",生成Java Model 和基于注解的Mapper对象
type="MIXEDMAPPER",生成基于注解的Java Model 和相应的Mapper对象
type="XMLMAPPER",生成SQLMap XML文件和独立的Mapper接口
-->
<!-- targetPackage:mapper接口dao生成的位置 -->
<!--<javaClientGenerator type="XMLMAPPER" targetPackage="com.mmall.dao" targetProject=".\src\main\java">-->
<javaClientGenerator type="XMLMAPPER" targetPackage="${mybatis.generator.mapper.package}" targetProject="./src/main/java">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
</javaClientGenerator>
<table tableName="${mybatis.generator.table.prefix}_%">
<generatedKey column="id" sqlStatement="MySql" identity="true"/>
<!-- 这行配置存在bug 生成的model多个单词都小写了 -->
<domainObjectRenamingRule searchString="^${mybatis.generator.table.prefix.clear}" replaceString="" />
<!-- <columnOverride column="detail" javaType="java.lang.String" jdbcType="VARCHAR"/>-->
<!-- <columnOverride column="sub_images" javaType="java.lang.String" jdbcType="VARCHAR" />-->
</table>
</context>
</generatorConfiguration>
获取属性文件
接下来,我们再回到mbgen.gradle
文件的编写上来,我们写一个方法来加载生成器属性配置文件:
def getDbProperties = {
def properties = new Properties()
file("src/main/resources/mbgen.properties").withInputStream { inputStream ->
properties.load(inputStream)
}
properties
}
定义ant任务使用生成器
最后一步,我们借助ant工具来完成属性的填充,并调用生成器工具来完成生成工作。写一个ant任务:
task mybatisGenerate {
def properties = getDbProperties()
// ant.properties['targetProject'] = projectDir.path
ant.properties['mybatis.generator.dataSource.driverClassName'] = properties.getProperty("mybatis.generator.dataSource.driverClassName")
ant.properties['mybatis.generator.dataSource.username'] = properties.getProperty("mybatis.generator.dataSource.username")
ant.properties['mybatis.generator.dataSource.password'] = properties.getProperty("mybatis.generator.dataSource.password")
ant.properties['mybatis.generator.genUrl'] = properties.getProperty("mybatis.generator.genUrl")
ant.properties['mybatis.generator.target-runtime'] = properties.getProperty("mybatis.generator.target-runtime")
ant.properties['mybatis.generator.model.package'] = properties.getProperty("mybatis.generator.model.package")
ant.properties['mybatis.generator.mapper.package'] = properties.getProperty("mybatis.generator.mapper.package")
ant.properties['mybatis.generator.mapper.xml'] = properties.getProperty("mybatis.generator.mapper.xml")
ant.properties['mybatis.generator.table.prefix'] = properties.getProperty("mybatis.generator.table.prefix")
ant.properties['mybatis.generator.table.prefix.clear'] = properties.getProperty("mybatis.generator.table.prefix.clear")
ant.taskdef(
name: 'mbgenerator',
classname: 'org.mybatis.generator.ant.GeneratorAntTask',
classpath: configurations.mybatisGenerator.asPath
)
ant.mbgenerator(overwrite: true,
configfile: projectDir.path + '/src/main/resources/generatorConfig.xml', verbose: true) {
propertyset {
// propertyref(name: 'targetProject')
propertyref(name: 'mybatis.generator.dataSource.driverClassName')
propertyref(name: 'mybatis.generator.dataSource.username')
propertyref(name: 'mybatis.generator.dataSource.password')
propertyref(name: 'mybatis.generator.genUrl')
propertyref(name: 'mybatis.generator.target-runtime')
propertyref(name: 'mybatis.generator.model.package')
propertyref(name: 'mybatis.generator.mapper.package')
propertyref(name: 'mybatis.generator.mapper.xml')
propertyref(name: 'mybatis.generator.table.prefix')
propertyref(name: 'mybatis.generator.table.prefix.clear')
}
}
}
执行生成器
当我们刷新gradle配置后,就执行了生成任务,可以看到生成的结果:
同时我们在gradle工具箱中也可以看到生成的任务,以后双击就可以重新生成并覆盖了。
友情提示
当我们无需重新生成时,可以把
apply from: 'mbgen.gradle'
注释掉,不然每次刷新gradle配置,都要执行一次。
使用生成的api
因为我们采用的是mybatis3的dynamic sql特性,因此不再生成传统的xml形式的mapper文件了。
关于这个特性的语法可以参考:https://mybatis.org/mybatis-dynamic-sql/docs/mybatis3.html
为此我们还要引入一个mybatis dynamic sql的依赖:
dependencies {
...
implementation 'org.mybatis.dynamic-sql:mybatis-dynamic-sql:1.4.0'
}
在编写一个单元测试来使用生成器生成的mapper前,我们先配置下生成器mapper的日志输出:
application.yml
logging:
level:
...
com.xiaojuan.boot.dao.generated.mapper: trace
然后将原来写的model和mapper删掉,因为生成器帮我们生成了这些,category-mapper.xml也删除。
最后是我们的单元测试:
package com.xiaojuan.boot.dao.mapper;
import ...
import static org.junit.jupiter.api.Assertions.*;
import static com.xiaojuan.boot.dao.generated.mapper.CategoryDynamicSqlSupport.*;
import static org.mybatis.dynamic.sql.SqlBuilder.*;
@Transactional // 确保每个单元测试后数据会回滚,实现单元测试数据的隔离
@SpringBootTest
public class CategoryMapperTest {
@Resource
private CategoryMapper categoryMapper;
@Test
public void testCountAll() {
long count = categoryMapper.count(CountDSLCompleter.allRows());
assertEquals(3, count);
}
@Test
public void testListTopLevelCategories() {
List<Category> categories = categoryMapper.select(c -> c.where(level, isEqualTo((byte)1)));
assertEquals(1, categories.size());
}
}
测试ok!