最简单的通用Mapper的使用手册不了解一下?

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS PostgreSQL,高可用系列 2核4GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 通用Mapper 是一个 Mybatis 的增强工具,在 Mybatis 的基础上只做增强不做改变,为简化开发、提高效率而生。开发人员可以随意的按照自己的需要选择通用方法,还可以很方便的开发自己的通用方法,极其方便的使用MyBatis单表的增删改查。

通用Mapper的定义

通用Mapper 是一个 Mybatis 的增强工具,在 Mybatis 的基础上只做增强不做改变,为简化开发、提高效率而生。开发人员可以随意的按照自己的需要选择通用方法,还可以很方便的开发自己的通用方法,极其方便的使用MyBatis单表的增删改查。

本文下从如下三个方面来介绍:

1.SpringBoot如何整合通用mapper

2.如何使用通用Mapper的方法

3.通用方法怎么来的

SpringBoot如何整合通用mapper

第一步:引入依赖

<!--mapper需要依赖jpa-->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
  </dependency>
  <!--MyBatis 通用 Mapper-->
  <dependency>
    <groupId>tk.mybatis</groupId>
    <artifactId>mapper-spring-boot-starter</artifactId>
    <version>1.1.4</version>
  </dependency>
  <!-- SpringBoot - MyBatis 逆向工程 -->
  <dependency>
    <groupId>org.mybatis.generator</groupId>
    <artifactId>mybatis-generator-core</artifactId>
    <version>1.3.2</version>
  </dependency>
  <!-- mysql连接需要的类 -->
  <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
  </dependency>

如上我们需要引入四个依赖一个是jpa依赖,一个是通用Mapper自身的依赖,一个是MyBatis逆向工程需要的依赖,最后就是连接mysql的依赖。依赖引入之后,接下来就是配置逆向工程。(PS:逆向工程就是帮助我们生成Model,Dao以及XML)。


第二步:配置逆向工程

首先我们需要在resources目录下新建一个属性文件config.properties用来定义数据库连接,生成类的目标包等信息。定义如下:

#jdbcConnection 连接
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatisdemo?useUnicode=true&characterEncoding=utf8
jdbc.user=root
jdbc.password=admin
#targetProject是包所在的位置
#mac 下
targetProject=/Volumes/Develop/WorkSpace/auto_java/auto-mapper-demo/src/main/
#model的目标包名
modelTargetPackage=com.jay.model
#dao的目标包名
daoTargetPackage=com.jay.mapper

前面我们新建的属性文件其实是给后面的XML文件用的,接下来我们就来看看XML文件generatorConfig.xml吧。

<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
    <!--引入属性文件-->
    <properties resource="config.properties"/>
    <context id="Mysql" targetRuntime="MyBatis3Simple" defaultModelType="flat">
        <property name="beginningDelimiter" value="`"/>
        <property name="endingDelimiter" value="`"/>
        <!--配置生成类的插件-->
        <plugin type="tk.mybatis.mapper.generator.MapperPlugin">
            <property name="mappers" value="tk.mybatis.mapper.common.Mapper"/>
            <property name="caseSensitive" value="true"/>
        </plugin>
        <!--连接数据库-->
        <jdbcConnection driverClass="${jdbc.driverClass}"
                        connectionURL="${jdbc.url}"
                        userId="${jdbc.user}"
                        password="${jdbc.password}">
        </jdbcConnection>
        <!--指定model的生成路径-->
        <javaModelGenerator targetPackage="${modelTargetPackage}"
                            targetProject="${targetProject}/java"/>
        <!--指定mapper的生成路径-->
        <sqlMapGenerator targetPackage="mapper"
                         targetProject="${targetProject}/resources"/>
        <!--指定dao的生成路径-->
        <javaClientGenerator targetPackage="${daoTargetPackage}"
                             targetProject="${targetProject}/java"
                             type="XMLMAPPER"/>
        <!--指定需要生成Model,Dao,Xml的数据表-->
        <table tableName="classroom"></table>
    </context>
</generatorConfiguration>

需要注意的是定义的属性名要与属性文件中的一致,比如jdbc.driverClass


第三步:定义逆向工程的启动类

将相关的必要的配置弄好之后,接下来我们就来定义逆向工程的启动类。这个启动类的代码也相对比较简单,代码如下所示:

public class Generator {
    public static InputStream getResourceAsStream(String path){
        return Thread.currentThread().getContextClassLoader().getResourceAsStream(path);
    }
    public static void main(String[] args) throws Exception {
        List<String> warnings = new ArrayList<String>();
        boolean overwrite = true;
        ConfigurationParser cp = new ConfigurationParser(warnings);
        Configuration config = cp.parseConfiguration(getResourceAsStream("generatorConfig.xml"));
        DefaultShellCallback callback = new DefaultShellCallback(overwrite);
        MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
        myBatisGenerator.generate(null);
        for (String warning : warnings) {
            System.out.println(warning);
        }
    }
}

