JavaEE 进阶第十六期:MyBatis,查询请求的生命周期全景图(一)

专栏:JavaEE 进阶跃迁营

个人主页:手握风云

目录

一、MyBatis

[二、MyBatis 入门](#二、MyBatis 入门)

[2.1. 创建项目](#2.1. 创建项目)

[2.2. 配置数据库连接字符串](#2.2. 配置数据库连接字符串)

[2.3. 数据准备](#2.3. 数据准备)

[2.3. 持久层代码](#2.3. 持久层代码)

[2.4. 单元测试](#2.4. 单元测试)

[三、MyBatis 的基础操作](#三、MyBatis 的基础操作)

[3.1. 打印日志](#3.1. 打印日志)

[3.2. 查(Select)](#3.2. 查(Select))

[3.3. 参数传递](#3.3. 参数传递)


一、MyBatis

MyBatis 是一款优秀的持久层(Persistence Layer)框架,其核心目标是简化 JDBC 的开发流程。根据官网定义,MyBatis 支持自定义 SQL、存储过程以及高级映射。在传统的 JDBC 开发中,程序员需要手动编写大量代码来管理数据库连接、设置参数以及处理结果集,过程繁琐且容易出错。MyBatis 通过封装这些底层细节,几乎免除了所有的 JDBC 代码,使开发者能够专注于 SQL 语句和业务逻辑本身。从架构上看,它通常位于 Web 应用程序的 Dao 层(数据访问层),负责程序与数据库之间的交互。

MyBatis 的前身是 Apache 的开源项目 iBatis。该项目于 2010 年由 Apache 迁移到了 Google Code,并正式更名为 MyBatis,随后在 2013 年迁移至 GitHub。这种历史背景使其拥有成熟的技术积淀和广泛的社区支持。作为一款半自动化的 ORM(对象关系映射)框架,MyBatis 既保留了 SQL 的灵活性,又提供了对象映射的便利性,非常适合需要对 SQL 进行精细控制的场景。

在技术实现上,MyBatis 提供了高度的灵活性,支持 XML 配置文件和 Java 注解两种方式来配置和映射原生信息。它能够将数据库中的记录映射为 Java 的原生类型、接口和 POJO(Plain Old Java Objects,普通老式 Java 对象)。通过简单的配置,MyBatis 就能自动将 SQL 查询结果转换为 Java 对象,彻底解决了传统 JDBC 中手动解析 ResultSet 的痛点。此外,它还支持动态 SQL 功能,允许根据不同的条件动态生成 SQL 语句,极大地提高了代码的可维护性和复用性。

二、MyBatis 入门

2.1. 创建项目

添加依赖 Spring Web,MySQL Driver,MyBatis FrameWork。创建好项目之后,会自动导入 MySQL 驱动和 MyBatis 依赖。

XML 复制代码
<dependency>
	<groupId>org.mybatis.spring.boot</groupId>
	<artifactId>mybatis-spring-boot-starter</artifactId>
	<version>4.0.1</version>
</dependency>

<dependency>
	<groupId>com.mysql</groupId>
	<artifactId>mysql-connector-j</artifactId>
	<scope>runtime</scope>
</dependency>

2.2. 配置数据库连接字符串

XML 复制代码
# 数据库连接配置
spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncoding=utf8&useSSL=false
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver

password 改成自己设置的数据库密码。如果密码为空,则使用"";如果密码为纯数字,也需要使用""。

2.3. 数据准备

sql 复制代码
-- 1. 删除已存在的数据库(避免冲突)
DROP DATABASE IF EXISTS mybatis_test;

-- 2. 创建数据库并指定默认字符集
CREATE DATABASE mybatis_test DEFAULT CHARACTER SET utf8mb4;

-- 3. 使用目标数据库
USE mybatis_test;

-- 4. 删除已存在的用户表(避免冲突)
DROP TABLE IF EXISTS user_info;

-- 5. 创建用户表 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; 

-- 6. 插入4条测试用户数据
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' );
java 复制代码
package com.yang.test2_3_1.model;

import lombok.Data;

import java.util.Date;

@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;
}

2.3. 持久层代码

java 复制代码
package com.yang.test2_3_1.mapper;

import com.yang.test2_3_1.model.UserInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

import java.util.List;

@Mapper
public interface UserInfoMapper {
    @Select("SELECT * FROM `user_info`")
    public List<UserInfo> selectList();
}
java 复制代码
package com.yang.test2_3_1.controller;

import com.yang.test2_3_1.model.UserInfo;
import com.yang.test2_3_1.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RequestMapping("/user")
@RestController
public class UserController {
    @Autowired
    private UserService userService;

    @RequestMapping("/getList")
    public List<UserInfo> getList() {
        return userService.getList();
    }
}
java 复制代码
package com.yang.test2_3_1.service;

import com.yang.test2_3_1.mapper.UserInfoMapper;
import com.yang.test2_3_1.model.UserInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class UserService {
    @Autowired
    private UserInfoMapper userInfoMapper;

    public List<UserInfo> getList() {
        return userInfoMapper.selectList();
    }
}

2.4. 单元测试

上面的方法要是测试起来非常麻烦,而 test 包路径下已经包含了测试类。

java 复制代码
package com.yang.test2_3_1;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class Test231ApplicationTests {

    @Test
    void contextLoads() {
    }

}

@SpringBootTest 告诉 Spring Boot 去寻找主配置类(通常是带有 @SpringBootApplication 的类),并使用它来启动一个 Spring 应用上下文(Application Context)。@Test 注解用于标记方法为一个测试用例,运行测试时 JUnit 会执行这个方法。

如果我们写了多个接口,我们就可以使用 IDEA 创建测试类。我们使用快捷键 alt + 回车,点击 Test。

java 复制代码
package com.yang.test2_3_1.mapper;

import com.yang.test2_3_1.model.UserInfo;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

@SpringBootTest
class UserInfoMapperTest {
    @Autowired
    private UserInfoMapper userInfoMapper;

    @BeforeEach
    void setUp() {
        System.out.println("Before Each");
    }

    @AfterEach
    void tearDown() {
        System.out.println("After Each");
    }

    @Test
    void selectList() {
        List<UserInfo> userInfos = userInfoMapper.selectList();
        System.out.println(userInfos);
    }
}

三、MyBatis 的基础操作

3.1. 打印日志

java 复制代码
mybatis:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

3.2. 查(Select)

java 复制代码
@Select("SELECT id, username, `password`, age, gender, phone, " +
        "delete_flag, create_time from user_info")
public List<UserInfo> selectList2();
java 复制代码
@Test
void selectList2() {
    List<UserInfo> userInfos = userInfoMapper.selectList2();
    System.out.println(userInfos);
}

从上面的运行结果可以看出,delete_flag、create_time、update_time,这个字段名返回的是 null,这是因为在 Java 的命名规范中,字段名采用小驼峰命名,而 MySQL 中字段全部采用小写,如果字段对应不上,就会映射失败。

第一个方法:起别名。

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> selectList3();
java 复制代码
@Test
void selectList3() {
    List<UserInfo> userInfos = userInfoMapper.selectList3();
    System.out.println(userInfos);
}

第二个方法,结果映射。

java 复制代码
@Results(id = "BaseMap", value = {
        @Result(column = "delete_flag", property = "deleteFlag"),
        @Result(column = "create_time", property = "createTime"),
        @Result(column = "update_time", property = "updateTime"),
})
@Select("SELECT * FROM `user_info`")
List<UserInfo> selectList4();
java 复制代码
@Test
void selectList4() {
    List<UserInfo> userInfos = userInfoMapper.selectList4();
    System.out.println(userInfos);
}

第三个方法,开启驼峰命名。先在 applicaiton 里面添加配置。

java 复制代码
mybatis:
  configuration:
    map-underscore-to-camel-case: true  #自动驼峰转换
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> selectList5();
java 复制代码
@Test
void selectList5() {
    List<UserInfo> userInfos = userInfoMapper.selectList5();
    System.out.println(userInfos);
}

3.3. 参数传递

参数传递的核心目的是解决 SQL 语句中参数固定化问题,实现动态传入参数,典型场景为根据动态条件(如 id)查询数据。

如果 Mapper 接口方法形参仅为**一个普通类型参数。**比如我们想查询 id = 4 的用户。

java 复制代码
package com.yang.test2_3_1.mapper;

import com.yang.test2_3_1.model.UserInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

@Mapper
public interface UserInfoMapper2 {
    @Select("SELECT * FROM `user_info` where id = #{id}")
    UserInfo selectById(Integer id);
}
java 复制代码
package com.yang.test2_3_1.mapper;

import com.yang.test2_3_1.model.UserInfo;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class UserInfoMapper2Test {
    @Autowired
    private UserInfoMapper2 userInfoMapper2;

    @Test
    void selectById() {
        UserInfo userInfo1 = userInfoMapper2.selectById(1);
        System.out.println(userInfo1);
    }
}

能正确查询出 id=4 的用户数据,日志显示参数以预编译形式(? 占位)传入。

如果需明确参数标识(如多参数场景、参数名需与 SQL 中占位符严格对应),通过 @Param 为参数设置别名。

java 复制代码
@Select("select username, `password`, age, gender, phone from user_info where id= #{userid} ")
UserInfo queryById(@Param("userid") Integer id); // 为id设置别名userid
相关推荐
大模型玩家七七2 小时前
安全对齐不是消灭风险,而是重新分配风险
android·java·数据库·人工智能·深度学习·安全
wxin_VXbishe2 小时前
springboot旅游信息管理系统-计算机毕业设计源码21675
java·c++·spring boot·python·spring·django·php
Serene_Dream2 小时前
Java 垃圾收集器
java·jvm·面试·gc
爬山算法2 小时前
Hibernate(86)如何在性能测试中使用Hibernate?
java·后端·hibernate
索荣荣2 小时前
Web基石:Java Servlet 全面指南:从基础原理到 Spring Boot 实战
java·springboot·web
菜鸟小杰子2 小时前
Spring Boot集成asyncTool:复杂任务的优雅编排与高效执行(实战优化版)
java·spring boot·后端
茶本无香2 小时前
Spring 异步执行器(Executor)配置策略与命名实践
java·spring·多线程·异步
弹简特2 小时前
【JavaEE06-后端部分】SpringMVC01-Spring MVC第一大核心URL 路由映射【建立连接】与 Postman 接口测试详解
java·spring boot·测试工具·spring·postman
rannn_1112 小时前
【苍穹外卖|Day3】公共字段自动填充、新增菜品功能、菜品分页查询功能、删除菜品功能、修改菜品功能、起售停售菜品
java·spring boot·后端·学习·项目