Mybatis:灵活掌控SQL艺术

发布于:2025-06-02 ⋅ 阅读:(19) ⋅ 点赞:(0)

在前面的文章中,小编分享了spring中相关的知识,但是没有分享到,如何去更高效操作数据库。

操作数据库传统的方法就是通过JDBC来进行操作。

这个传统方法使用上可谓是够麻烦的

  • 1.首先创建一个数据源对象
  • 2.设置该数据源的属性(密码、用户、位置……)
  • 3.获取数据库的连接
  • 3.构建sql语句、预编译sql语句、发送sql语句

这些操作,应用到开发中,是比较麻烦的,所以Mybatis就应运而生了。

Mybatis

这是一个优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射。

Mybatis避免几乎所有的JDBC代码和手动设置参数以及获取结果集的过程。

它最初是一个Apache软件基金会下发起的一个项目,最初名为ibatis,随着项目的演进,提供到的功能越来越多

ibatis团队决定从Apache软件基金会迁移出来,并改名为Mybatis。

持久层框架:持久层框架是一种用于简化数据库访问代码的软件工具,它位于应用程序的业务逻辑层和数据存储层之间。

Mybatis的核心功能:

  • SQL映射:Mybatis允许你通过XML或者注解的方式来编写SQL语句,并将其与Java方法进行映射。
  • 对象关系映射:通过简单的配置,可以告诉Mybatis如何将数据库表的列映射到Java对象属性上
  • 事务管理:Mybatis支持声明式事务管理,通常与Spring等框架集成使用,可以让你轻松管理事务,确保数据的一致性和完整性。
  • 缓存机制:Mybatis提供了一级缓存和二级缓存的支持。一级缓存是SqlSession级别的缓存,默认开启且不能关闭,二级缓存则是跨SqlSession的缓存,需要手动配置启用。合理运用缓存,可以提高应用性能
  • 动态SQL:Mybatis提供了强大的动态SQL功能,允许你根据不同的条件动态生成SQL语句。

那么接下来小编就来分享下Mybatis如何进行操作数据库的方式。

操作数据的方式,有两个方式,一是注解,二是XML。

注解:

准备工作:

引入依赖:

XML
<!--    这个依赖项用于将 MyBatis 集成到 Spring Boot 应用程序中。它简化了配置过程,并提供了自动配置功能,使得开发者可以更轻松地使用 MyBatis 进行数据库操作。此外,它还整合了 MyBatis 和 Spring 的事务管理、依赖注入等功能。-->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>3.0.4</version>
</dependency>
<!--此依赖项是 MySQL 数据库的 JDBC 驱动程序,允许你的应用程序连接到 MySQL 数据库。<scope>runtime</scope> 表示该依赖仅在运行时需要,编译期间不需要。-->
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <scope>runtime</scope>
</dependency>

非Springboot项目,比如像是Maven项目的话,那就这样引入:

XML
<!--对于基本的 MyBatis 功能,你需要添加 MyBatis 的核心依赖:-->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>最新版本号</version>
</dependency>
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <version>最新版本号</version>
</dependency>

数据准备:

SQL
-- 创建数据库
DROP DATABASE IF EXISTS mybatis_test;
CREATE DATABASE mybatis_test DEFAULT CHARACTER SET utf8mb4;
-- 使用数据数据
USE mybatis_test;
-- 创建表[用户表]
DROP TABLE IF EXISTS user_info;
CREATE TABLE `user_info` (
    `id` INT(11) NOT NULL AUTO_INCREMENT,
    `username` VARCHAR(127) NOT NULL,
    `password` VARCHAR(127) NOT NULL,
    `age` TINYINT(4) NOT NULL,
    `gender` TINYINT(4) DEFAULT '0' COMMENT '1-男 2-女 0-默认',
    `phone` VARCHAR(15) DEFAULT NULL,
    `delete_flag` TINYINT(4) DEFAULT 0 COMMENT '0-正常, 1-删除',
    `create_time` DATETIME DEFAULT now(),
    `update_time` DATETIME DEFAULT now() ON UPDATE now(),
    PRIMARY KEY (`id`)
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4;
--create_time字段:记录数据的创建时间(当前时间),仅在插入时生效。
--update_time字段:记录数据的最后更新时间,插入时默认值,更新时自动刷新(当前时间)
-- 添加用户信息
INSERT INTO mybatis_test.user_info(username, `password`, age, gender, phone)
VALUES ('admin', 'admin', 18, 1, '18612340001');

INSERT INTO mybatis_test.user_info(username, `password`, age, gender, phone)
VALUES ('zhangsan', 'zhangsan', 18, 1, '18612340002');

INSERT INTO mybatis_test.user_info(username, `password`, age, gender, phone)
VALUES ('lisi', 'lisi', 18, 1, '18612340003');

INSERT INTO mybatis_test.user_info(username, `password`, age, gender, phone)
VALUES ('wangwu', 'wangwu', 18, 1, '18612340004');

这里,小编使用的是navicat客户端(文章最后会介绍)

sql语句执行效果如下:

配置application.yml文件

YAML
spring:
  application:
    name: MyBatis
  #数据库配置
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncoding=utf8&useSSL=false
    username: 自行填写
    password: 自行填写(纯数字要加单引号)
    driver-class-name: com.mysql.cj.jdbc.Driver
#为什么要配置打印日志,这里小编觉得原版打印出来不太好看,所以加上了这个打印日志配置    
mybatis:
  configuration: # 配置打印 MyBatis日志
      log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
     

准备工作到这里结束了,接下来进行代码编写

持久层代码编写

首先在创建好的项目中,创建一个mapper包和一个model包。

model包是存储实体类信息的,实体类是什么?

通常指的是软件开发中与数据表结构相对应的Java类。

既然我们表创建好了,那么代码中也要有对应的字段去映射

UserInfo(其他名字也可以):

TypeScript
@Data
public class UserInfo {
    private Integer id;
    private String username;
    private String password;
    private Integer age;
    private Integer gender;
    private String phone;
    private Integer deleteFlag;
    private Date createTime;
    private Date updateTime;
}

在Mapper包,创建一个UserInfoMapper接口(其他名字也可以)。

Java
@Mapper
public interface UserInfoMapper {

}

为什么要使用Mapper注解呢?

这个Mapper注解是一个标记注解,它的作用是:

  • 告诉 Spring 和 MyBatis:这个接口是一个 MyBatis Mapper。
  • Spring Boot 在启动时会扫描这些接口,并为它们生成动态代理对象(由 MyBatis 自动生成实现类)。
  • 有了这个注解,你就可以直接通过 @Autowired 注入这个接口并使用它进行数据库操作

动态代理简单解释下:

动态代理是Java中一种非常强大的机制,它允许你在运行时创建一个实现了一组接口的类的实例,而无需在编写的代码中显式地定义这些类。这个特性特别适用于像MyBatis这样的框架,它们需要在运行时根据配置或注解自动生成代码来处理数据库操作。

为什么要是一个接口呢?

这是因为Mybatis使用的是接口+动态代理的机制

这也是属于Mybatis的设计哲学之一:

  • 开发者只定义接口方法(比如:List<UserInfo> getAllUsers();
  • MyBatis 会在运行时自动生成该接口的实现类(动态代理)
  • 实现类中封装了底层 JDBC 操作、SQL 执行、结果映射等逻辑

如若不想在之后的接口上,都写一Mapper注解,那么可以使用MapperScan注解

TypeScript
@SpringBootApplication
@MapperScan("com.example.demo.mapper") // 自动扫描包下所有Mapper接口
public class MyBatiesApplication{
    public static void main(String[] args) {
        SpringApplication.run(MyBatiesApplication.class, args);
    }
}

接下来写我们的增删改查代码了。

查询:

通过Mybatis提供的Select注解

方式一:

Java
//不推荐
@Select("select * from user_info")
List<UserInfo> selectUserInfo();
//返回的表中多行信息,所以用List接收

不推荐原因原因:*号还要被自动解析为各个表字段,消耗性能

那么如何进行测试呢?

这里提供一个测试快捷方式

在UsetInfoMapper代码中右键

这里勾选下面selectUserInfo(),点击ok就自动生成测试类了

如若想手写测试,也是可以的,在该目录下创建一个测试类即可

那么一定义要加上@SpringBootTest,这样才会注入我们想要的UserInfoMapper对象

Java
@SpringBootTest
class UserInfoMapperTest {
//要用到该方法,那么首先要注入对象,这是手写的。
    @Autowired
    private UserInfoMapper userInfoMapper;
    @Test
    void selectUserInfo() {
        System.out.println(userInfoMapper.selectUserInfo());
    }
}

此时在测试类中,点击该方法的左边的绿色三角形按钮即可允许

结果

方式二:

Java
  @Select("select id,username,password,age," +
            "gender,phone,delete_flag,create_time,update_time from user_info")
 List<UserInfo> selectUserInfo();           

生成测试:

Java
    @Test
    void selectUserInfo2() {
        userInfoMapper.selectUserInfo2().forEach(x-> System.out.println(x));
    }

结果:

此时,我们发现,为什么create_time字段没有值呢?

这是因为当用一个集合作为范围类型的的时候,此时,返回的结果会与UserInfo的属性进行一一映射

当发现字段不一样的的时候,就映射失败,默认为null了,基础数据类型就会默认为0。

为什么字段不能都合为一致呢?

这是因为java有java的开发规范,数据库有数据库的建表规范,这些规范形成一个行业共识,一般都不会去改的。

当然还是有办法的

方法三:

Java
@Select("select id,username,password,age," +
        "gender,phone,delete_flag as deleteFlag,create_time as createTime," +
        "update_time as updateTime from user_info")
List<UserInfo> selectUserInfo();

测试类一样的,就不再生成

结果:

方法四:

Python
//注解实现
@Select("select id,username,password,age," +
        "gender,phone,delete_flag,create_time,update_time from user_info")
@Results( {
        @Result(column = "delete_flag",property = "deleteFlag"),
        @Result(column = "create_time",property = "createTime"),
        @Result(column = "update_time",property = "updateTime")
})
List<UserInfo> selectUserInfo2();

@Results 注解用于定义结果映射(Result Mapping),它允许你更精细地控制如何将查询结果中的列映射到Java对象的属性。

生成测试:

Java
    @Test
    void selectUserInfo2() {
        userInfoMapper.selectUserInfo2().forEach(x-> System.out.println(x));
    }

结果是和方法三中是一致的,不再展示。

而该注解还可以进行复用

将Restults注解修改下:

Java
@Results(id="baseMap",value = {
        @Result(column = "delete_flag",property = "deleteFlag"),
        @Result(column = "create_time",property = "createTime"),
        @Result(column = "update_time",property = "updateTime")
})

Python
//复用注解
@Select("select id,username,password,age," +
        "gender,phone,delete_flag,create_time,update_time from user_info")
@ResultMap("baseMap")
List<UserInfo> selectUserInfo3();

运行该生成的测试代码后,结果与方法三一致

方法五:

通过配置文件修改

YAML
mybatis:
  # 配置 mybatis xml 的文件路径,在 resources/mapper 创建所有表的 xml 文件
  mapper-locations: classpath:mapper/**Mapper.xml
  configuration: # 配置打印 MyBatis日志
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    map-underscore-to-camel-case: true #配置驼峰自动转换

此时呢,不用Restults注解也可以进行字段映射了

查询(带有条件):

上面刚刚使用的是不带条件查询,接下来介绍下带有条件查询的。

既然是带有条件,那么用到where或者order by等等,需要个参数。

如何写呢?

方法一:

Java
  //以下为非唯一主键查询,返回结果建议使用list
    //该查询方式推荐使用
    @Select("select * from user_info where gender=#{age} and age=#{gender}")
  List<UserInfo> selectUserByGenderAndAge( Integer age,
                                            Integer gender);  

此时,where中的参数和方法中参数,要一一对应,特别是顺序上,使用#占位符,预编译时,对应参数用?代替

#{}占位符:

  • 预编译 SQL 语句:使用 #{} 时,MyBatis 会将传入的参数视为一个预编译 SQL 语句的参数。这意味着参数会被当作 JDBC 预编译语句中的参数来处理,可以有效地防止 SQL 注入攻击。
  • 类型安全:MyBatis 会自动根据 Java 类型对参数进行适当的转换。例如,如果你传递的是一个整数类型的参数,MyBatis 会确保它以正确的格式插入到 SQL 语句中。

测试前,修改下表中数据

生成测试:

Java

@Test
void selectUserByGenderAndAge() {
    System.out.println(userInfoMapper.selectUserByGenderAndAge(20, 0));
}

结果:

值得一提的是,这样写,参数对应不上,会报出参数异常错误

Java
    //参数异常
    @Select("select * from user_info where gender=#{age1} and age=#{gender}")

方法二:

Java
    //参数排序,不推荐
      @Select("select * from user_info where gender=#{param1} and age=#{param2}")
      List<UserInfo> selectUserByGenderAndAge( Integer gender,
                                               Integer age);

方法中参数,gender和age,会被mybatis默认为param1为gender,age为param2.

所以这样写的话,可读性不是那么高

测试结果,与刚刚结果一致,不再展示

方法三:

Java
//参数重命名,param中的参数要和@select中参数一致
    @Select("select * from user_info where gender=#{gender} and age=#{age}")
List<UserInfo> selectUserByGenderAndAge(@Param("gender") Integer gender,
                                        @Param("age")Integer age);

测试结果,与刚刚结果一致,不再展示

增加:

通过Mybatis提供的Insert注解

这里可以通过方法,方法中,要求传入一个个参数,进行插入。

但这里,小编使用的是,通过参数中传递对象进行插入

当参数中是对象的时候,此时呢,mybatis会将对象中设置好的值,与插入语句中参数一一映射

Java
 //插入操作:(传递对象),返回影响行数
@Insert("insert into user_info (username,`password`,age,gender) " +
        "values (#{username},#{password},#{age},#{gender})")
    Integer insertUserInfo(UserInfo userInfo);

values中,不需要userInfo.username相关操作,因为,这里没有涉及到重命名参数。

生成测试:

Java
@Test
void insertUserInfo() {
    UserInfo userInfo=new UserInfo();
    userInfo.setUsername("李四");
    userInfo.setPassword("123");
    userInfo.setAge(18);
    userInfo.setGender(0);
    System.out.println(userInfoMapper.insertUserInfo(userInfo));
}

结果展示:

有时候,我们插入一个数据的时候,需要返回它的ID值,作为参数去传递到其他接口,那么此时该如何做呢?

Java
//同样传递对象,此时进行重命名,同时返回自增主键的值
@Options(useGeneratedKeys = true,keyProperty ="id")
@Insert("insert into user_info (username,`password`,age,gender)" +
        " values (#{userInfo.username},#{userInfo.password},#{userInfo.age},#{userInfo.gender})")
Integer insertUserInfo2(@Param("userInfo") UserInfo userInfo);

此时,使用了重命名后,values中的参数就要使用userInfo.username了。

生成测试:

Java
@Test
void insertUserInfo2() {
    UserInfo userInfo=new UserInfo();
    userInfo.setUsername("王五");
    userInfo.setPassword("456");
    userInfo.setAge(20);
    userInfo.setGender(0);
    System.out.println(userInfoMapper.insertUserInfo2(userInfo)+"返回ID:"+
            userInfo.getId());
}

结果展示:

修改:

通过Mybatis提供的Update注解

Java
//修改数据
@Update("update user_info set password=#{newPassword} where id=#{id}")
Integer updateUserInfo(String newPassword,Integer id);

当然,方法传入的参数也可以是对象。

生成测试:

Java

@Test
void updateUserInfo() {
    System.out.println(userInfoMapper.updateUserInfo("12345",4));
}

结果展示:

删除:

通过Mybatis提供的Delete注解

Java
//删除数据
@Delete("delete from user_info where id=#{id}")
Integer deleteUserInfo(Integer id);

生成测试

Java
@Test
void deleteUserInfo() {
    System.out.println(userInfoMapper.deleteUserInfo(5));
}

结果展示:

排序:

Java
    @Select("select id,username,password,age,gender,phone,delete_flag,create_time,update_time" +
            "from user_info order by id #{sort}")//(报错)
     List<UserInfo> selectUserInfoByOrder(String sort);

生成测试:

Java
@Test
void selectUserInfoByOrder() {
    System.out.println(userInfoMapper.selectUserInfoByOrder("desc"));
}

结果展示:

TypeScript
重要的一条:
Caused by: java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''desc'' at line 1

此时发现报错信息后,我们查看信息是从下往上看,找到你看得懂的地方。

可以发现,这是语法错误

这是因为使用#{}占位符的时候,当参数为String类型,此时,就会自动单引号,导致最终的结果会多一个单引号。

修改方法,使用${}占位符

Java
@Select("select id,username,password,age,gender,phone,delete_flag,create_time,update_time " +
        "from user_info order by age ${sort}")
List<UserInfo> selectUserInfoByOrder(String sort);

测试代码一样的,这里就展示为结果

那么,也可以发现,使用$占位符的时候,就会直接将结果进行填入,不会使用?符号代替

like查询

Java
   //由于方法参数为String类型,会自动为填入结果增加单引号,所以这里就用$占位符
    @Select("select id,username,password,age,gender,phone,delete_flag,create_time,update_time " +
            "from user_info where username like '%${key}%'")
     List<UserInfo> selectUserInfoByLike(String key);       

测试之前,改下数据库信息

测试用例:

Java
@Test
void selectUserInfoByLike() {
    System.out.println(userInfoMapper.selectUserInfoByLike("java"));
}

结果展示:

当然,除了这个方法,还可以使用更安全的:

使用数据库自带的contact拼接方法

Java
@Select("select id,username,password,age,gender,phone,delete_flag,create_time,update_time " +
        "from user_info where username like concat ('%',#{key},'%')")
List<UserInfo> selectUserInfoByLike(String key);

测试用例和结果一样,就不做展示。

由刚刚的例子得知,使用#号占位符和$占位符也是可以进行参数替换的,那么它们有什么区别呢?

#{}占位符和${}区别

特性

#{}

${}

处理方式

预编译参数

直接字符串替换

安全性

安全,防止 SQL 注入

不安全,可能导致 SQL 注入

适用场景

绝大多数情况下的参数传递

动态 SQL 片段,如表名、列名等

类型转换

支持类型转换

不支持类型转换

刚刚提到了SQL注入,那么SQL注入是什么呢?

SQL注入:


SQL注入(SQL Injection)是一种代码注入技术,攻击者通过将恶意的SQL代码插入到查询字符串中,进而操控数据库执行非授权的操作。

举例:

Java
@Select("select * from user_info where username= '${username}' and password= '${password}'")
List<UserInfo> selectUserInfoByNameAndPassword(String username,String password);

此时,当我们的${password}被用户输入用户和密码分别为为admin和 ' or 1='1后

此时呢,我们的SQL语句,就变成

Java
select * from user_info where username='admin'and password= '' or 1='1';

那么此时,后面or语句总是为真,所以就会把所有用户信息参数出来,这是很危险的

  1. 防止SQL注入简单建议:
    核心建议:始终使用预编译语句和参数化查询
  1. 不要信任任何外部输入:所有来自用户的输入都应该被视为潜在的安全威胁,确保对这些输入进行验证和清理。
  1. 最小权限原则:为应用程序使用的数据库账户分配尽可能少的权限。例如,如果应用不需要删除表的功能,那么就不要给这个账户赋予删除权限。
  1. 保持软件更新:定期更新你的数据库管理系统、操作系统以及所使用的框架或库,以利用最新的安全补丁。

注解的方式就介绍到这里,接下来就介绍下XML的方式

XML

准备工作:

配置文件修改:

YAML
mybatis:
  # 配置 mybatis xml 的文件路径,在 resources/mapper 创建所有表的 xml 文件
  mapper-locations: classpath:mapper/**Mapper.xml
  configuration: # 配置打印 MyBatis日志
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    map-underscore-to-camel-case: true #配置驼峰自动转换

文件创建:

在resources目录下创建个mapper目录,以及一个UserInfoMapper.xml(名字可以随心取),最后在启动类所在包的mapper包下,创建UserInfoXMLMapper接口(名字可以随心取)

在resources包下的xml文件中,填入以下基本内容

XML
<!--这是 XML 文件的标准声明,表示该文件遵循 XML 1.0 规范,并且使用 UTF-8 编码格式。UTF-8 是一种字符编码方式,支持几乎所有的字符集,适合用于国际化的应用程序。 -->
<?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="com.nanxi.mybatis.mapper.UserInfoXmlMapper">

</mapper>

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">:

简单解释下:

  • <!DOCTYPE mapper ... >:这是一个文档类型声明(Document Type Definition, DTD),它指定了当前 XML 文档遵循的规则。在这个例子中,mapper 元素必须符合 MyBatis 提供的 Mapper 3.0 规范。
  • "-//mybatis.org//DTD Mapper 3.0//EN":这是 DTD 的公共标识符,用来唯一标识该 DTD。
  • "http://mybatis.org/dtd/mybatis-3-mapper.dtd":这是 DTD 的系统标识符,通常是一个 URL,指向实际的 DTD 文件位置。虽然现代开发环境中可能不需要直接访问这个 URL,但它有助于验证 XML 文件是否符合 MyBatis 的规范

namespace这个是说明,该XML下的SQL语句与哪个接口下的接口方法对应起来

插件下载(推荐):

该插件有助于我们定义好接口方法后,快速生成对应的SQL语句标签。

XML代码编写

XML中编写的SQL语句,是基于一对对SQL语句标签的,比如<select> </select>。

查询:

方法一:

XML
<select id="selectUserInfo" resultType="com.nanxi.mybatis.model.UserInfo">
    select id,username,password,age,gender,phone,
           delete_flag as deleteFlage,create_time as createTime,update_time as updateTime
    from user_info;
</select>

resultType="com.nanxi.mybatis.model.UserInfo":

这个代表的是,返回类型是哪种的。

接口类中:

Java
@Mapper
public interface UserInfoXmlMapper {
List<UserInfo> selectUserInfo();
}

生成测试:

TypeScript
@SpringBootTest
class UserInfoXmlMapperTest {
    @Autowired
    private UserInfoXmlMapper mapper;
    @Test
    void selectUserInfo() {
        mapper.selectUserInfo().forEach(x-> System.out.println(x));
    }

结果展示:

不想写AS去规范变量名的话,可以使用到resultMap标签

方法二:

XML
   <resultMap id="BaseMap" type="com.nanxi.mybatis.model.UserInfo">
<!--        表中id涉及到主键,强烈建议写上id标签-->
        <id property="id" column="id"></id>
        <result column="delte_flage" property="deleteFlag"></result>
        <result column="update_time" property="updateTime"></result>
        <result column="create_time" property="createTime"></result>
    </resultMap>
    <select id="selectUserInfo" resultMap="BaseMap">
             select id,username,password,age,gender,phone,delete_flag,create_time,update_time
                   from user_info;
  </select>                 

测试、接口以及结果都一样,这里就不做展示

以上这个是一种复用写法,如若想对单个查询标签起效

可以把resultMap中的id和type去掉,以及select标签下的resultMap去掉即可。

除了这个方法,还有一个就是修改配置文件,这个与注解那里一模一样,就不做讲解。

增加:

方法一:

XML
<!--    插入操作-->
    <insert id="insertUserInfo">
        insert into user_info(username,password,age,gender)
        values(#{username},#{password},#{age},#{gender})
    </insert>

接口方法:

Java
Integer insertUserInfo(UserInfo userInfo);

生成测试:

Java
void insertUserInfo() {
    UserInfo userInfo=new UserInfo();
    userInfo.setUsername("java");
    userInfo.setPassword("java112");
    userInfo.setAge(18);
    userInfo.setGender(0);
    System.out.println(mapper.insertUserInfo(userInfo));
}

结果展示:

方法二:

XML
<!--    插入操作(重命名方式)-->
    <insert id="insertUserInfo2">
        insert into user_info(username,password,age,gender)
        values(#{userInfo.username},#{userInfo.password},#{userInfo.age},#{userInfo.gender})
    </insert>

接口方法:

Java
Integer insertUserInfo2(@Param("userInfo") UserInfo userInfo);

生成测试内容与结果,与方法一类似,所以就不做展示。

更新:

XML
<!--    更新操作-->
    <update id="updateUserInfo">
        update user_info set `password`=#{password} where id=#{id};
    </update>

接口方法:

Java
//(也可以通过对象)进行传入
Integer updateUserInfo(@Param("password") String password,
                       @Param("id") Integer id);

生成测试:

Java
@Test
void updateUserInfo() {
    System.out.println(mapper.updateUserInfo("123wangwu", 4));
}

结果展示:

删除:

XML
<!--    删除操作-->
    <delete id="deleteUserInfo">
        delete from user_info where id=#{deleteId};
    </delete>

接口方法

Java
Integer deleteUserInfo(@Param("deleteId") Integer id);

生成测试:

Java

@Test
void deleteUserInfo() {
    System.out.println(mapper.deleteUserInfo(5));
}

结果展示

对于XML中的一些基础增删查改就介绍到这。

接下来介绍下动态SQL语句。

动态SQL语句

举个例子

比如你进行用户信息编辑的时候,页面让你填入电话号码,但这个电话号码不一定是必选项,此时呢,你填入了电话号码,编辑后信息就有电话号码,如若你没有填入,编辑后信息就没有电话号码。

如何去实现呢?

接下来用到了if标签了

if标签:

XML
if标签使用
<!--    插入选项是可选的-->
    <insert id="insertUserInfoByCondition">
        insert into user_info (username,
        `password`,
        age,
        <if test="phone!=null">
            phone,
        </if>
        gender
        )
        values (
                #{username},
                #{password},
                #{age},
                <if test="phone!=null">
                    #{phone},
                </if>
                #{phone}
        )
    </insert>

接口方法:

XML
Integer insertUserInfoByCondition(UserInfo userInfo);

生成测试:

TypeScript
@Test
 void insertUserInfoByCondition() {
        UserInfo userInfo=new UserInfo();
        userInfo.setUsername("c++112");
        userInfo.setPassword("c++1234");
        userInfo.setAge(20);
        userInfo.setGender(0);
//        userInfo.setPhone("134879924");
        System.out.println(mapper.insertUserInfoByCondition(userInfo));
    }

结果展示:

把setPhone的注释去掉后:

结果:

那么这个if标签要谨慎使用

比如

Bash
<insert id="insertUserInfoByCondition">
    insert into user_info (username,
    `password`,
    age,
    <if test="phone!=null">
        phone,
    </if>
    <if test="gender!=null">
        gender
    </if>

    )
    values (
            #{username},
            #{password},
            #{age},
            <if test="phone!=null">
                #{phone},
            </if>
            <if test="gender!=null">
                #{gender}
            </if>

    )
</insert>

当phone和gender都没有填入,此时呢,sql语句这里,到age就结束,但是多了逗号没有去处理,那么就会出现

语法错误

那么接下来用到了trim标签了

trim标签:

它有这几个属性值:

  • suffixOverrides:去除后缀
  • suffix:添加 后缀
  • prefix:添加前缀
  • prefixOverrides:去除前缀

这些属性根据情况进行选择即可

这里小编当if语句不生效时,前一个的字段后逗号会去掉,使用到了去除后缀属性

代码:

XML
<!--    分割标签-->
    <insert id="insertUserInfoByTrim">
        insert into user_info (
         <trim suffixOverrides=",">
             <if test="username!=null">
                 username,
             </if>
             <if test="password!=null">
                 `password`,
             </if>
             <if test="age!=null">
                 age,
             </if>
             <if test="gender!=null">
                 gender,
             </if>
             <if test="phone!=null">
                 phone
             </if>
         </trim>
        )
        values (
        <trim suffixOverrides=",">
            <if test="username!=null">
                #{username},
            </if>
            <if test="password!=null">
                #{password},
            </if>
            <if test="age!=null">
                #{age},
            </if>
            <if test="gender!=null">
                #{gender},
            </if>
            <if test="phone!=null">
                #{phone}
            </if>
        </trim>
        )
    </insert>

接口方法:

Java
Integer insertUserInfoByTrim(UserInfo userInfo);

生成测试:

TypeScript
   @Test
    void insertUserInfoByTrim() {
        UserInfo userInfo=new UserInfo();
        userInfo.setUsername("JS112");
        userInfo.setPassword("Js12345");
        userInfo.setAge(20);
        userInfo.setGender(0);
//        userInfo.setPhone("13712661733");
        System.out.println(mapper.insertUserInfoByTrim(userInfo));
    }

结果:

where标签:

它存在以下特性:

  1. 自动添加 WHERE 关键字:如果 <where> 标签内的内容非空,则会自动添加 WHERE 关键字。这意味着你不需要手动在 SQL 中写 WHERE,这可以减少一些潜在的错误。
  1. 去除不必要的 AND 或 OR:当多个条件组合时,可能会出现第一个条件前有 AND 或 OR 的情况。<where> 标签会自动移除这些多余的关键词,确保生成的 SQL 语句正确无误。
  1. 支持动态条件:可以在 <where> 标签内部使用其他动态 SQL 元素(如 <if><choose> 等),根据不同的条件构建灵活的查询条件。

代码:

XML
<!--    where标签-->
        <select id="selectUserInfoByWhere" resultType="com.nanxi.mybatis.model.UserInfo">
            select id,username,password,age,gender,phone,delete_flag,create_time,update_time
                   from user_info
            <where>
                <if test="age!=null">
                    age=#{age}
                </if>
                <if test="gender!=null">
                    and gender=#{gender}
                </if>
            <if test="password!=null">
                and password=#{password}
            </if>
            </where>
        </select>

接口方法:

Java
List<UserInfo> selectUserInfoByWhere(UserInfo userInfo);

生成测试:

Java
@Test
void selectUserInfoByWhere() {
    UserInfo userInfo=new UserInfo();
    userInfo.setAge(18);
    userInfo.setGender(0);
    System.out.println(mapper.selectUserInfoByWhere(userInfo));
}

结果:

set标签:

它存在以下特性:

  1. 自动添加 SET 关键字:如果 <set> 标签内的内容非空,则会自动添加 SET 关键字。
  1. 去除多余的逗号:当多个条件组合时,可能会出现最后一个字段前有多余逗号的情况。<set> 标签会自动移除这些多余的逗号,确保生成的 SQL 语句正确无误。
  1. 支持动态更新字段:可以在 <set> 标签内部使用其他动态 SQL 元素(如 <if><choose> 等),根据不同的条件构建灵活的更新操作。

代码:

XML
<!--    set标签-->
    <update id="updateUserInfoBySet">
        update user_info set

        <if test="password!=null">
            `password` = #{password},
        </if>
        <if test="age!=null">
            age=#{age},
        </if>
        <if test="phone!=null">
            phone=#{phone}
        </if>
         where id=#{id};
    </update>

接口方法:

Java
Integer updateUserInfoBySet(UserInfo userInfo);

测试代码:

Java
@Test
void updateUserInfoBySet() {
    UserInfo userInfo=new UserInfo();
    userInfo.setId(11);
    userInfo.setPhone("1242533");
    System.out.println(mapper.updateUserInfoBySet(userInfo));
}

结果:

当我们想进行批量删除的时候,可以使用到foreach标签:

foreach标签

XML
<!--    foreach标签-->
    <delete id="deleteUserInfoByDelete">
        delete from user_info where id in
        <foreach collection="ids" item="id" separator="," open="(" close=")">
            #{id}
        </foreach>
    </delete>

collection参数是要和传入的集合参数名一致

item参数是代表中,从ids集合取出来的元素用id去接收

separator是代表着分隔符,是逗号

open和close代表着,从那里开始从哪里结束

接口方法:

Java
Integer deleteUserInfoByDelete(List<Integer> ids);

生成测试:

Java
@Test
void deleteUserInfoByDelete() {
    List<Integer> list = List.of(10, 11, 12);
    System.out.println(mapper.deleteUserInfoByDelete(list));
}

结果:

include标签

当我们发现,对于XML编写中,一些语句是重复出现了,所以整体看起来比较繁重。

使用include标签,可以把它合在一起,然后增删查改标签页也可以使用了

代码:

XML
<!--        include标签-->
    <sql id="allColumn">
        id,username,password,age,gender,phone,delete_flag,create_time,update_time
    </sql>
     <select id="selectUserInfo" resultMap="BaseMap">
            select
                <include refid="allColumn"></include>
                   from user_info;
        </select>

结果与之前展示过,这里就不再展示。

注解使用动态SQL语句

对于注解来说,就不可以使用动态SQL语句吗?

当然不是,只是会显示稍微麻烦,可读性没那么好罢了。

这里举个例子:

Java
@Delete({
    "<script>",
    "DELETE FROM users WHERE id IN",
    "<foreach item='id' collection='list' open='(' separator=',' close=')'>",
    "#{id}",
    "</foreach>",
    "</script>"
})
Integer deleteUsersByIds(@Param("list") List<Integer> ids);

测试和结果就不做展示,与foreach标签那里,大差不差。

所以注解使用动态SQL语句,只需把<script> </script>标签即可

所以总的来说,这个mybatis可是方便了广大程序员,毕竟少些了很多共性代码。

既然这样的话,mybatis何不再努力下,把一些增删改查的基本代码一并帮写了?

接下来,介绍这个一个mybatis的增强工具

Mybatis Generator

是一个用于生成 MyBatis 相关代码的工具,旨在减少开发人员的手动编码工作量。它能够自动生成与数据库表对应的实体类(JavaBeans)、Mapper接口以及相应的XML映射文件.

快速使用:

1.引入插件:

XML
<plugin>
    <groupId>org.mybatis.generator</groupId>
    <artifactId>mybatis-generator-maven-plugin</artifactId>
    <version>1.4.1</version>
    <executions>
       <execution>
          <id>Generate MyBatis Artifacts</id>
          <phase>deploy</phase>
          <goals>
             <goal>generate</goal>
          </goals>
       </execution>
    </executions>
    <configuration>
       <!--generator配置文件所在位置-->
       <configurationFile>src/main/resources/generator/generator.xml</configurationFile>
       <!-- 允许覆盖生成的文件, mapxml不会覆盖, 采用追加的方式-->
       <overwrite>true</overwrite>
       <verbose>true</verbose>
       <!--将当前pom的依赖项添加到生成器的类路径中-->
       <includeCompileDependencies>true</includeCompileDependencies>
    </configuration>
    <dependencies>
       <dependency>
          <groupId>com.mysql</groupId>
          <artifactId>mysql-connector-j</artifactId>
          <version>8.0.33</version>
       </dependency>
    </dependencies>
</plugin>

2.解释相关信息:

XML
 <groupId>org.mybatis.generator</groupId>
    <artifactId>mybatis-generator-maven-plugin</artifactId>
    <version>1.4.1</version>
  

这部分表示引入了 MyBatis Generator 的 Maven 插件。

XML
 <executions>
       <execution>
          <id>Generate MyBatis Artifacts</id>
          <phase>deploy</phase>
          <goals>
             <goal>generate</goal>
          </goals>
       </execution>
    </executions>

  • <executions>:定义插件的执行方式和时机。
  • <execution>:一个具体的执行配置项。
  • <id>:执行 ID,用于唯一标识这个 execution,便于调试和日志查看。
  • <phase>:绑定到 Maven 生命周期中的哪个阶段,默认是 none,这里设置为 deploy 阶段执行 MBG。
  • <goals>:要执行的目标(即插件的功能),这里调用的是 generate,表示运行 MyBatis Generator 生成代码。

添加配置文件:

值得注意的是,配置文件要和pom.xml文件中,指定的一样

配置文件如下:

XML
<?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 -->
    <context id="MysqlTables" targetRuntime="MyBatis3" defaultModelType="flat">
        <!--去除注释-->
        <commentGenerator>
            <property name="suppressDate" value="true"/>
            <property name="suppressAllComments" value="true" />
        </commentGenerator>

        <!--数据库链接信息-->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                        connectionURL="jdbc:mysql://127.0.0.1:3306/mybatis_test?serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true"
                        userId="root"
                        password='12345'>
        </jdbcConnection>

        <!-- 生成实体类 -->
        <javaModelGenerator targetPackage="com.nanxi.mybatis.generator" targetProject="src/main/java" >
            <property name="enableSubPackages" value="false"/>
            <property name="trimStrings" value="true"/>
        </javaModelGenerator>
        <!-- 生成mapxml文件 -->
        <sqlMapGenerator targetPackage="generator" targetProject="src/main/resources" >
            <property name="enableSubPackages" value="false" />
        </sqlMapGenerator>
        <!-- 生成mapxml对应client,也就是接口dao -->
        <javaClientGenerator targetPackage="com.nanxi.mybatis.generator" targetProject="src/main/java" type="XMLMAPPER" >
            <property name="enableSubPackages" value="false" />
        </javaClientGenerator>
        <!-- table可以有多个,每个数据库中的表都可以写一个table,tableName表示要匹配的数据库表,也可以在tableName属性中通过使用%通配符来匹配所有数据库表,只有匹配的表才会自动生成文件 -->
        <table tableName="user_info">
            <property name="useActualColumnNames" value="false" />
            <!-- 数据库表主键 -->
            <generatedKey column="id" sqlStatement="Mysql" identity="true" />
        </table>
    </context>
</generatorConfiguration>

3.对配置文件相关信息解释:

Java
<context id="MysqlTables" targetRuntime="MyBatis3" defaultModelType="flat">

  • targetRuntime
  • 指定目标运行时框架版本。
  • 常用值:
  • MyBatis3:适用于最新版 MyBatis。
  • MyBatis3Simple:不生成 Example 查询方法。
  • defaultModelType
  • 控制模型类(实体类)的生成方式。
  • 常见值:
  • flat:为每张表只生成一个实体类(推荐使用)。
  • hierarchical:按层次结构生成多个类(如主键类、查询条件类等

XML
<commentGenerator>
    <property name="suppressDate" value="true"/>
    <property name="suppressAllComments" value="true" />
</commentGenerator>

  • 常见 property:
  • suppressDate
  • 设置为 true 表示在注释中不显示生成时间。
  • 默认会加上生成时间,例如:* @generated Thu May 27 09:45:00 CST 2025
  • suppressAllComments
  • 设置为 true 表示完全禁用所有注释(包括警告信息)。
  • 如果你不希望看到“不要修改此文件”的警告注释,可以开启这个。

XML
<javaModelGenerator targetPackage="com.nanxi.mybatis.generator" targetProject="src/main/java" >
    <property name="enableSubPackages" value="false"/>
    <property name="trimStrings" value="true"/>
</javaModelGenerator>

属性解释:

  • targetPackage:生成的 Java 类所在的包名。
  • targetProject:生成的 Java 类所在项目的路径(相对于项目根目录)。
  • enableSubPackages
  • 是否根据表名自动创建子包(如 user -> com.example.model.user)。
  • 设置为 false 表示不自动创建子包。
  • trimStrings
  • 设置为 true 表示对字符串类型的字段进行 trim 处理(防止空格问题)

XML
<sqlMapGenerator targetPackage="generator" targetProject="src/main/resources" >
    <property name="enableSubPackages" value="false" />
</sqlMapGenerator>

  • 属性解释:
  • targetPackage:XML 文件的包路径(通常是资源目录下的子路径)。
  • targetProject:XML 文件输出位置。
  • enableSubPackages
  • 是否根据表名自动生成子包。
  • 设置为 false 表示统一放在指定目录下。

XML
<javaClientGenerator targetPackage="com.nanxi.mybatis.generator" targetProject="src/main/java" type="XMLMAPPER" >
    <property name="enableSubPackages" value="false" />
</javaClientGenerator>

  • 属性解释:
  • targetPackage:接口类所在的包名。
  • targetProject:接口类输出路径。
  • type
  • 接口类型,常用值:
  • ANNOTATEDMAPPER:基于注解的 Mapper(不需要 XML)。
  • MIXEDMAPPER:混合模式(有注解也有 XML)。
  • XMLMAPPER:纯 XML 模式(最常用)。
  • enableSubPackages
  • 是否根据表名生成子包。

XML
<table tableName="user_info">
    <property name="useActualColumnNames" value="false" />
    <generatedKey column="id" sqlStatement="Mysql" identity="true" />
</table>

  • 属性解释:
  • tableName:要生成代码的数据库表名,支持通配符 %,比如 "%" 表示所有表。
  • useActualColumnNames
  • 设置为 false 表示列名转换为驼峰命名法(如 user_name → userName)。
  • 设置为 true 表示保留实际列名(不建议)。
  • <generatedKey>
  • 用于处理主键自增字段。
  • 属性解释:
  • column="id":主键字段名。
  • sqlStatement="Mysql":数据库类型(MySQL)。
  • identity="true":表示是自增主键,插入数据后会回填 ID。

如若遇到generator.xml中,dtd信息报错:

可以对它进行临时忽略:

点击idea->file->setting->Languages & FrameWorks->Schemas and DTDS

点击下面的Ignored上的+号

出现的页面中,加入xml中,对应出错的URL即可

4.运行使用:

如若想生成动态SQL,按照以下步骤:

pom文件引入该依赖:

XML
<dependency>
    <groupId>org.mybatis.dynamic-sql</groupId>
    <artifactId>mybatis-dynamic-sql</artifactId>
    <version>1.3.1</version> <!-- 或者更高版本,比如1.4.0+ -->
</dependency>

generator.xml修改:

XML
<context id="MysqlTables" targetRuntime="MyBatis3DynamicSql" defaultModelType="flat">

注意,版本要相互对应,提供官方的链接,可以自行去比对

Introduction to MyBatis Generator

最后点击运行即可。

这个增强工具,更多是在项目初期的时候使用,一键生成代码,减少工作量。

介绍Navicat

Navicat 是一款功能强大且广受欢迎的数据库管理和开发工具,支持多种数据库系统。它由 PremiumSoft CyberTech Ltd. 开发,并面向数据库管理员、软件开发者等专业人士设计,旨在简化数据库管理流程,提高工作效率。

下载软件:

Navicat | 产品

下载这个免费轻量版

下载安装。

安装成功后,进入该页面:

点击连接:

点击MySQL,小编本地是MySQL,所以,这个具体数据,看个人电脑是什么,或者说远程的SQL是什么

进入该页面

输入对应内容连接即可

连接成功后:

右键demo可以创建数据库,点击数据库下的表,右键它可以创建表。

如若想输入SQL语句来完成,那么点击对应最上面的新建查询,打开后,输入对应的SQL语句。

到这里,mybatis一些基础使用,小编分享到这。


网站公告

今日签到

点亮在社区的每一天
去签到