第四步 测试逆向工程

准备工作都做好之后,接下来,我们就来测试生成model,dao,XML等文件吧。我们直接运行Generator类正常的话就可以得到生成的Model,Dao和XML,生成结果如下图所示:

0186dfd287e5144196ab6d671adb45b9_20200308171626517.jpg

怎么用?

如何调用方法?

说完了逆向工程,生成了我们需要的Dao类之后,接下来我们就要看看怎么使用了Dao类,调用其拥有的通用方法,其实调用也相当简单。下面我先给出一个调用示例:

我们定义了一个ClassroomServiceImpl业务类,并且实现了三个方法,分别是调用insert方法保存班级,调用selectByPrimaryKey方法根据主键查找班级,调用select方法根据班级名称查找班级。这些方法都是ClassroomMapper接口中自带的方法。

@Service
public class ClassroomServiceImpl implements ClassroomService {
    @Autowired
    private ClassroomMapper classroomMapper;
  //保存班级
    @Override
    public boolean saveClassroom(Classroom classroom) {
        int result = classroomMapper.insert(classroom);
        return result == 1 ? true : false;
    }
  //根据主键查找
    @Override
    public Classroom getClassroomById(int id) {
        Classroom classroom = classroomMapper.selectByPrimaryKey(id);
        return classroom;
    }
  //根据某个字段查找
    @Override
    public List<Classroom> getClassroomByName(String name) {
        Classroom param = new Classroom();
        param.setName(name);
        List<Classroom> classrooms = classroomMapper.select(param);
        return classrooms;
    }
}

然后,我们在启动类中添加一个扫描Mapper的注解@MapperScan,这个注解一定要加,不然会找不到Mapper接口 。

@SpringBootApplication
//使之可以扫描到mapper接口
@MapperScan(basePackages = "com.jay.mapper")
public class AutoMapperDemoApplication {
  public static void main(String[] args) {
  SpringApplication.run(AutoMapperDemoApplication.class, args);
  System.out.println("********启动项目成功");
  }
}

添加完相关的方法之后,最后就是添加测试类测试这些方法是否起作用了,

@SpringBootTest
@RunWith(SpringRunner.class)
@Transactional
public class ClassroomServiceImplTest {
    @Autowired
    private ClassroomService classroomService;
    @Before
    public void setUp() {
        Classroom classroom = new Classroom();
        classroom.setId(3);
        classroom.setClassId(3);
        classroom.setClassType("0");
        classroom.setName("优秀的三班");
        classroomService.saveClassroom(classroom);
    }
  //测试保存班级
    @Test
    public void saveClassroom() throws Exception {
        Classroom classroom = new Classroom();
        classroom.setId(4);
        classroom.setClassId(4);
        classroom.setClassType("1");
        classroom.setName("优秀的四班");
        boolean b = classroomService.saveClassroom(classroom);
        Assert.assertTrue(b);
    }
  //测试主键查询
    @Test
    public void getClassroomById() throws Exception {
        Classroom classroomById = classroomService.getClassroomById(3);
        Assert.assertEquals("优秀的三班", classroomById.getName());
    }
  //测试按照名称查询
    @Test
    public void getClassroomByName() throws Exception {
        List<Classroom> classrooms = classroomService.getClassroomByName("优秀的三班");
        Assert.assertEquals("优秀的三班", classrooms.get(0).getName());
    }
}

运行测试用例的结果如下:

a73e22ba8cc8d7435dea6a5ae28fb4be_watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTQ1MzQ4MDg=,size_16,color_FFFFFF,t_70.jpg

方法哪里来的

说完了怎么用之后,通用Mapper的使用方法我们就说完了。接下来我们来看看ClassroomMapper中的方法是怎么来的吧,不看代码我们想当然的会以为这些方法会在ClassroomMapper接口中有定义,但事实是这样子的么? 这里先留一个彩蛋,请听我慢慢道来。

首先,我们来看看生成的XML文件。

c5ac45c104390dd4cdb87783dbef9139_watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTQ1MzQ4MDg=,size_16,color_FFFFFF,t_70.jpg

我们很惊奇的发现XML里面竟然一条SQL语句都莫得,那对应的ClassroomMapper接口中也应该没有定义相关的方法呀。那问题来了,这些个增删改查的方法是哪里来的呢?

2a889ba9bd2a7898983cdf6eef42e7be_watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTQ1MzQ4MDg=,size_16,color_FFFFFF,t_70.jpg

这有点让人摸不着头脑,百思不得姐。话不多说,还是让我们接着看看

