MyBatis - CRUD 操作

news2024/11/25 18:45:18

文章目录

    • 1.环境配置
      • 1.1 导入相关依赖
      • 1.2 基本配置
      • 1.3 数据模型
    • 2.基于 XML 开发
      • 2.1 创建 Mapper 接口
      • 2.2 创建 XML 映射文件
      • 2.3 insert
      • 2.4 select
      • 2.5 delete
      • 2.6 update
      • 2.7 编写单元测试
    • 3.基于注解开发
      • 3.1 常用注解
      • 3.2 创建 Mapper 接口

MyBatis 支持通过 XML 和注解两种方式来配置映射。从早期的 iBatis 开始,XML 的功能就已经十分齐全,而注解的方式则是后来才出现的。至于使用 XML 方式还是注解方式,完全取决于个人偏好。

1.环境配置

创建一个 Spring Boot 项目,这里以 Spring Boot 2.7.5 + JDK1.8 为例。

1.1 导入相关依赖

首先,我们需要在 pom.xml 文件中添加 MyBatis 的 Starter 、数据源(以 Druid 为例)和对应的 MySQL 驱动依赖:

<!-- MyBatis -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.2.2</version>
</dependency>

<!-- 数据库驱动 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.28</version>
</dependency>

<!-- 数据库连接池 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.6</version>
</dependency>

1.2 基本配置

下面以 application.yml 为例进行一些基本的配置:

# 配置数据库
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/<your-database>?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: <password>

# 配置mybatis
mybatis:
  # 配置别名
  type-aliases-package: cn.javgo.learningmybatis.model
  configuration:
    # 开启驼峰命名
    map-underscore-to-camel-case: true

注意:需要将上述 <your-database> 替换为自己需要连接的数据库,并将 <password> 替换为正确的数据库连接密码。

1.3 数据模型

准备测试需要使用的数据表,我们以 Student 相关操作为例,对应的 student 数据表如下:

在 MyBatis 中,我们对实体类没有什么要求,也无须添加特定的注解,各种映射都是通过 Mapper 接口来定义的。对应的 cn.javgo.learningmybatis.model.Student 实体类如下:

@Data
public class Student {
    private Long id;
    private String name;
    private Integer age;
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
}

2.基于 XML 开发

2.1 创建 Mapper 接口

由于使用了 MyBatis 为了更加体现其特点,笔者将原本持久层的 dao 包名对应修改为了 mapper,并在其中创建了一个 Mapper 接口 StudentMapper。在该接口中我们就可以定义一些基本的 CRUD 操作了,示例代码如下:

@Repository
public interface StudentMapper {
    
    /**
     * 插入学生
     * @param student 学生
     * @return 影响行数
     */
    int insert(Student student);

    /**
     * 根据id查询学生
     * @param id 学生id
     * @return 学生
     */
    Student selectById(Long id);

    /**
     * 查询所有学生
     * @param student 学生
     * @return 学生列表
     */
    List<Student> selectAll();

    /**
     * 根据id更新学生
     * @param student 学生
     * @return 影响行数
     */
    int updateById(Student student);

    /**
     * 根据id删除学生
     * @param id 学生id
     * @return 影响行数
     */
    int deleteById(Long id);
}

然后添加 Java 配置,通过 @MapperScan 配置好 Mapper 接口路径,让 Spring Boot 能自动扫描到:

@MapperScan("cn.javgo.learningmybatis.mapper")
@SpringBootConfiguration
public class MyBatisConfig {
    
}

2.2 创建 XML 映射文件

Mapper 接口创建好之后,我们需要准备一个同名的 XML 文件并在其中编写具体的 XML 语句。对于映射文件的位置老项目一般就与 Mapper 接口放在同一个包下,但是更推荐将所有映射文件放在 resources 资源目录下,例如我们就将所有的映射文件放在了 resources/mapper 目录下:

为了能够找到这些映射文件,我们需要在配置文件中添加相对应的配置:

