news 2026/5/20 4:14:21

JavaEE进阶:MyBatis 操作数据库(入门)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
JavaEE进阶:MyBatis 操作数据库(入门)

1.什么是MyBatis?

MyBatis是一款优秀的持久层框架,用于简化JDBC的开发.

持久层,指的就是持久化操作的层,通常是指数据访问层.用来操作数据库.

简单来说,MyBatis是更简单完成程序和数据库交互的框架,也就是更简单操作和读取数据库的工具.

接下来我们就通过一个入门的程序,感受一下MyBatis是如何操作数据库的.

2.MyBatis入门

2.1 准备工作

创建一个SpringBoot项目,引入mybatis起步依赖和mysql的驱动包

用mysql数据库创建数据.

用navicat运行mysql脚本.

-- 创建数据库 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');

在Java中创建对应的实体类UserInfo

@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 配置数据库连接字符串

MyBatis中药连接数据库,需要数据库相关配置.

如果是application.properties文件:

#驱动类名称 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver #数据库连接的url spring.datasource.url=jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncoding=utf8&useSSL=false #连接数据库的用户名 spring.datasource.username=root #连接数据库的密码 spring.datasource.password=root

如果是.yml文件:

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

2.3 写持久层代码

在项目中创建持久层接口UserInfoMapper

@Mapper public interface UserInfoMapper { @Select("select * from user_info") public List<UserInfo> getAll(); }

Mybatis的持久层接口规范一般都叫XxxMapper

@Mapper注解:表示是MyBatis中的Mapper接口.

程序运行时,框架会自动生成接口的实现类对象.并交给SpringIoC容器管理.

@Select注解:代表的就是Select查询,也就是注解对应方法中的具体实现内容.

2.4 单元测试

在创建出来的SpringBoot工程中,在src的test目录下,已经帮我们自动创建好了测试类,我们可以直接用这个测试类来进行测试.

运行程序,就帮我们拿到了数据库中的信息.

除此之外,我们还可以使用IDEA中自动生成测试类.右键Generate,然后选Test

然后选择要测试的方法就行

@SpringBootTest class UserInfoMapperTest { @Autowired private UserInfoMapper userInfoMapper; @Test void getAll() { System.out.println(userInfoMapper.getAll()); } }

运行代码,同样可以拿到相同的结果.

3.MyBatis的基础操作

3.1 打印日志

在Mybatis中,我们可以借助日志,查看SQL语句的执行,执行传递的参数以及执行的结果.

在配置文件中配置即可.

yml的配置:

mybatis: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

properties配置:

mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

将配置加入即可,然后重新运行程序,观察结果.就可以拿到日志信息了

这里显示了查询语句,传递参数以及类型.还有查询结果.

3.2 参数传递

需求,想要查找ID = 4的用户,对应的SQL就是select * from user_info where id = 4

但是这样的话,我们就只能查找id = 4的数据了,所以SQL语句中,不能把id写成固定的值,需要动态的数值.

解决方法,在getOne方法中,添加一个参数,将方法中的参数,传递给SQL语句.

我们使用#{}来获取方法中的参数.

添加测试用例:

运行程序:

在写SQL语句的时候,我们也可以通过@Param对参数起别名.这样在SQL语句中的参数就要和别名一样了.

3.3 增(Insert)

SQL语句:

insert into user_info (username, `password`, age, gender, phone) values ("zhaoliu","zhaoliu",19,1,"18700001234")

使用#{}将SQL中的常量转换为动态的参数

@Insert("insert into user_info (username, `password`, age, gender, phone) values (#{username},#{password},#{age},#{gender},#{phone})") public int insert(UserInfo userInfo);

生成测试用例,然后用userInfo对象的属性名来获取参数:

@Test void insert() { UserInfo userInfo = new UserInfo(); userInfo.setUsername("zhaoliu"); userInfo.setPassword("zhaoliu"); userInfo.setGender(2); userInfo.setAge(21); userInfo.setPhone("18612340005"); userInfoMapper.insert(userInfo); }

运行代码,观察结果.显示成功

打开数据库,也可以看到成功添加这条数据.