ClassroomMapper接口吧!
public interface ClassroomMapper extends Mapper<Classroom> {
}

果然,一个方法都没有定义,只是继承了一个Mapper<Classroom>接口,那么这些方法就有应该来源于是Mapper接口了,为了验证我们的猜测,让我们一起来看看这个自带神力的Mapper接口吧。

Mapper

/**
 * 通用Mapper接口,其他接口继承该接口即可
 * <p/>
 * <p>这是一个例子,自己扩展时可以参考</p>
 * <p/>
 * <p>项目地址 : <a href="https://github.com/abel533/Mapper" target="_blank">https://github.com/abel533/Mapper</a></p>
 *
 * @param <T> 不能为空
 * @author liuzh
 */
public interface Mapper<T> extends
        BaseMapper<T>,
        ExampleMapper<T>,
        RowBoundsMapper<T>,
        Marker {
}

哦豁,这个Mapper接口里也没有金屋藏娇呀,同样的它也是一个方法都没有定义,只是很单纯的继承了BaseMapper,ExampleMapper,RowBoundsMapper和Marker这四个接口,其中Marker只是一个标记接口,暂不在我们的介绍范围内!(PS:不得不说这个框架的作者牛逼呀)按照上面分析的思路我们很自然的认为,这些方法应该是由上面的三个接口定义的。那么就让它们一一粉墨登场吧。

首先出场的是我们的 BaseMapper接口。

BaseMapper接口

/**
 * 通用Mapper接口,其他接口继承该接口即可
 * <p/>
 * <p>这是一个例子,自己扩展时可以参考</p>
 * <p/>
 * <p>项目地址 : <a href="https://github.com/abel533/Mapper" target="_blank">https://github.com/abel533/Mapper</a></p>
 *
 * @param <T> 不能为空
 * @author liuzh
 */
public interface BaseMapper<T> extends
        BaseSelectMapper<T>,
        BaseInsertMapper<T>,
        BaseUpdateMapper<T>,
        BaseDeleteMapper<T> {
}

朋友们,这完全是跟Mapper接口一模一样的套路呀。BaseMapper接口里面也是啥方法都没有定义,同样是继承了好几个接口,一个是BaseSelectMapper接口,一个是BaseInsertMapper接口,一个是BaseUpdateMapper接口,最后一个是BaseDeleteMapper。从接口的命名我们不难猜测出这些个接口的作用。下面我们就分别介绍下他们。


从BaseSelectMapper 接口的定义来看,其主要就是定义基础的查询,最简单的查询请交就给它吧。我们进入它的身体里一探究竟。

/**
 * 通用Mapper接口,基础查询
 *
 * @param <T> 不能为空
 * @author liuzh
 */
public interface BaseSelectMapper<T> extends
        SelectOneMapper<T>,
        SelectMapper<T>,
        SelectAllMapper<T>,
        SelectCountMapper<T>,
        SelectByPrimaryKeyMapper<T>,
        ExistsWithPrimaryKeyMapper<T> {
}

套路真的好深呀,进到BaseSelectMapper接还是没有看到有方法定义,同样的只是继承好些个接口。不要心急,不要烦躁,我们马上就可以看到真正干活的接口了。这里我选取SelectMapper接口做一个剖析。

/**
 * 通用Mapper接口,查询
 *
 * @param <T> 不能为空
 * @author liuzh
 */
public interface SelectMapper<T> {
    /**
     * 根据实体中的属性值进行查询,查询条件使用等号
     *
     * @param record
     * @return
     */
    @SelectProvider(type = BaseSelectProvider.class, method = "dynamicSQL")
    List<T> select(T record);
}

看到这儿朋友们是不是恍然大悟,这不就是前面classroomMapper.select(param)调用的这个方法么!!!这个方法是根据动态SQL(dynamicSQL)来执行SQL语句的。其只能根据实体中的属性值进行查询,并且查询条件使用等号。其他的接口也是一样的道理的。在此就不在赘述了。

总之就是一个接口里定义一个方法,并且这个方法的SQL是动态生成的。


ExampleMapper接口

说完了BaseMapper接口,接下来就让我们来看看ExampleMapper接口吧,这个接口的查询条件是传入Example来查询。例如:

可以直接带关键字查询,类似于动态的拼接SQL的方式。

@Override
    public List<Classroom> getClassByExample() {
        Example classRoomExample = new Example(Classroom.class);
        Example.Criteria criteria = classRoomExample.createCriteria();
        criteria.andLike("name", "%班%");
        criteria.andEqualTo("classType", "0");
        List<Classroom> classrooms = classroomMapper.selectByExample(classRoomExample);
        return classrooms;
    }