# 配置mybatis
mybatis:
  # 配置别名
  type-aliases-package: cn.javgo.learningmybatis.model
  # 配置mapper的扫描路径(使用通配符)
  mapper-locations: classpath:mapper/*.xml
  configuration:
    # 开启驼峰命名
    map-underscore-to-camel-case: true

一个基本的 XML 映射文件内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.javgo.learningmybatis.mapper.StudentMapper">
    <!-- 编写各种 XML 语句 -->
</mapper>

上面的 namespace 的值就是对应的 Mapper 接口的全路径类名。

2.3 insert

<insert id="insert" parameterType="Student">
    insert into student (name, age,create_time,update_time) values (#{name}, #{age},now(),now())
</insert>

上述新增语句中的 id 对应的就是 StudentMapper 接口中的方法名,parameterType 对应的是 StudentMapper 接口中的方法参数类型。原则上应该写全类名 cn.javgo.learningmybatis.model.Student,但是由于我们在配置文件中配置了 mybatis.type-aliases-package=cn.javgo.learningmybatis.model 配置了包路径的别名,MyBatis 就会按照该路径进行查找了。

当在 MyBatis 中方法参数多于一个时,可以使用 @Param 注解来指定每个参数的名称,以便与 SQL 语句中的占位符进行匹配。下面是使用 @Param 注解的示例:

int insert(@Param("name") String name,@Param("age") Integer age);

一般来说,在插入语句执行后,我们都需要返回生成的自增 ID。在 XML 映射文件中,可以使用 useGeneratedKeys 属性和 keyProperty 属性来配置自增 ID 的获取。这种方式适用于支持 JDBC 3.0 以上版本的数据库。

<insert id="insert" parameterType="Student" useGeneratedKeys="true" keyProperty="id">
    insert into student (name, age,create_time,update_time) values (#{name}, #{age},now(),now())
</insert>

在执行完插入语句后,就可以通过 user.getId() 方法获取自增 ID。

除了上面的方式外,我们还可以使用 selectKey 元素来获取自增 ID。这种方式适用于不支持 JDBC 3.0 以上版本的数据库或者需要在插入语句执行前获取自增 ID 的情况。

<insert id="insert" parameterType="Student" useGeneratedKeys="true" keyProperty="id">
    insert into student (name, age,create_time,update_time) values (#{name}, #{age},now(),now())
    <selectKey keyProperty="id" resultType="long" order="AFTER">
        select last_insert_id()
    </selectKey>
</insert>

TIP:

在 MyBatis 中,可以在映射文件中省略 parameterType 的情况有以下几种:

  1. 使用注解方式:如果你使用 MyBatis 的注解方式进行 SQL 的编写,映射文件是可选的,因此不需要指定 parameterType
  2. 使用参数注解:当方法只有一个参数,并且该参数在 SQL 语句中被使用时,可以省略 parameterType。MyBatis 会自动将参数对象传递给 SQL 语句。

基于上述知识点的学习,对于插入方法我们的 SQL 可以按如下方式写:

<insert id="insert" useGeneratedKeys="true" keyProperty="id">
    insert into student (name, age,create_time,update_time) values (#{name}, #{age},now(),now())
</insert>

2.4 select

<!-- 使用resultType定义返回的结果类型 -->
<select id="selectById" resultType="Student">
    select id,name,age,create_time,update_time from student where id = #{id}
</select>

<!-- 使用resultMap定义映射关系 -->
<select id="selectAll" resultMap="studentMap">
    select id,name,age,create_time,update_time from student
</select>

<!-- 定义返回的结果集映射
    id:结果集映射的唯一标识
    type:结果集映射的类型
    property:结果集映射的属性
    column:结果集映射的列
-->
<resultMap id="studentMap" type="Student">
    <id property="id" column="id"/>
    <result property="name" column="name"/>
    <result property="age" column="age"/>
    <result property="createTime" column="create_time"/>
    <result property="updateTime" column="update_time"/>
</resultMap>

上述 SQL 中,我们使用 resultType 指定方法的返回值类型,同时通过 resultMap 元素定义了一个返回结果集将查询到的字段进行一一映射为一个 Student 对象。

TIP:

  1. 如果你不写 resultMap,MyBatis 仍然会自动将查询到的数据封装为 Student 对象,但是会按照默认规则进行映射。默认规则是将查询结果的列名与 Student 对象的属性名进行匹配。
  2. 通过编写 resultMap,你可以定义自己的映射规则,可以进行更细粒度的控制。例如,你可以指定不同的列名与属性名之间的映射关系,可以处理一对多关系,可以处理复杂类型等等。此外,你还可以在 resultMap 中定义一些特殊的映射处理逻辑,如类型转换、联合查询等。

由于上述示例我们使用了默认的映射规则,故可以进行简化如下:

<select id="selectById" resultType="Student">
    select id,name,age,create_time,update_time from student where id = #{id}
</select>

<select id="selectAll" resultType="Student">
    select id,name,age,create_time,update_time from student
</select>

2.5 delete

删除操作使用的知识点与插入操作一致,无非就是换了以下标签名,此处不再赘述。

<delete id="deleteById">
    delete from student where id = #{id}
</delete>

2.6 update

更新操作使用的知识点与插入操作一致,无非就是换了以下标签名,此处不再赘述。

<update id="updateById">
    update student set name = #{name}, age = #{age}, update_time = now() where id = #{id}
</update>

2.7 编写单元测试

下面是一个完整的单元测试案例,用于测试上述每个方法:

@SpringBootTest
public class StudentMapperTest {
    @Autowired
    private StudentMapper studentMapper;

    @Test
    void testInsert() {
        // 创建Student对象
        Student student = new Student();
        student.setName("张三");
        student.setAge(20);

        // 插入数据
        int result = studentMapper.insert(student);

        // 判断是否插入成功
        assertEquals(1, result);
        assertNotNull(student.getId());
    }

    @Test
    void testSelectById() {
        // 根据id查询
        Student student = studentMapper.selectById(1L);

        // 判断是否查询到数据
        assertNotNull(student);
        assertEquals("张三", student.getName());
        assertEquals(20, student.getAge());
    }

    @Test
    void testSelectAll() {
        // 查询所有学生
        List<Student> studentList = studentMapper.selectAll();

        // 判断是否查询到数据
        assertNotNull(studentList);
        assertEquals(1, studentList.size());
    }

    @Test
    void testUpdateById() {
        // 根据id查询
        Student student = studentMapper.selectById(1L);

        // 修改学生信息
        student.setName("李四");
        student.setAge(30);

        // 更新数据
        int result = studentMapper.updateById(student);

        // 判断是否更新成功
        assertEquals(1, result);

        // 再次查询
        student = studentMapper.selectById(1L);

        // 判断是否更新成功
        assertNotNull(student);
        assertEquals("李四", student.getName());
        assertEquals(30, student.getAge());
    }

    @Test
    void testDeleteById(){
        // 根据id查询
        Student student = studentMapper.selectById(1L);

        // 判断是否查询到数据
        assertNotNull(student);

        // 删除数据
        int result = studentMapper.deleteById(1L);

        // 判断是否删除成功
        assertEquals(1, result);
    }
}

TIP:

Spring Framework 为测试提供了强大的支持,在涉及数据库操作时,为了保证每个测试的运行不会对其他测试产生影响,它可以直接回滚测试中的操作,这也是默认的逻辑。因此,即使不添加 @Rollback 注解,也能达到同样的效果。如果希望测试代码的变动被提交到数据库中,可以使用 @Commit@Rollback(false)。当然,这里的前提是必须存在事务,因此可以在测试方法尚上添加 @Transactional 注解开启此效果。

3.基于注解开发

3.1 常用注解

以下是 Spring Boot 集成 MyBatis 常用的一些注解:

注解说明
@Mapper标识该接口是一个 MyBatis Mapper 接口,Spring Boot 将自动扫描到这个接口并将其实例化为一个 Bean 以供使用。
@Select提供 SQL 查询语句,用于执行 SELECT 操作。
@Update提供 SQL 更新语句,用于执行 UPDATE 操作。
@Insert提供 SQL 插入语句,用于执行 INSERT 操作。
@Delete提供 SQL 删除语句,用于执行 DELETE 操作。
@Results结合 @Result 使用,提供复杂的结果映射。
@Result结合 @Results 使用,为 @Select 提供结果映射。
@Param在方法参数前使用,为参数命名,以使 SQL 语句中引用参数更清晰。
@Options提供对调用数据库函数时的更多选项,如自动生成键、执行前刷新缓存等。
@ResultMap引用一个已经定义的 @Results 注解,避免重复定义。
@MapperScan在 Spring Boot 应用启动类上使用,用于指定 MyBatis Mapper 接口的包路径,让 Spring Boot 能自动扫描到。
@One用于配置一对一的关联关系
@Many用于配置一对多的关联关系

3.2 创建 Mapper 接口

使用基于注解的方式我们便不再需要编写 XML 映射文件了,而是直接将 SQL 通过一系列注解直接卸载 Mapper 接口对应的方法上即可。既然不需要 XLM 映射文件,对应的我们也就没必要配置 mybatis.mapper-locations=classpath:mapper/*.xml 了。同时将前面的 @Repository 注解对应替换为 @Mapper,标识该接口是一个 MyBatis Mapper 接口,Spring Boot 将自动扫描到这个接口并将其实例化为一个 Bean 以供使用。

下面是一个等效的 StudentMapper

@Mapper
public interface StudentMapper {
    /**
     * 插入学生并返回主键
     * @param student 学生
     * @return 影响行数
     */
    @Insert("insert into student(name, age,create_time,update_time) values(#{name}, #{age},now(),now())")
    @Options(useGeneratedKeys = true, keyProperty = "id")
    int insert(Student student);

    /**
     * 根据id查询学生
     * @param id 学生id
     * @return 学生
     */
    @Select("select * from student where id = #{id}")
    @ResultMap("studentMap")
    Student selectById(Long id);

    /**
     * 查询所有学生
     * @return 学生列表
     */
    @Select("select * from student")
    @Results(id = "studentMap", value ={
            @Result(property = "id", column = "id"),
            @Result(property = "name", column = "name"),
            @Result(property = "age", column = "age"),
            @Result(property = "createTime", column = "create_time"),
            @Result(property = "updateTime", column = "update_time")
    })
    List<Student> selectAll();

    /**
     * 根据id更新学生
     * @param student 学生
     * @return 影响行数
     */
    @Update("update student set name = #{name}, age = #{age}, update_time = now() where id = #{id}")
    int updateById(Student student);

    /**
     * 根据id删除学生
     * @param id 学生id
     * @return 影响行数
     */
    @Delete("delete from student where id = #{id}")
    int deleteById(Long id);
}

