通用mapper

 ·  ☕ 8  · 👀...

介绍

技术 特性 适用场景 说明
通用mapper - 代码生成
- 扩展通用方法
- 二级缓存
与MyBatis结合使用 - 官网
- Github
- Doc
- MyBatis 通用 Mapper
- Mapper 接口大全
- 示例项目

开发

配置Maven

 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
<dependencies>
    <dependency>
        <groupId>tk.mybatis</groupId>
        <artifactId>mapper-spring-boot-starter</artifactId>
        <version>2.1.5</version>
    </dependency>
</dependencies>
<build>
    <plugins>
        <!--配置Mybatis-Generator插件-->
        <plugin>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-maven-plugin</artifactId>
            <version>1.3.7</version>
            <configuration>
                <verbose>true</verbose>
                <overwrite>true</overwrite>
                <configurationFile>src/main/resources/generatorConfig.xml</configurationFile>
            </configuration>
            <dependencies>
                <!--MyBatis Generator及工具-->
                <dependency>
                    <groupId>org.mybatis.generator</groupId>
                    <artifactId>mybatis-generator-core</artifactId>
                    <version>1.3.7</version>
                </dependency>
                <dependency>
                    <groupId>tk.mybatis</groupId>
                    <artifactId>mapper</artifactId>
                    <version>4.0.3</version>
                </dependency>
                <dependency>
                    <groupId>mysql</groupId>
                    <artifactId>mysql-connector-java</artifactId>
                    <version>5.1.46</version>
                </dependency>
            </dependencies>
        </plugin>
    </plugins>
</build>

配置实体生成

generatorConfig.xml(Postgresql版)

 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
<?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>
    <context id="webapi" targetRuntime="MyBatis3Simple" defaultModelType="flat">
        <!--处理sql中的`符号-->
        <property name="beginningDelimiter" value="`"/>
        <property name="endingDelimiter" value="`"/>

        <plugin type="org.mybatis.generator.plugins.SerializablePlugin" />
        <plugin type="org.mybatis.generator.plugins.EqualsHashCodePlugin"/>
        <plugin type="org.mybatis.generator.plugins.ToStringPlugin"/>
        <plugin type="tk.mybatis.mapper.generator.MapperPlugin">
            <property name="mappers" value="tk.mybatis.mapper.common.Mapper"/>
            <property name="caseSensitive" value="true"/>
        </plugin>

        <!--数据库链接URL,用户名、密码 -->
        <jdbcConnection driverClass="org.postgresql.Driver"
                        connectionURL="jdbc:postgresql://127.0.0.1:5432/postgres?characterEncoding=utf8"
                        userId="postgres"
                        password="postgres">
        </jdbcConnection>

        <!-- 生成model模型,对应的包路径,以及文件存放路径(targetProject),targetProject可以指定具体的路径,如./src/main/java,
                也可以使用“MAVEN”来自动生成,这样生成的代码会在target/generatord-source目录下 -->
        <!-- 生成模型的包名和位置-->
        <javaModelGenerator targetPackage="com.wanglibing.postgresql.pojo" targetProject="src/main/java" />
        <javaClientGenerator targetPackage="com.wanglibing.postgresql.mapper" targetProject="src/main/java"
                             type="ANNOTATEDMAPPER"/>

        <!-- 要生成的表 tableName是数据库中的表名或视图名 domainObjectName是实体类名-->
        <table tableName="demo1">
            <generatedKey column="id" sqlStatement="JDBC"/>
        </table>
    </context>
</generatorConfiguration>

generatorConfig.xml(MySQL版)

 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
