目录
1.前言
哈喽大家好吖,今天我们开始学习MyBatis,今天我们先学习如何用注解的方式完成代码的编写,下一篇博文在完成如何使用XML文件实现MyBatis的编程。无论你是想10分钟快速上手,还是彻底搞懂MyBatis的底层逻辑,无需任何MyBatis基础,我将从环境搭建、日志打印、参数绑定,一路带你手撕增删改查核心操作。
插播一条消息~
发现一个系统化的人工智能学习平台,涵盖从基础理论到工业级项目实战(人脸识别/自动驾驶/GANs等),内容由浅入深结构清晰,特别适合想体系化提升AI开发能力的朋友,忍不住把干货分享给大家👉
人工智能教学网站https://www.captainbed.cn/scy
2.正文
官网:MyBatis中文网(广告有点多看着心刺挠~)
2.1MyBatis与JDBC
MyBatis 和 JDBC 是 Java 中操作数据库的两个不同层级的工具,它们之间既有继承关系,也有显著的差异:
基础关系:
JDBC(Java Database Connectivity)是 Java 提供的标准数据库访问接口,定义了操作数据库的通用 API(如
Connection
、Statement
、ResultSet
),但需要开发者手动编写所有数据库操作代码。MyBatis 是基于 JDBC 的持久层框架,对 JDBC 进行了高级封装,简化了数据库操作流程,同时保留了直接编写 SQL 的灵活性。
关系总结:
MyBatis 底层依赖 JDBC 驱动与数据库交互,但通过封装和扩展,隐藏了 JDBC 的复杂性。
特性 JDBC MyBatis 代码复杂度 需要手动管理连接、语句、结果集,代码冗余。 自动管理资源(如连接、事务),减少样板代码。 SQL 与代码耦合 SQL 嵌入 Java 代码中,难以维护。 SQL 与 Java 代码解耦,通过 XML 或注解配置。 结果集映射 需手动遍历 ResultSet
并映射到对象。自动将结果集映射到 Java 对象(ORM 特性)。 动态 SQL 需手动拼接字符串,易出错且不安全。 支持动态 SQL 标签(如 <if>
、<foreach>
)。事务管理 需手动提交/回滚事务。 支持声明式事务管理(整合 Spring 时更灵活)。 缓存机制 无内置缓存。 提供一级缓存(SqlSession 级别)和二级缓存。 扩展性 直接控制底层,灵活性高但开发效率低。 通过插件机制扩展功能(如分页、日志)。
2.2MyBatis入门
2.2.1准备工作
配置项目用到的jar包:
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;
-- 添加用户信息
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' );
-- 创建文章表
DROP TABLE IF EXISTS article_info;
CREATE TABLE article_info (
id INT PRIMARY KEY auto_increment,
title VARCHAR ( 100 ) NOT NULL,
content TEXT NOT NULL,
uid INT NOT NULL,
delete_flag TINYINT ( 4 ) DEFAULT 0 COMMENT '0-正常, 1-删除',
create_time DATETIME DEFAULT now(),
update_time DATETIME DEFAULT now()
) DEFAULT charset 'utf8mb4';
-- 插入测试数据
INSERT INTO article_info ( title, content, uid ) VALUES ( 'Java', 'Java正文', 1 );
2.2.2配置数据库
配置yml文件:
spring:
application:
name: mybatis-demo
# 数据库配置
datasource:
url: jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
2.2.3持久层代码
编写代码规范:
数据库字段:全部小写,单词之间使用_分割
Java规范:属性使用小驼峰来表示
model包(采用注解的方式实现):
@Mapper
public interface UserInfoMapper {
@Select("select * from user_info")
List<UserInfo> selectAll();
}
mapper包:
@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.2.4测试
第一种测试方法(在Test文件夹写测试代码):
在test文件下创建mapper包,再在mapper包中创建Test用于测试数据库数据:
@SpringBootTest class UserInfoMapperTest { @Autowired private UserInfoMapper userInfoMapper; @Test void selectAll() { System.out.println(userInfoMapper.selectAll()); } }
测试结果:
或者直接在test文件夹下原有的Tests文件进行测试:
@SpringBootTest class MybatisDemoApplicationTests { @Autowired private ApplicationContext context;//在测试代码中直接注入 @Test void contextLoads() { UserInfoMapper bean = context.getBean(UserInfoMapper.class); bean.selectAll().stream().forEach(x-> System.out.println(x)); } }
也可以正常输出结果。
这里讲解两个新的注解:
@Test
注解:
作用:标记一个方法为单元测试方法,用于验证单个代码单元的逻辑(如一个方法、一个类)。
特点:
不依赖 Spring 上下文:测试方法独立运行,不启动 Spring 容器。
轻量快速:适合测试纯 Java 代码逻辑,无需加载 Spring 相关组件。
需手动处理依赖:如果被测试的类依赖其他组件(如 Service、Repository),需要手动 Mock(例如用 Mockito)。
@SpringBootTest
注解:
作用:标记一个测试类为集成测试,启动完整的 Spring 应用上下文,模拟真实运行环境。
特点:
加载完整 Spring 上下文:自动扫描配置、Bean、数据库、外部服务等。
支持依赖注入:可以直接使用
@Autowired
注入 Bean。配置灵活:通过
webEnvironment
指定 Web 环境(如模拟 Servlet 环境、真实端口等)。较慢:因为加载整个应用,适合测试组件交互、API 接口等。
特性 @Test
@SpringBootTest
测试类型 单元测试 集成测试 Spring 上下文 不加载 加载完整 Spring 上下文 依赖注入 不支持(需手动 Mock) 支持(通过 @Autowired
)执行速度 快 较慢(因加载上下文) 适用场景 验证纯 Java 逻辑 测试组件交互、数据库操作、API 接口
第二种测试方法(通过Controller调用service中方法):
代码结构:
userController:
@RequestMapping("/user") @RestController public class UserInfoController { @Autowired private UserService userService; @RequestMapping("/getAllUser") public List<UserInfo> getAllUser(){ return userService.getAllUser(); } }
userService:
@Service public class UserService { @Autowired private UserInfoMapper userInfoMapper; public List<UserInfo> getAllUser() { return userInfoMapper.selectAll(); } }
运行DemoApplication,并访问网页:
同样测试成功。
此外,我们会还有第三种方法来写测试用例,即让编译器为我们生成:
右键generate选择test出现:
解释@Before与@After:一个测试方法前实现,一个测试方法后实现
将二者勾选上后覆盖:
@SpringBootTest class UserInfoMapperTest { @Autowired private UserInfoMapper userInfoMapper; @Test void selectAll() { System.out.println(userInfoMapper.selectAll()); } @BeforeEach void setUp() { System.out.println("before......."); } @AfterEach void tearDown() { System.out.println("after......"); } }
输出结果如下:
至此,测试流程已讲解完毕,接下来来讲解MyBatis的基础操作。
2.3MyBatis基础操作
2.3.1打印日志
为了方便学习基础操作,很显然打印日志必不可少,所以我们先配置以下日志的输出格式:
mybatis:
# 配置 mybatis xml 的文件路径,在 resources/mapper 创建所有表的 xml 文件
mapper-locations: classpath:mapper/**Mapper.xml
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
map-underscore-to-camel-case: true #配置驼峰自动转换
输出测试日志:
在日志中间部分:
==>:表示向数据库输入的
<==:表示数据库返回的
2.3.2参数传递
MyBatis 的参数传递规则围绕 方法参数名与 SQL 占位符的绑定,分为以下几种场景:
1. 无参数方法
@Select("SELECT * FROM user_info") List<UserInfo> selectAll();
场景:无需传递参数。
要点:
直接执行 SQL,无需处理参数绑定。
2. 单参数方法
案例 1:
selectAllById1
@Select("select * from user_info where id = #{id}") UserInfo selectAllById1(Integer id);
案例 2:
selectAllById2
@Select("select * from user_info where id = #{id}") List<UserInfo> selectAllById2(Integer id);
参数规则:
单参数时:占位符
#{xxx}
的xxx
可以任意命名(如#{id}
、#{value}
、#{abc}
均可)。MyBatis 默认按参数顺序绑定,与占位符名称无关。
但为了可读性,建议占位符名称与参数名一致。
返回值差异:
selectAllById1
:返回单个对象(查询结果最多一条时)。
selectAllById2
:返回列表(即使结果只有一条)。
3. 多参数方法
@Select("select * from user_info where username = #{username} and password = #{password}") List<UserInfo> selectByNameAndPassword(String username, String password);
参数规则:
默认行为(无
@Param
注解):
MyBatis 会将多个参数封装为
Map
,键为param1, param2, ...
或arg0, arg1, ...
。此时 SQL 占位符必须使用
{param1}
、#{param2}
或#{arg0}
、#{arg1}
:直接使用参数名(如
#{username}
)会报错,因为默认无法解析参数名。推荐做法(使用
@Param
注解):@Select("SELECT * FROM user_info WHERE username = #{username} AND password = #{password}") List<UserInfo> selectByNameAndPassword( @Param("username") String username, @Param("password") String password );
通过
@Param
显式指定参数名,占位符#{xxx}
必须与注解值一致。本质:MyBatis 会将参数封装为 Map,键为
@Param
指定的名称。
MyBatis 的参数绑定最终是 基于键值对的 Map 结构:
-
单参数:键可以是任意名称,或通过
@Param
指定。 -
多参数:必须通过
@Param
指定键,否则只能用param1/arg0
等默认键。
这里最后对比下不同的参数传递:
场景 | 占位符写法示例 | 是否需要 @Param | 注意事项 |
---|---|---|---|
无参数 | 无 | 否 | 直接执行 SQL |
单参数 | #{任意名称} | 否(建议与参数名一致) | 占位符名称无约束,但需保持语义清晰 |
多参数(默认) | #{param1} , #{arg0} | 否 | 可读性差,易出错 |
多参数(推荐) | #{自定义名称} | 是 | 需通过 @Param 指定名称 |
2.3.3增
测试代码:
@Options(useGeneratedKeys = true, keyProperty = "id")
@Insert("insert into user_info(username, password, age) values (#{username}, #{password}, #{age})")
Integer insertUser(UserInfo userInfo);
1. 核心功能
作用:向
user_info
表插入一条用户记录,并回填数据库生成的自增主键到UserInfo
对象的id
属性。返回值:返回插入操作影响的行数(通常为
1
)。
2. 注解解析
(1)
@Insert
注解
定义 SQL:指定插入语句,使用
#{属性名}
占位符绑定参数。
#{username}
、#{password}
、#{age}
会调用UserInfo
对象的getUsername()
、getPassword()
、getAge()
方法获取值。要求:
UserInfo
类必须有对应的getter
方法。
(2)
@Options
注解
用途:控制 SQL 执行的附加选项。
关键参数:
useGeneratedKeys = true
:启用数据库自增主键回填。
keyProperty = "id"
:将生成的主键值回填到UserInfo
对象的id
属性中。要求:
数据库表的主键字段必须支持自增(如 MySQL 的
AUTO_INCREMENT
)。
UserInfo
类必须有id
属性的setter
方法(即setId
)。
2.3.4删(简)
delete from user_info where id=6
把SQL中的常量替换为动态的参数
Mapper接口:
@Delete("delete from user_info where id = #{id}")
void delete(Integer id);
2.3.5改(简)
update user_info set username="zhaoliu" where id=5
把SQL中的常量替换为动态的参数
Mapper接口:
@Update("update user_info set username=#{username} where id=#{id}")
void update(UserInfo userInfo);
2.3.6查
我们前文讲解参数传递的时候牵扯到一些查询操作,但难免会遇到比如代码中的参数名与表中的参数名不一致的情况接下来我们介绍三种解决该问题的方式。
2.3.6.1起别名
第一个方案这里我们就需要用到别名来进行操作了:
@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> selectAll();
2.3.6.2结果映射
当然,上面那个代码显得十分臃肿,这时又有注解来给咱们减轻压力了:
只要告诉注解,哪个参数和哪个参数是一一对应即可。
@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> selectAll();
正常运行~
核心思路解析:
@Results
注解的作用与使用:1.
@Results
注解的作用
定义结果映射规则:将查询结果中的数据库字段(
column
)映射到 Java 对象的属性(property
)。解决命名差异:当数据库字段名与 Java 属性名不一致时(如蛇形命名
delete_flag
vs 驼峰命名deleteFlag
),需手动指定映射关系。复用映射配置:通过
id = "BaseMap"
标记此映射规则,可在其他方法中通过@ResultMap("BaseMap")
复用。
2. 关键注解解析
注解/属性 说明 @Results
定义一组结果映射规则,包含多个 @Result
。id = "BaseMap"
为此映射规则命名,方便其他方法通过 @ResultMap
引用。@Result
定义单个字段的映射关系。 column = "delete_flag"
数据库字段名(实际查询结果中的列名)。 property = "deleteFlag"
Java 实体类属性名( UserInfo
类中的属性)。
3. 执行流程
执行查询:调用
selectAll()
方法,MyBatis 执行 SQL 查询。结果映射:
根据
@Results
定义的规则,将结果集中的delete_flag
映射到UserInfo.deleteFlag
。同理处理
create_time
→createTime
、update_time
→updateTime
。返回对象:将映射后的
UserInfo
对象装入列表返回。
2.3.6.3开启驼峰命名
当然我们可以在yml文件中配置使代码更加便捷:
mybatis:
# 配置 mybatis xml 的文件路径,在 resources/mapper 创建所有表的 xml 文件
configuration: # 配置打印 MyBatis日志
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
map-underscore-to-camel-case: true #配置驼峰自动转换
当然想使用这个配置,你的变量的命名必须严格按照小驼峰命名法(代码就很简单啦)。
@Select("select * from user_info")
List<UserInfo> selectAll();
正常输出:
3.小结
今天的分享到这里就结束了,喜欢的小伙伴点点赞点点关注,需要所有的源代码可以去我的gitee上就可以啦~你的支持就是对我最大的鼓励,大家加油!
爱吃烤鸡翅的酸菜鱼 (crjs-hao) - Gitee.comhttps://gitee.com/crjs-hao另外最后的最后,欢迎大家加入我的社区哦,初创社区难免经验不足,请大家多多包涵,也欢迎大家前来多多交流。