测试类可以使用同一套,但是需要进行一些修改,因为自增 ID 肯定不是之前的了,胖友自行进行单元测试。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/598961.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

chatgpt赋能python:Python运行程序没反应怎么办?

Python运行程序没反应怎么办&#xff1f; Python作为一种高级编程语言&#xff0c;已经成为了很多开发者的首选语言。然而&#xff0c;在使用Python编写程序时&#xff0c;有时候会出现运行程序却没有任何反应的情况。这是什么原因导致的呢&#xff1f;本文将为大家介绍Python…

单例模式的饿/懒汉模式

目录 1. 什么是单例模式2. 饿汉模式2.1 饿汉模式概念2.2 饿汉模式代码 3. 懒汉模式3.1 懒汉模式概念3.2 单线程情况下的懒汉模式3.3 单例模式的写法(保证线程安全) 4. wait 和 sleep 的区别 1. 什么是单例模式 保证某个类在程序中只存在一份实例&#xff0c;而不会创建多个实例…

Apache Kafka - 跨集群数据镜像 MirrorMaker

文章目录 概述跨集群数据镜像的原理MirrorMaker配置小结 概述 在分布式系统中&#xff0c;数据镜像是一项重要的功能&#xff0c;它可以将数据从一个集群复制到另一个集群&#xff0c;以保证数据的高可用性和容错性。Apache Kafka是一个流处理平台&#xff0c;它提供了一种跨集…