<?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>
    <context id="webapi" targetRuntime="MyBatis3Simple" defaultModelType="flat">
        <!--处理sql中的`符号-->
        <property name="beginningDelimiter" value="`"/>
        <property name="endingDelimiter" value="`"/>

        <plugin type="org.mybatis.generator.plugins.SerializablePlugin" />
        <plugin type="org.mybatis.generator.plugins.EqualsHashCodePlugin"/>
        <plugin type="org.mybatis.generator.plugins.ToStringPlugin"/>
        <plugin type="tk.mybatis.mapper.generator.MapperPlugin">
            <property name="mappers" value="tk.mybatis.mapper.common.Mapper"/>
            <property name="caseSensitive" value="true"/>
        </plugin>

        <!--数据库链接URL,用户名、密码 -->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                        connectionURL="jdbc:mysql://127.0.0.1:3306/webapi?characterEncoding=utf8"
                        userId="root"
                        password="123456">
            <!-- 解决高版本MySQL生成实体不包含@Id的问题 -->
            <property name="nullCatalogMeansCurrent" value="true" />
        </jdbcConnection>

        <!-- 生成model模型,对应的包路径,以及文件存放路径(targetProject),targetProject可以指定具体的路径,如./src/main/java,
                也可以使用“MAVEN”来自动生成,这样生成的代码会在target/generatord-source目录下 -->
        <!-- 生成模型的包名和位置-->
        <javaModelGenerator targetPackage="com.wanglibing.webapi.pojo" targetProject="src/main/java" />
        <javaClientGenerator targetPackage="com.wanglibing.webapi.mapper" targetProject="src/main/java"
                             type="ANNOTATEDMAPPER"/>

        <!-- 要生成的表 tableName是数据库中的表名或视图名 domainObjectName是实体类名-->
        <table tableName="User">
            <generatedKey column="id" sqlStatement="JDBC"/>
        </table>
    </context>
</generatorConfiguration>

配置model继承基类

1
2
3
4
5
6
7
<!-- 生成模型的包名和位置-->
<javaModelGenerator targetPackage="com.wanglibing.api.entity" targetProject="../api/src/main/java">
    <property name="enableSubPackages" value="true" />
    <property name="trimStrings" value="true" />
    <!-- 配置model继承基类 -->
        <property name="rootClass" value="com.wanglibing.api.pojo.BaseEntity"/>
</javaModelGenerator>

配置Mapper继承基类

1
2
3
4
<javaClientGenerator type="XMLMAPPER" targetPackage="com.dfz.mybatis.dao"  targetProject="src/main/java">
    <property name="enableSubPackages" value="true" />
    <property name="rootInterface" value="com.wanglibing.service.util.mapper.MyMapper"/>
</javaClientGenerator>

通用方法

Select

方法 说明
List select(T record); 根据实体中的属性值进行查询,查询条件使用等号
T selectByPrimaryKey(Object key); 根据主键字段进行查询,方法参数必须包含完整的主键属性,查询条件使用等号
List selectAll(); 查询全部结果,select(null)方法能达到同样的效果
T selectOne(T record); 根据实体中的属性进行查询,只能有一个返回值,有多个结果是抛出异常,查询条件使用等号
int selectCount(T record); 根据实体中的属性查询总数,查询条件使用等号

Insert

方法 说明
int insert(T record); 保存一个实体,null的属性也会保存,不会使用数据库默认值
int insertSelective(T record); 保存一个实体,null的属性不会保存,会使用数据库默认值

Update

方法 说明
int updateByPrimaryKey(T record); 根据主键更新实体全部字段,null值会被更新
int updateByPrimaryKeySelective(T record); 根据主键更新属性不为null的值

Delete

方法 说明
int delete(T record); 根据实体属性作为条件进行删除,查询条件使用等号
int deleteByPrimaryKey(Object key); 根据主键字段进行删除,方法参数必须包含完整的主键属性

Example

方法 说明
List selectByExample(Object example); 根据Example条件进行查询
这个查询支持通过Example类指定查询列,通过selectProperties方法指定查询列
int selectCountByExample(Object example); 根据Example条件进行查询总数
int updateByExample(T record, Object example); 根据Example条件更新实体record包含的全部属性,null值会被更新
int updateByExampleSelective(T record, Object example); 根据Example条件更新实体record包含的不是null的属性值
int deleteByExample(Object example); 根据Example条件删除数据

Condition

Condition方法和Example方法作用完全一样,只是为了避免Example带来的歧义,提供的的Condition方法。

方法 说明
List selectByCondition(Object condition); 根据Condition条件进行查询
int selectCountByCondition(Object condition); 根据Condition条件进行查询总数
int updateByCondition(T record,Object condition); 根据Condition条件更新实体record包含的全部属性,null值会被更新
int updateByConditionSelective(T record, Object condition); 根据Condition条件更新实体record包含的不是null的属性值
int deleteByCondition(Object condition); 根据Condition条件删除数据