同样的ExampleMapper接口也是继承了SelectByExampleMapper,SelectCountByExampleMapper,DeleteByExampleMapper,UpdateByExampleMapper已经UpdateByExampleSelectiveMapper这五个接口,每个接口里面都定义了一个方法。也是通过动态SQL来生成执行语句。在此就不在赘述了。

/**
 * 通用Mapper接口,Example查询
 *
 * @param <T> 不能为空
 * @author liuzh
 */
public interface ExampleMapper<T> extends
        SelectByExampleMapper<T>,
        SelectCountByExampleMapper<T>,
        DeleteByExampleMapper<T>,
        UpdateByExampleMapper<T>,
        UpdateByExampleSelectiveMapper<T> {
}

最后,我们来看下RowBoundsMapper接口,这个接口继承了SelectByExampleRowBoundsMapper接口和SelectRowBoundsMapper接口。主要是配合分页插件PageHelper来实现分页查询的。分页查询可以详细看Mybatis-PageHelper分页插件的使用与相关原理分析。


RowBoundsMapper接口

/**
 * 通用Mapper接口,带RowBounds参数的查询
 * <p/>
 * 配合分页插件PageHelper可以实现物理分页
 * <p/>
 * PageHelper - http://git.oschina.net/free/Mybatis_PageHelper
 *
 * @param <T> 不能为空
 * @author liuzh
 */
public interface RowBoundsMapper<T> extends
        SelectByExampleRowBoundsMapper<T>,
        SelectRowBoundsMapper<T> {
}

总结

本文首先介绍了如何在SpringBoot中整合通用的Mapper,其中详细介绍了逆向工程的使用。接着就是介绍了通用方法的调用,通用Mapper运用动态SQL的方式,省去了编写SQL的繁琐,实现了单表的基本的增删改查方法,极大的提高了对单表操作的效率。最后就是介绍了通用Mapper内置的方法。希望对读者朋友们有所帮助。如有疑问欢迎与我联系。

参考

https://github.com/abel533/Mapper

源码地址:

https://github.com/XWxiaowei/auto-mapper-demo

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
相关文章
|
存储 Kubernetes 应用服务中间件
k8s 1.24.3版本使用nfs-provisioner4.0.0动态创建PV
k8s 1.24.3版本使用nfs-provisioner4.0.0动态创建PV
2680 0
|
3月前
|
存储 Java 区块链
Springboot应用开发:工具类整理
在实际的Springboot应用开发中,有很多类可作为工具类,这些类将实际开发中可能用到的重复性代码进行提取,方便在后续的开发中使用,在这里我对在开发中经常用到的工具类进行整理,方便自己之后查找,同时希望可以帮助到有实现相关功能的朋友。
194 1
|
流计算 Java SQL
Flink落HDFS数据按事件时间分区解决方案
0x1 摘要 Hive离线数仓中为了查询分析方便,几乎所有表都会划分分区,最为常见的是按天分区,Flink通过以下配置把数据写入HDFS, BucketingSink<Object> sink = new BucketingSink<>(path); //通过这样的方式来实现数据跨天分区 sink.
4554 0
|
SQL 前端开发 Java
编译JSqlParser4.6-4.7最新源代码
编译JSqlParser4.6-4.7最新源代码
319 0
|
11月前
|
XML Java 测试技术
什么是 JavaConfig?
什么是 JavaConfig?
304 7
|
机器学习/深度学习 人工智能 语音技术
情感识别与表达:FunAudioLLM的情感智能技术
【8月更文第28天】随着人工智能的发展,语音交互系统越来越普遍。其中,情感智能技术成为提高用户体验的关键因素之一。本文将探讨 FunAudioLLM 如何利用情感识别和表达技术来增强语音交互的真实感,并提供具体的代码示例。
1207 0
|
缓存 监控 Shell
使用 HBase Shell 进行数据的实时监控和备份
使用 HBase Shell 进行数据的实时监控和备份
412 6
|
消息中间件 缓存 Shell
RT-Thread记录(十七、AT组件 — ESP8266使用 at_device 软件包联网)
AT 组件:RT-Thread 一个比较典型的组件, 解决了不同网络模块AT命令之间的差异导致的重复开发的问题,大幅度简化了MCU+无线模块方案开发。
1547 0
RT-Thread记录(十七、AT组件 — ESP8266使用 at_device 软件包联网)
|
小程序 容灾
单点登录模式
单点登录模式
695 0
|
canal 缓存 关系型数据库
高并发场景下,6种方案,保证缓存和数据库的最终一致性!
在解决缓存一致性的过程中,有多种途径可以保证缓存的最终一致性,应该根据场景来设计合适的方案,读多写少的场景下,可以选择采用“Cache-Aside结合消费数据库日志做补偿”的方案,写多的场景下,可以选择采用“Write-Through结合分布式锁”的方案,写多的极端场景下,可以选择采用“Write-Behind”的方案。
2229 0