程序设计综合实习(C语言):学生成绩单制作

一、目的 1&#xff0e;掌握结构体变量及数组的定义、赋值、初始化、输入、输出 2&#xff0e;结构体数组的操作。 二、实习环境 Visual Stdio 2022 三、实习内容、步骤与要求 1&#xff0e;定义一个结构体数组&#xff0c;存放10个学生的学号&#xff0c;姓名&#xff0c;三…

Linux 设备树文件手动编译的 shell 脚本

前言 前面通过 Makefile 实现手动编译 Linux 设备树 dts 源文件及其 设备树依赖 dtsi、.h 头文件&#xff0c;如何写成一个 shell 脚本&#xff0c;直接编译呢&#xff1f; 其实就是 把 Makefile 重新编写为 shell 脚本即可 编译设备树 shell 脚本 脚本内容如下&#xff1a…

【六一 iKun】Happy LiuYi, iKuns

六一了&#xff0c;放松下。 Python iKun from turtle import * screensize(1000,1000) speed(6)#把衣服画出来&#xff0c;从右肩膀开始#领子 penup() goto(-141,-179) pensize(3) fillcolor("black") pencolor("black") begin_fill() pendown() left(1)…

【Python实战】Python采集高校信息

前言 大家好,我们今天来爬取某站的高校名单,把其高校名单,成员和内容数获取下来,不过,我们发现这个网站比我们平时多了一个验证,下面看看我是怎么解决的。 环境使用 python 3.9pycharm模块使用 requests模块介绍 requests requests是一个很实用的Python HTTP客户端…