RowBounds

默认为内存分页,可以配合PageHelper实现物理分页。

方法 说明
List selectByRowBounds(T record, RowBounds rowBounds); 根据实体属性和RowBounds进行分页查询
SelectByExampleRowBoundsMapper 根据example条件和RowBounds进行分页查询
List selectByConditionAndRowBounds(Object condition, RowBounds rowBounds); 根据example条件和RowBounds进行分页查询,该方法和selectByExampleAndRowBounds完全一样,只是名字改成了Condition

特殊

这些接口针对部分数据库设计,不是所有数据库都支持。

方法 说明
int insertList(List recordList); 批量插入,支持批量插入的数据库可以使用,例如MySQL,H2等,另外该接口限制实体包含id属性并且必须为自增列
int insertList(List recordList); 批量插入,支持批量插入的数据库可以使用,另外该接口限制实体包含id属性并且必须为自增列
int insertUseGeneratedKeys(T record); 插入数据,限制为实体包含id属性并且必须为自增列,实体配置的主键策略无效
List selectByIds(String ids) 根据主键字符串进行查询,类中只有存在一个带有@Id注解的字段
int deleteByIds(String ids) 根据主键字符串进行删除,类中只有存在一个带有@Id注解的字段

Example方法示例

1
2
3
4
5
6
7
8
// 创建Example
Example example = new Example(Demo1.class);
// 设置排序字段
example.setOrderByClause("字段名 ASC/DESC");
// 创建Criteria
Example.Criteria criteria = example.createCriteria();
// 添加条件
criteria.andEqualTo("name","wlb");

配置Mapper和PageHelper整合

mybatis-config.xml内容如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<plugins>
    <!-- 分页插件 分页插件配置在通用Mapper上面,否则会报错 -->
    <plugin interceptor="com.github.pagehelper.PageInterceptor">
        <!-- 设置数据库类型 Oracle,Mysql,MariaDB,SQLite,Hsqldb,PostgreSQL六种数据库-->
        <!--5.0以后自动识别数据库-->
        <!--<property name="dialect" value="mysql"/>-->
        <!-- reasonable:value=true时,pageNum小于1会查询第一页,如果pageNum大于pageSize会查询最后一页 -->
        <property name="reasonable" value="true"/>
        <!--
            默认值为 false。设置为 true 时,允许在运行时根据多数据源自动识别对应方言的分页。
            如果你在 Spring 中配置了动态数据源,并且连接不同类型的数据库,这时你可以配置 autoRuntimeDialect 为 true
        -->
        <property name="autoRuntimeDialect" value="true"/>
        <!-- autoRuntimeDialect:默认值为 false。设置为 true 时,允许在运行时根据多数据源自动识别对应方言的分页  -->
        <property name="autoRuntimeDialect" value="true"/>
    </plugin>
    <!-- 通用Mapper -->
    <plugin interceptor="com.github.abel533.mapperhelper.MapperInterceptor">
        <!--主键自增回写方法,默认值MYSQL,详细说明请看文档 -->
        <property name="IDENTITY" value="MYSQL" />
        <!--通用Mapper接口,多个通用接口用逗号隔开 -->
        <property name="mappers" value="com.github.abel533.mapper.Mapper" />
    </plugin>
</plugins>

自定义Mapper接口

mybatis-generator 添加自定义注释自动生成swagger注解

配置pom.xml

1
2
3
4
5
6
7
<!-- generator-core 自动添加swagger实体类注解 -->
<dependency>
    <groupId>org.mybatis.generator</groupId>
    <artifactId>mybatis-generator-core</artifactId>
    <version>1.3.7</version>
</dependency>
<!-- generator-core 自动添加swagger实体类注解 -->

配置generatorConfig.xml

1
2
3
4
5
<!-- 自定义插件,自动为entity生成swagger2文档-->
<plugin type="com.springboot.MybatisGenerator">
    <property name="apiModelAnnotationPackage" value="io.swagger.annotations.ApiModel" />
    <property name="apiModelPropertyAnnotationPackage" value="io.swagger.annotations.ApiModelProperty" />
</plugin>

配置MybatisGenerator.java

添加自定义实体类MybatisGenerator.java,路径要和配置文件路径一致。

 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
