前言

有 Java Web 应用开发经验的同学应该很熟悉 Controller/Service/Dao 这样的三层结构设计,MyBatis 就是实现 Dao 层的主流方式之一,用于完成数据库的读写操作;Dao 层服务于 Service 层,用于完成完成业务逻辑操作。

本文聚焦于 SpringBoot 和 MyBatis 的整合使用,考虑到引入 Controller 和 Service 层描述起来会比较复杂,因此仅涉及 Dao 层。

创建项目/模块

在 Maven 项目 SpringBoot 中添加模块 mybatis 用于演示,mybatis pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springboot</artifactId>
<groupId>tech.exchange</groupId>
<version>0.1</version>
</parent> <modelVersion>4.0.0</modelVersion> <artifactId>mybatis</artifactId>
</project>

按常规套路,都是 Controller 调用 Service,Service 调用 Dao;如前所述,不引入 Controller 和 Service,那么怎么实现 Dao 的调用呢?

其实,SpringBoot 不仅可以是一个 Web 应用,也可以是一个 命令行(Console) 应用,类似于一个 Java Main 的应用程序。

SpringBoot Console Application

演示如何创建一个 SpringBoot 命令行应用。

  1. 添加依赖
    <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>

注意:Web 应用需要添加依赖 spring-boot-starter-web,命令行 应用需要添加依赖 spring-boot-starter,两者是不一样的。

  1. 创建 Main
package tech.exchange.springboot.mybatis;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; /**
* @author yurun
*/
@SpringBootApplication
public class Main implements CommandLineRunner { @Override
public void run(String... args) throws Exception { } public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
}

这里的 Main,本质就是一个 Java Main,只不过额外添加注解和实现特定接口方法。

CommandLineRunner

CommandLineRunner is a simple Spring Boot interface with a run method. Spring Boot will automatically call the run method of all beans implementing this interface after the application context has been loaded.

CommandLineRunner 是 SpringBoot 的一个接口,它只有一个 run 方法;SpringBoot 容器加载完成之后,所有实现 CommandLineRunner 接口的 Beans 都会被自动调用 run 方法。

Main 就相当于实现接口 CommandLineRunner 的一个特殊 Bean,SpringBoot 容器加载完成之后,run 方法会被自动执行。我们可以在 run 方法内部实现 Dao 的调用。

SpringBoot 集成 MyBatis

MyBatis 官方提供了 SpringBoot 的集成方案,过程很简单,添加依赖 mybatis-spring-boot-starter 即可:

    <dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>

SpringBoot 和 MyBatis 集成完成。

本文数据库使用 MySQL,添加驱动 mysql-connector-java :

    <dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>

创建数据库/表

创建演示使用的数据表 mytable:

CREATE TABLE `mytable` (
`id` int NOT NULL AUTO_INCREMENT,
`col1` varchar(45) COLLATE utf8mb4_unicode_ci NOT NULL,
`col2` varchar(45) COLLATE utf8mb4_unicode_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

其中,主键 id 使用自增(AUTO_INCREMENT)策略。

配置数据源/连接池

SpringBoot 和 数据库 的交互需要通过数据源(DataSource)实现,数据源仅需要使用配置文件(application.yml)声明相关属性即可,不需要额外操作。MyBatis 也会使用数据源完成自身的初始化。

数据源

创建 application.yml(src/main/resources/application.yml):

spring:
datasource:
url: jdbc:mysql://mysql_dev:13306/yurun?useUnicode=true&characterEncoding=UTF-8
username: root
password: root
  • url:MySQL 连接信息,包括地址、端口、数据库名称和其他参数;其中,建议设置 useUnicode=true 和 characterEncoding=UTF-8,避免出现中文乱码的情况;
  • username:MySQL 用户名;
  • password:MySQL 密码;

连接池

连接池就是缓存若干数据库(MySQL)的连接(Connection),避免连接的重复创建销毁,减少 SQL 执行耗时。

SpringBoot 集成 MyBatis 时,会自动集成连接池 HikariCP,这也是 SpringBoot 官方推荐使用的,连接池这里使用默认配置。

CRUD

MyBatis 对于数据库的读写操作是通过 Mapper 实现的,Mapper 有两种形式:

  • 接口(Interface) + XML
  • 接口(Interface) + 注解

这两种形式并没有绝对意义上的好坏之分,使用 接口 + 注解,只需要编写一个接口,实现方式会更简洁一些,但灵活性会相对弱一些;使用 接口 + XML,除编写接口之外,还需要编写额外的 XML 文件,但灵活性更强。

本文使用 接口 + XML 的形式,以读写数据表 mytable 为例,创建接口和XML文件。

创建接口

接口文件:src/main/java/tech/exchange/springboot/mybatis/dao/MyTableMapper.java

package tech.exchange.springboot.mybatis.dao;

import org.apache.ibatis.annotations.Mapper;

/**
* @author yurun
*/
@Mapper
public interface MyTableMapper {
}

创建 XML

XML文件:src/main/resources/mapper/MyTableMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="tech.exchange.springboot.mybatis.dao.MyTableMapper">
</mapper>

接口文件名称建议和XML文件名称保持一致(非强制)方便查找。

XML文件位于目录 src/main/resources/mapper,需要配置 application.yml,使得 Mapper 可以被 SpringBoot 正确扫描加载。

mybatis:
mapper-locations:
- classpath:mapper/*.xml

相当于告诉 SpringBoot,到类路径下的 mapper 目录下面扫描加载 Mapper。

XML namespace 用于绑定 Mapper 的接口和XML文件,两者必须一一对应,namespace 的值必须为接口的全类名。

创建实体类

package tech.exchange.springboot.mybatis.model;

/**
* @author yurun
*/
public class MyRow {
private int id;
private String col1;
private String col2; ......
}

实体类 MyRow 的字段与数据库表 MyTable 的字段一一对应(非强制),MyRow 的一个实例表示 MyTable 的一行记录。

Create

  1. 插入单行记录

    XML插入元素

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="tech.exchange.springboot.mybatis.dao.MyTableMapper">
    <insert id="insertMyRow">
    INSERT INTO
    mytable (col1, col2)
    VALUES
    (#{col1}, #{col2})
    </insert>
    </mapper>

    MyBatis XML 可以使用 #{name} 的形式声明参数;其中,name 为参数名称。

    那么,这些参数来源于哪里?元素 insert 有一个 属性 parameterType 用于指定参数类型,如:

      <insert id="insertMyRow" parameterType="tech.exchange.springboot.mybatis.model.MyRow">
    ......
    </insert>

    表示会使用 MyRow 的实例来传递元素 insert 所需的参数,#{col1} 和 #{col2} 分别对应着 MyRow (实例)的 字段 col1 和 col2。

    大多数情况下,我们可以省略属性 parameterType,MyBatis 会为我们自动推断这个类型。

    接口方法

    package tech.exchange.springboot.mybatis.dao;
    
    import org.apache.ibatis.annotations.Mapper;
    import tech.exchange.springboot.mybatis.model.MyRow; import java.util.List; /**
    * @author yurun
    */
    @Mapper
    public interface MyTableMapper {
    /**
    * 插入一行记录。
    *
    * @param row 一行记录
    * @return 影响行数
    */
    int insertMyRow(MyRow row);
    }

    接口方法名称需要与XML插入元素id保持一致,调用接口方法时会执行对应名称XML元素中的SQL语句。

    接口方法使用 MyRow 实例传入参数,XML插入元素 INSERT 语句中的参数 #{col1}、#{col2} 需要与实体类 MyRow 中的字段 col1、col2 名称保持一致(字段顺序可任意);也就是说,接口方法执行时,会将 MyRow 实例对象 row 中的字段值按名称赋值给 INSERT 语句中的各个参数。

    接口方法的返回值为影响行数,插入单选记录返回值为1。

    :插入、修改和删除的接口方法返回值均为影响行数。

    插入单行记录

    在 Main 中添加 MyTableMapper 实例 mapper:

      @Autowired
    private MyTableMapper mapper;

    @Autowired 表示 SpringBoot 会根据接口类型自动注入相应的实例。

    在 Main run 方法中添加插入记录的代码:

        MyRow row = new MyRow();
    
        row.setCol1("a");
    row.setCol2("b"); int value = mapper.insertMyRow(row);
    System.out.println(value);

    因为数据表 mytable 的主键 id 是自增的,所以不需要设置 MyRow id 的值。

    Main 完整代码:

    package tech.exchange.springboot.mybatis;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.CommandLineRunner;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import tech.exchange.springboot.mybatis.dao.MyTableMapper;
    import tech.exchange.springboot.mybatis.model.MyRow; /**
    * @author yurun
    */
    @SpringBootApplication
    public class Main implements CommandLineRunner { @Autowired
    private MyTableMapper mapper; @Override
    public void run(String... args) throws Exception {
    MyRow row = new MyRow(); row.setCol1("a");
    row.setCol2("b"); int value = mapper.insertMyRow(row);
    System.out.println(value);
    } public static void main(String[] args) {
    SpringApplication.run(Main.class, args);
    }
    }
  2. 插入单行记录,且获取已插入记录的主键ID

    如前文所述,数据表 mytable 主键字段 id 是支持自增的,这里所说的就是获取已插入记录 id 的自增值。

    XML插入元素

      <insert id="insertMyRow" useGeneratedKeys="true" keyProperty="id">
    INSERT INTO
    mytable (col1, col2)
    VALUES
    (#{col1}, #{col2})
    </insert>
    • useGeneratedKeys 值为 true,表示通知 MyBatis 使用 JDBC getGeneratedKeys 方法获取已插入记录自增主键的值;
    • keyProperty 值为 id,表示 MyBatis 会将获取到的自增主键的值,赋值给 MyRow 实例的字段 id;

    插入单行记录,且获取已插入记录的主键ID

        // 创建记录
    MyRow row = new MyRow(); row.setCol1("a");
    row.setCol2("b"); // 插入
    int value = mapper.insertMyRow(row);
    System.out.println(value); // 获取主键ID
    System.out.println(row.getId());

    插入方法执行完成之后

        mapper.insertMyRow(row)

    即可以获取主键ID

        row.getId()

    获取主键 ID 是通过 row 记录实例获取的。

  3. 插入多行记录

    插入多行记录和插入单行记录,大体类似,关键在于 INSERT 语句如何表述插入多行操作。

    XML插入元素

      <insert id="insertMyRows" useGeneratedKeys="true" keyProperty="id">
    INSERT INTO
    mytable (col1, col2)
    VALUES
    <foreach item="item" collection="list" separator=",">
    (#{item.col1}, #{item.col2})
    </foreach>
    </insert>

    插入多行记录的关键就是需要使用 foreach 元素,循环生成插入多行记录所需的 SQL 语句:

    • collection="list",表示接口方法会通过集合(List)传入多行记录,如:List;
    • item="item",表示处理某一行记录时,该行记录的名称(别名)为 item,可以通过 item 获取记录字段的值;如:item.col1,表示获取某个记录(row)字段 col1 的值;
    • separator=",",表示多行记录的处理结果以逗号进行连接;如:(a1, b1), (a2, b2)。

    还可以有另一种写法:

      <insert id="insertMyRows" useGeneratedKeys="true" keyProperty="id">
    <foreach item="item" collection="list" separator=";">
    INSERT INTO
    mytable (col1, col2)
    VALUES
    (#{item.col1}, #{item.col2})
    </foreach>
    </insert>

    同样是插入多行记录,第一种写法是通过一次请求(MySQL)执行一条 SQL 语句实现的,第二种写法是通过一次请求执行多条 SQL 语句实现的,这里仅仅是为了演示 foreach 的灵活用法,实际批量场景中不推荐这样使用。

    注意:MySQL 一次请求执行多条 SQL 语句需要数据库连接Url添加 allowMultiQueries=true。

    接口方法

      /**
    * 插入多行记录。
    *
    * @param rows 多行记录
    * @return 影响行数
    */
    int insertMyRows(List<MyRow> rows);

    相较于插入单行记录,接口方法的参数为 MyRow(单数);插入多行记录,接口方法的参数为 List(复数)。

    插入多行记录

        MyRow row1 = new MyRow();
    
        row1.setCol1("a1");
    row1.setCol2("b1"); MyRow row2 = new MyRow(); row2.setCol1("a2");
    row2.setCol2("b2"); // 多行记录
    List<MyRow> rows = new ArrayList<>(); rows.add(row1);
    rows.add(row2); // 插入,返回影响行数
    int value = mapper.insertMyRows(rows);
    System.out.println(value); rows.forEach(row -> {
    // 获取已插入记录的主键ID
    System.out.println(row.getId());
    });

    和插入单行记录类似,多行记录插入完成之后,每一条记录的 id 字段也会被 MyBatis 自动赋予相应的主键ID值。

Read

  1. 查询单行记录

    指定记录ID,查询相应的一行记录。

    XML查询元素

      <select id="selectMyRow" resultType="tech.exchange.springboot.mybatis.model.MyRow">
    SELECT
    *
    FROM
    mytable
    WHERE
    id = #{id}
    </select>

    元素 select 必须使用返回类型属性 resultType 声明查询返回的结果类型,这里为 tech.exchange.springboot.mybatis.model.MyRow,表示 MyBatis 会将查询到的字段值按数据表列名一一映射到 MyRow 实例的各个字段;如果数据表列名与 MyRow 的字段名称不一致,SELECT 语句可以使用别名(AS)重新定义列名:

        SELECT
    id,
    col1 AS col1,
    col2 AS col2
    FROM
    mytable
    WHERE
    id = #{id}

    接口方法

      /**
    * 查询一行记录
    *
    * @param id 记录ID
    * @return 记录
    */
    MyRow selectMyRow(int id);

    MyBatis 也是支持使用一个或多个基本类型变量传递参数的,注意名称要对应保持一致。

    查询一行记录

        int id = 1;
    // 查询记录
    MyRow row = mapper.selectMyRow(id); System.out.println(row.getId());
    System.out.println(row.getCol1());
    System.out.println(row.getCol2());

    可以看到,这里使用 insert 元素声明的返回类型 MyRow 的实例变量接收查询结果。

  2. 查询多行记录

    以查询数据表 mytable 的全部记录演示查询多行记录,也可以使用参数指定查询条件,参数使用方法如上方所示,不再赘述。

    XML查询元素

    <select id="selectMyRows" resultType="tech.exchange.springboot.mybatis.model.MyRow">
    SELECT
    *
    FROM
    mytable
    </select>

    查询多行记录,返回结果应该是一个类似 集合 的类型,但是 MyBatis 要求属性 resultType 声明的不是具体的集合类型(List),而是集合元素的类型,这里仍是 tech.exchange.springboot.mybatis.model.MyRow。

    接口方法

      /**
    * 查询多行记录
    *
    * @return 多行记录
    */
    List<MyRow> selectMyRows();

    查询多行记录

        List<MyRow> rows = mapper.selectMyRows();
    
        rows.forEach(System.out::println);

Update

修改数据表 mytable 指定主键ID的记录。

XML修改元素

  <update id="updateMyRow">
UPDATE
mytable
SET
col1 = #{col1},
col2 = #{col2}
WHERE
id = #{id}
</update>

接口方法

  /**
* 修改记录
*
* @param row 一行记录
* @return 影响行数
*/
int updateMyRow(MyRow row);

修改记录

    MyRow row = new MyRow();

    // 指定ID
row.setId(1); // 指定新的字段值
row.setCol1("c");
row.setCol2("d"); // 修改
int value = mapper.updateMyRow(row);
System.out.println(value);

Delete

删除数据表 mytable 指定主键ID的记录。

XML删除元素

  <delete id="deleteMyRow">
DELETE FROM
mytable
WHERE
id = #{id}
</delete>

接口方法

  /**
* 删除记录
*
* @param id 记录ID
* @return 影响行数
*/
int deleteMyRow(int id);

删除记录

    int id = 1;

    int value = mapper.deleteMyRow(id);
System.out.println(value);

小结

本文通过 SpringBoot 的命令行应用,演示 SpringBoot 和 MyBatis 的整体过程,以及实现基本 CRUD 的示例。

整体实践下来,发现 MyBatis 的使用是有套路可循的,对于某一张数据表的读写操作:

  1. 创建一个或多个实体类,用于数据交互;
  2. 创建一个 MyBatis Mapper,用于封装数据方法,Mapper 由两部分组成:Interface(接口) + XML;
  3. Interface 中的每一个方法(Method)对应着 XML 中的一个元素(Element, insert/select/update/delete);
  4. MyBatis Mapper 方法的调用执行,本质就是 SQL 语句的执行。

受限于篇幅,只能讨论 MyBatis 最基础的内容,帮助大家入门,详细内容请参考 MyTatis 官方文档

SpringBoot 整合 MyBatis,实现 CRUD 示例的更多相关文章

  1. SpringBoot 整合 Mybatis 进行CRUD测试开发

    今天来和大家分享下 Spring Boot 整合 MyBatis 的 CRUD 测试方法开发.因为 MyBaits 有两种开发形式,一种基于注解,一种基于 xml . SpringBoot配置文件也有 ...

  2. 【java框架】SpringBoot(7) -- SpringBoot整合MyBatis

    1.整合MyBatis操作 前面一篇提到了SpringBoot整合基础的数据源JDBC.Druid操作,实际项目中更常用的还是MyBatis框架,而SpringBoot整合MyBatis进行CRUD也 ...

  3. SpringBoot整合mybatis——配置mybatis驼峰命名规则自动转换

    一.简述 mybatis驼峰式命名规则自动转换: 使用前提:数据库表设计按照规范“字段名中各单词使用下划线"_"划分”: 使用好处:省去mapper.xml文件中繁琐编写表字段列表 ...

  4. SpringBoot 整合 Mybatis + Mysql——XML配置方式

    一.介绍 SpringBoot有两种方法与数据库建立连接,一种是集成Mybatis,另一种用JdbcTemplate,本文主要讨论集成Mybatis方式. SpringBoot整合Mybatis也有两 ...

  5. SpringBoot整合MyBatis与MySql8.0

    一.前言 之前已经有一篇文章讨论过SpringBoot整合MyBatis,因而此篇不在重复累赘,本文主要是最新版的SpringBoot2.0与MyBatis.最新MySQL8.0整合过程中遇到的问题进 ...

  6. SpringBoot数据访问(一) SpringBoot整合Mybatis

    前言 SpringData是Spring提供的一个用于简化数据库访问.支持云服务的开源框架.它是一个伞形项目,包含了大量关系型数据库及非关系型数据库的数据访问解决方案,其设计目的是为了使我们可以快速且 ...

  7. SpringBoot整合Mybatis之项目结构、数据源

    已经有好些日子没有总结了,不是变懒了,而是我一直在奋力学习springboot的路上,现在也算是完成了第一阶段的学习,今天给各位总结总结. 之前在网上找过不少关于springboot的教程,都是一些比 ...

  8. SpringBoot整合Mybatis【非注解版】

    接上文:SpringBoot整合Mybatis[注解版] 一.项目创建 新建一个工程 ​ 选择Spring Initializr,配置JDK版本 ​ 输入项目名 ​ 选择构建web项目所需的state ...

  9. SpringBoot整合Mybatis注解版---update出现org.apache.ibatis.binding.BindingException: Parameter 'XXX' not found. Available parameters are [arg1, arg0, param1, param2]

    SpringBoot整合Mybatis注解版---update时出现的问题 问题描述: 1.sql建表语句 DROP TABLE IF EXISTS `department`; CREATE TABL ...

  10. springboot学习随笔(四):Springboot整合mybatis(含generator自动生成代码)

    这章我们将通过springboot整合mybatis来操作数据库 以下内容分为两部分,一部分主要介绍generator自动生成代码,生成model.dao层接口.dao接口对应的sql配置文件 第一部 ...

随机推荐

  1. BOOST内存管理-intrusive_ptr

    参考链接https://blog.csdn.net/harbinzju/article/details/6754646 intrusive_ptr 是shared_ptr的插入式版本.与shared_ ...

  2. fd定时器--timerfd学习

    定时器 可以用系统定时器信号SIGALARM 最近工作需要于是又发现了一个新玩意timerfd配合epoll使用. man 手册看一下 TIMERFD_CREATE(2) Linux Programm ...

  3. 腾讯云星星海SA2云服务器特点

    一.腾讯云星星海SA2云服务器特点 腾讯云深度定制AMD处理器.AMD EPYC ROME ,频率3.3Ghz.提供超大单核 L3 Cache.(基础频率2.6Ghz,睿频3.3Ghz).企业级服务器 ...

  4. spring security之 默认登录页源码跟踪

    spring security之 默认登录页源码跟踪 ​ 2021年的最后2个月,立个flag,要把Spring Security和Spring Security OAuth2的应用及主流程源码研究透 ...

  5. c++ IO库

    1:为了支持使用宽字符的语言,标准库定义了一组类型和对象来操作wchar_t类型的数据.宽字符版本的类型和函数的名字以w开头.宽字符版本和普通的char版本定义在同一个头文件中,例如头文件fstrea ...

  6. Vue首屏性能优化组件

    Vue首屏性能优化组件 简单实现一个Vue首屏性能优化组件,现代化浏览器提供了很多新接口,在不考虑IE兼容性的情况下,这些接口可以很大程度上减少编写代码的工作量以及做一些性能优化方面的事情,当然为了考 ...

  7. Java测试开发--JSONPath、JSONArray、JSONObject使用(十)

    一.Maven项目,pom.xml文件中导入 <dependency> <groupId>com.alibaba</groupId> <artifactId& ...

  8. JavaScript数组方法大集合

    JavaScript数组方法集合 本文总结一下js数组处理用到的所有的方法.自己做个笔记. 数组方法 concat() 合并两个或多个数组 concat()能合并两个或者多个数组,不会更改当前数组,而 ...

  9. appium环境搭建基于安卓(mac系统)

    1.需要环境 JDK Python Andriod SDK Node.js Appium Appium-Python-Client Appium-doctor 2.安装jdk(我的版本是1.8) 下载 ...

  10. golang常用库:日志记录库-logrus使用

    介绍 logrus 它是一个结构化.插件化的日志记录库.完全兼容 golang 标准库中的日志模块.它还内置了 2 种日志输出格式 JSONFormatter 和 TextFormatter,来定义输 ...