正常情况下,Insert语句返回的是受影响的行数.

但是在有些情况下,数据插入之后,还有相关的后续操作.需要获取到新插入数据的id

如果想要拿到自增id,就需要再@Mapper接口上添加一个Option注解.

@Options(useGeneratedKeys = true, keyProperty = "id")

useGeneratedKeys:这会令Mybatis使用jdbc的getGeneratedKeys方法取出由数据库内部生产的主键.

keyProperty:指定能够唯一识别对象的属性,mybatis会使用getGeneratedKeys的返回值或者insert语句的selectKey子元素设置它的值.

设置useGeneratedKey = true之后,方法返回的依然是受影响的行数,自增id会在设置上述keyProperty指定属性中.

有的人可能就会问了,这个id本来就是自增啊,要这个注解有啥用.

如果不加这个注解.就不能从对象中获取到id.把刚刚的option注解注释掉,再次运行看看结果.

这里的id就显示为null.

这个注解的作用就是在不手动给对象添加id的情况下把数据库生成的id回填给Java对象.

3.4 删(Delete)

SQL语句:

delete from user_info where id = 6

把SQL中的常数换为动态参数.

生成测试用例并运行代码

3.5 改(Update)

SQL语句:

update uer_info set nsername="zhaoliu" where id=5

运行代码,去数据库中观察,可以看到已经被改为wnagwu1

3.6 查(Select)

我们在上面查询的时候发现,有几个字段是没有被赋值的.在数据库中有值,但是在select中却查不到.

这是因为在自动映射查询结果的时候,mybatis会获取结果并在Java类中查找相同名字的属性,而Java类中的deleteFlag和SQL中的delete_flag名字不同.

这时候就有三种解决办法了.

1.起别名

在SQL语句中,给列起别名,让列名和Java对象属性名一致.

2.结果映射

我们可以用@Results注解创建映射关系,后续其他SQL想用这个映射关系,就可以使用@ResultMap注解

执行查询,看看结果.此时就可以拿到所有的数据.

3.开启驼峰命名

数据库一般使用_分割,而Java对象一般使用驼峰命名,这时候我们只需要在这两种命名方式之间进行映射就行,需要将mapUnderscoreToCameCase设置为true.

这样,再次运行程序,也可以正确映射到Java对象中.

4.MyBatis XML配置文件

Mybatis的开发方式有两种,一种是注解的方式,另一种使用XML的方式.

上面,我们使用的是注解来实现mybatis,接下来让我们学习使用xml来实现mybatis

4.1 配置连接字符串和MyBatis

如果使用的是yml配置文件:

# 数据库连接配置 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 # 配置 mybatis xml 的文件路径,在 resources/mapper 创建所有的 xml 文件 mybatis: mapper-locations: classpath:mapper/**Mapper.xml

properties配置文件:

# 驱动类名称 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver # 数据库连接的url spring.datasource.url=jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncoding=utf8&useSSL=false # 连接数据库的用户名 spring.datasource.username=root # 连接数据库的密码 spring.datasource.password=root # 配置 mybatis xml 的文件路径,在 resources/mapper 创建所有的 xml 文件 mybatis.mapper-locations=classpath:mapper/**Mapper.xml

4.2 写持久层代码

持久层代码分为两部分,方法定义和方法实现.

4.2.1 添加mapper接口

数据持久层接口定义:

@Mapper public interface UserInfoXMLMapper { public List<UserInfo> quaryAllList(); }

4.2.2 添加UserInfoXMLMapper.xml

数据持久层的实现,mybatis的固定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="com.mybatis.mapper.UserInfoMapper"> </mapper>

创建路径在resources的mapper包下面

查询所有用户的具体实现:

将.xml文件中的namespace改为想要实现的接口路径.

然后写xml的实现.用<select></select>将select语句包起来

然后id就是要实现接口的方法名.resultType就是返回的数据类型.

单元测试:

生成单元测试用例,运行代码

就可以拿到正确结果了.

4.3 增删改查操作

4.3.1 增(Insert)

定义接口:

接口实现:

测试用例:

运行代码,成功添加数据.