【线性dp必学四道题】线性dp四道经典例题【最长上升子序列】、【最长公共子序列】、【最长公共上升子序列(maxv的由来)】【最长公共子串】

【最长上升子序列】、【最长公共子序列】、【最长公共上升子序列】 最长上升子序列f[i] 表示以i结尾的最长子序列 最长公共子序列f[i][j] 表示 a前i 和 b前j个 最长公共长度 最长公共上升子序列f[i][j]代表所有a[1 ~ i]和b[1 ~ j]中以b[j]结尾的公共上升子序列的集合 最长公共子…

Spring Boot如何实现分布式追踪和监控

Spring Boot如何实现分布式追踪和监控 在分布式系统中&#xff0c;由于服务数量的增加和服务之间的相互调用&#xff0c;会出现跨服务的请求链路较长&#xff0c;难以追踪问题和定位性能瓶颈等问题。因此&#xff0c;分布式追踪和监控变得越来越重要。本文将介绍如何使用 Spri…

怎样一元钱部署自己的AI网站

前段时间我开发了一个简洁的AI问答网站&#xff0c;好多朋友感兴趣&#xff0c;因此我将网站代码在github上开源&#xff0c;并编写此教程&#xff0c;帮助大家快速部署自己的AI网站&#xff0c;会编程的朋友们也可在此基础上定制开发。 前提条件&#xff1a;有自己的ChatGPT账…

AI实战营:开源计算机视觉神器OpenMMLab