import org.mybatis.generator.api.*;
import org.mybatis.generator.api.dom.java.Field;
import org.mybatis.generator.api.dom.java.TopLevelClass;

import java.util.List;
import java.util.Objects;

/**
 * @author: iamwlb
 *T@date:+08:00
 */
public class MybatisGenerator extends PluginAdapter {


    public static void main(String[] args) {
        generate();
    }

    public static void generate() {
        String config = Objects.requireNonNull(MybatisGenerator.class.getClassLoader().getResource("generatorConfig.xml")).getFile();
        String[] arg = { "-configfile", config, "-overwrite" };
        ShellRunner.main(arg);
    }

    @Override
    public boolean validate(List<String> list) {
        return true;
    }


    /**
     * 实体类添加swagger注解
     * @param field
     * @param topLevelClass
     * @param introspectedColumn
     * @param introspectedTable
     * @param modelClassType
     * @return
     */
    @Override
    public boolean modelFieldGenerated(Field field, TopLevelClass topLevelClass, IntrospectedColumn introspectedColumn,
                                       IntrospectedTable introspectedTable, Plugin.ModelClassType modelClassType) {
        String classAnnotation = "@ApiModel(value=\"" + topLevelClass.getType().getShortName() + "\")";
        if (!topLevelClass.getAnnotations().contains(classAnnotation)) {
            topLevelClass.addAnnotation(classAnnotation);
        }
        String apiModelAnnotationPackage = this.properties.getProperty("apiModelAnnotationPackage");
        String apiModelPropertyAnnotationPackage = this.properties.getProperty("apiModelPropertyAnnotationPackage");
        if (null == apiModelAnnotationPackage) {
            apiModelAnnotationPackage = "io.swagger.annotations.ApiModel";
        }
        if (null == apiModelPropertyAnnotationPackage) {
            apiModelPropertyAnnotationPackage = "io.swagger.annotations.ApiModelProperty";
        }
        topLevelClass.addImportedType(apiModelAnnotationPackage);
        topLevelClass.addImportedType(apiModelPropertyAnnotationPackage);
        field.addAnnotation("@ApiModelProperty(value=\"" + introspectedColumn.getRemarks() +
                "\",name=\""+introspectedColumn.getJavaProperty()+
                "\",dataType=\""+introspectedColumn.getFullyQualifiedJavaType().getShortName()+
                "\")");
        return super.modelFieldGenerated(field, topLevelClass, introspectedColumn, introspectedTable, modelClassType);
    }

    /**
     * 生成dao
     */
    /*@Override
    public boolean clientGenerated(Interface interfaze, TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        FullyQualifiedJavaType fqjt = new FullyQualifiedJavaType("BaseDao<" + introspectedTable.getBaseRecordType() + ","+introspectedTable.getBaseRecordType()+"Example>");
        FullyQualifiedJavaType imp = new FullyQualifiedJavaType("com.springboot.dao.base.BaseDao");
        interfaze.addSuperInterface(fqjt);// 添加 extends BaseDao<User>
        interfaze.addImportedType(imp);// 添加import common.BaseDao;
        interfaze.getMethods().clear();
        return true;
    }*/
}

常见错误

通用mapper无法注入问题

问题描述

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.wanglibing.webapi.mapper.UserMapper' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1506)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1101)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1062)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:583)
    ... 42 more

解决办法

在Application.java上加上注解@MapperScan(mapper所在的包路径)。

1
@MapperScan("com.wanglibing.webapi.mapper")

could not autowire

问题描述

Could not autowire.No beans of “Demo1Mapper” type found.
虽然不影响使用,但是看着不爽。

解决办法

在Mapper加一个注解。如所示:

1
2
3
@Component(value = "demo1Mapper")
public interface Demo1Mapper extends Mapper<Demo1> {
}

高版本MySQL中mapper-generator生成实体类的@Id问题

问题描述

MySQL版本 mapper-generator生成实体类是否包含@Id
5.x
6.x 没有
8.x 没有

解决办法

generatorConfig.xml中:

1
2
3
4
<jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost/my_schema"
            userId="my_user" password="my_password">
    <property name="nullCatalogMeansCurrent" value="true" />
</jdbcConnection>

参考


Wanglibing
Wanglibing
Engineer,Lifelong learner