如果想要返回自增id,则需要设置useGeneratedKeys和keyProperty属性

4.3.2 删(Delete)

4.3.3 改(Update)

4.3.4 查(Select)

在查询的时候,和我们使用注解相同,也会出现匹配不到内容的情况,这时候解决方法也是有三个,起别名,结果映射和开启驼峰命名

这里我们主要讲结果映射.

5.其他查询操作

5.1 多表查询

多表查询和代表查询类似,只是SQL不同.

在上面,我们创还能了一张用户表,我们再来创建一张文章表,进行多表查询.

MySQL代码:

-- 创建文章表 DROP TABLE IF EXISTS articleinfo; CREATE TABLE articleinfo ( 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 articleinfo ( title, content, uid ) VALUES ( 'Java', 'Java正文', 1 );

Java实体类:

import lombok.Data; import java.util.Date; @Data public class ArticleInfo { private Integer id; private String title; private String content; private Integer uid; private Integer deleteFlag; private Date createTime; private Date updateTime; }

数据查询:

需求:根据uid查询作者名称等相关信息.

SQL:

select ta.id,ta.title,ta.content,ta.uid,tb.username,tb.age,tb.gender from articleinfo ta left join user_info tb on ta.uid = tb.id where ta.id = 1

补充实体类:

接口定义:

生成测试用例,然后运行代码:

5.2 #{}和${}的使用

5.2.1 预编译和即时编译

当我们传递Integer参数的时候

我们将#{}替换为${},看看SQL的打印日志有什么变化.

使用#{}的时候,SQL的日志:

使用${}的时候,SQL的日志:

这个时候都能正常运行,当我们换成String类型参数,再试试

使用#{}

使用${}

当使用#{}的时候,可以正常运行程序,但是当换成${}的时候,报错了,我们可以看到在使用${}的时候,IDEA直接把admin拼接在了SQL语句的后面,没有用'',这时候程序才会报错.

我们将${}放在单引号中,再次运行程序.

这次程序就可以正常运行.

使用#{}是预编译SQL,也就是通过?占位的方式,提前对SQL进行编译,然后把参数填充到SQL语句中,#{}会根据参数类型,自动拼接' '

${}会直接进行字符串替换,一起对SQL进行编译,如果参数为字符串,需要加上' '

5.2.2 #{}和${}的区别

#{}和${}的区别就是预编译和即时编译的区别.

1.性能更高

绝大多数情况下,某一条 SQL 语句可能会被反复调用执行,或者每次执行的时候只有个别的值不同 (比如 select 的 where 子句值不同,update 的 set 子句值不同,insert 的 values 值不同).

如果每次都需要经过上面的语法解析,SQL 优化、SQL 编译等,则效率就明显不行了.

预编译 SQL, 编译一次之后会将编译后的 SQL 语句缓存起来,后面再次执行这条语句时,不会再次编译 (只是输入的参数不同), 省去了解析优化等过程,以此来提高效率

2.更安全(防止SQL注入)

SQL 注入:是通过操作输入的数据来修改事先定义好的 SQL 语句,以达到执行代码对服务器进行攻击的方法。

当我们将${}中传递的数据更改,将方法接收改为List,再次查询:

就会一次性查出所有数据,而用#{}就不会有这种风险.

使用#{}可以防止SQL注入.

5.3 排序功能

从上面的例子中,我们可以知道使用${}会有SQL注入的风险,所以我们尽量使用#{}来完成查询操作.

既然如此,那么${}存在的意义是什么呢?

在我们使用软件的时候,也会碰到排序这种功能,比如价格从高到低,销量从高到低排序等.

这种情况下,我们的SQL语句就是

@Select("select id, username, age, gender, phone, delete_flag, create_time, update_time from user_info order by id ${sort} ")

在这里我们需要使用${}而不能使用#{}.

因为在mybatis中,预编译机制会将#{}预编译成?

比如你写的是 select * from userinfo order by id #{}.

数据库就会编译成 select * from userinfo order by id ?

这样就会出现SQL语法错误.

所以#{}只能处理值,而不能处理列名或者关键字.

使用${},运行程序,可以正确排序.

使用#{}代替${},运行程序.

可以看到这边出现报错信息.

5.4 like查询

like使用#{}也会报错,同样是因为会被编译成?

所以在like查询的时候,同样需要使用$[{}来完成.

但是使用${}会出现SQL注入的问题.

这里我们就可以使用MySQL的内置函数concat()来处理.

正常使用${}.

使用MySQL内置函数concat()

6.数据库连接池

6.1 介绍

在上面mybatis的讲解中,我们使用了数据库连接池技术,避免频繁创建销毁连接.

那么什么是数据库连接池?

数据库连接池负责分配管理和释放数据库连接.他允许应用程序重复使用一个现有的数据库连接,而不是再建立一个.

没有使用数据库连接池的情况:

每次执行 SQL 语句,要先创建一个新的连接对象,然后执行 SQL 语句,SQL 语句执行完,再关闭连接对象释放资源。

这种重复的创建连接,销毁连接比较消耗资源.

使用数据库连接池的情况:

程序启动时,会在数据库连接池中创建一定数量的 Connection 对象,当客户请求数据库连接池,会从数据库连接池中获取 Connection 对象,然后执行 SQL, SQL 语句执行完,再把 Connection 归还给连接池.

优点:

1.减少了网络开销.

2.资源重用.

3.提升了系统的性能.

2.使用

常见的数据库连接池有C3P0,DBCP,Druid,Hikari.

SpringBoot用的数据库连接池就是Hikari,可以在SpringBoot的日志上看到.

如果我们想把默认的数据库连接池换成Druid,只需要引入相关依赖即可.

<dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-3-starter</artifactId> <version>1.2.21</version> </dependency>
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/20 4:13:11

ARM PMU原理与缓存性能优化实战

1. ARM PMU基础概念与工作原理性能监控单元(PMU)是现代处理器中用于硬件事件统计的关键模块&#xff0c;在ARM架构中扮演着系统性能分析的重要角色。PMU通过专用计数器实时记录各类微架构事件&#xff0c;为开发者提供底层硬件行为的可视化窗口。1.1 PMU的硬件实现机制ARM PMU通…

作者头像 李华
网站建设 2026/5/20 4:13:10

量子能量隐形传态(QET)原理与应用解析

1. 量子能量隐形传态&#xff08;QET&#xff09;基础解析量子能量隐形传态&#xff08;Quantum Energy Teleportation, QET&#xff09;是近年来量子信息科学领域最具突破性的概念之一。与传统的量子隐形传态&#xff08;Quantum Teleportation&#xff09;不同&#xff0c;QE…

作者头像 李华
网站建设 2026/5/20 4:07:13

一小时搞懂Python函数:原理+实践

目录 &#x1f644;什么是Python函数&#xff08;了解函数的概念&#xff09; &#x1f914;为什么需要它&#xff1f;&#xff08;背景和痛点&#xff09; &#x1f62e;函数的分类&#xff08;函数有哪些&#xff1f;&#xff09; 内置函数 标准库函数 第三方库函数 定…

作者头像 李华
网站建设 2026/5/20 4:07:07

企业级AI选型决策指南:Gemini Ultra在金融文档解析、代码生成、多模态检索三大场景的ROI实测(含TCO测算表)

更多请点击&#xff1a; https://codechina.net 第一章&#xff1a;企业级AI选型决策框架与Gemini Ultra定位解析 企业在构建AI能力体系时&#xff0c;需超越单一模型性能指标&#xff0c;转向系统性、可治理、可持续的选型决策框架。该框架涵盖五大核心维度&#xff1a;任务适…

作者头像 李华
网站建设 2026/5/20 4:04:06

Tabbit:美团Tabbit AI浏览器实测:从“看网页”到“替我干活”

一、核心主旨 Tabbit是一款AI原生浏览器&#xff0c;通过深度整合上下文理解、智能任务执行和高效信息管理功能&#xff0c;旨在成为用户的生产力工具&#xff0c;而非仅仅是网页浏览工具。 二、核心亮点 2.1 标签管理 随时唤起 AI&#xff0c;结合上下文精准回答。 智能标签…

作者头像 李华