目录 OpenMMLab概述 OpenMMLab各开源算法库详细介绍 OpenMMLab开源生态 OpenMMLab概述 部署框架&#xff1a;MMdeploy 算法框架&#xff1a;MMPretrain预训练多模态、MMDetection目标检测、MMDetection3D目标检测、MMRotate旋转目标检测、MMSegmentation语义分割、MMPose姿…

操作系统_进程

操作系统_进程 1 冯•诺依曼体系结构2 操作系统&#xff08;Operator System&#xff09;2.1 设计OS的目的2.2 OS的定位 3 进程3.1 什么是进程&#xff1f;3.2 查看进程3.3 通过fork创建进程使用 if 进行分流如何杀死一个进程 3.4 进程状态R - 运行状态S - 浅睡眠状态D - 磁盘休…

Java中关于ConditionObject的signal()方法的分析

代码块的展示 isHeldExclusively()这个仅持有锁资源的方法&#xff0c;在ReentrantLock中重写进行判断&#xff0c;要是没有持有锁资源那么会返回false&#xff0c;就会出现直接抛异常IllegalMonitorStateException&#xff08;非法监视器状态异常&#xff09;获取排在Conditi…

zookeeper相关,安装,认识......

目录 Zookeeper 1 第一章 Zookeeper简介... 1 1.1 Zookeeper概述和功能... 1 1.2 Zookeper安装... 1 1.3 Zookeper数据模型... 3 第二章 Zookeeper命令操作... 4 1.1 Zookeeper Client 4 1.2 Zookeeper JavaClient 6 第三章 集群角色... 28 Zookeeper 第一章 Zookeepe…

详解Servlet API

目录 前言 HttpServlet HttpServletRequest 代码实例 打印请求信息 通过URL中的queryString进行传递。 通过post请求的body&#xff0c;使用form表单传递 通过POST 请求中的 body 按照 JSON 的格式进行传递 HttpServletResponse 核心方法代码实例 设置状态码 自动刷…

【C++初阶】string 类的认识与学习

在学习string类之前&#xff0c;先在这里推荐2个好用的网站&#xff0c;可以用来查阅C的相关知识 https://cplusplus.com https://en.cppreference.com/w/ 上面的是非官方的&#xff0c;下面的官方的&#xff0c;但是个人感觉还是上面的好用。 一.string 类是什么 简单来说&…

State of GPT (ChatGPT 原理及现状介绍)

State of GPT 演讲信息&#xff1a; 演讲人&#xff1a;Andrej Karpathy (现在OpenAI任职)&#xff0c;之前是特斯拉视觉研发负责人&#xff0c;斯坦福深度学习入门课程 CS231N 讲师演讲主题&#xff1a;受到微软 BUILD2023 邀请&#xff0c;介绍 GPT 的原理及研发现状&#x…

VMware虚拟机安装Ubuntu 22.04详细教程(2023年新版教程)

VMware虚拟机安装Ubuntu 22.04详细教程&#xff08;保姆级教程&#xff09; 大家好&#xff0c;我是洲洲&#xff0c;欢迎关注&#xff0c;一个爱听周杰伦的程序员。关注公众号【程序员洲洲】即可获得10G学习资料、面试笔记、大厂独家学习体系路线等…还可以加入技术交流群欢…

代码创作世界——pytorch深度学习框架数据类型

代码创作世界——pytorch深度学习框架数据类型 torch中的数据类型张量&#xff08;tensor&#xff09; pytorch中的 在数学中&#xff0c;一个单独的数可以成为标量&#xff0c;一行或者一列数组可以称为向量&#xff0c;一个二维数组称为一个矩阵&#xff0c;矩阵中的每一个元…

编译安装nginx服务

目录 编译安装nginx服务 1.关闭防火墙&#xff0c;将安装nginx所需软件包传到/opt目录下 2.安装依赖包 3.创建运行用户、组&#xff08;Nginx服务程序默认以nobody身份运行&#xff0c;建议为其创建专门的用户账号&#xff0c;以便更准确的控制访问权限&#xff09; 4.编译…