目录
一、什么是Mybatis
MyBatis 是一款优秀的持久层框架,用于简化数据库操作。它通过 XML 或注解的方式将 Java 对象与数据库表中的记录进行映射,避免了手动编写 JDBC 代码的繁琐过程。MyBatis 支持定制化 SQL、存储过程以及高级映射,同时保持了良好的灵活性和性能。
Mybatis本是Apache的一个开源项目iBatis,2010年这个项目有Apache迁移到了google code,并改名为Mybatis,2013年11月迁移到Github。
持久层
持久层指的就是持久化操作的层,通常指数据访问层(Dao),是用来操作数据库的。
二、Mybatis入门
Mybatis操作数据库的步骤:
- 准备工作:创建Sprintboot工程、数据库表准备、实体类
- 引入Mybatis的相关依赖,配置Mybatis数据库连接信息
- 编写SQL语句(注解/XML)
- 测试
1. 准备工作
1.1 创建工程
创建springboot工程,并导入mybatis的起步依赖和mysql的驱动包(Mybatis是一个持久层框架,具体的数据存储和数据操作还是在MySQL中操作的,所以需要添加MySQL驱动)。

SpringBoot版本和Mybatis版本对应参考:https://mybatis.org/spring-boot-starter/mybatis-spring-boot-autoconfigure/

1.2 数据准备
创建用户表和对应的实体类UserInfo:
-- 创建数据库
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' );
UserInfo:字段名和属性名要一一对应
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;
}
规范:
数据库字段:全部小写,单词之间使用下划线(_)分割
Java属性:使用小驼峰来表示
代码结构一定要体现出MVC架构:
- Controller层 (表现层/控制层)
- Service层 (业务逻辑层)
- Mapper层 (数据访问层/持久层)
- Model层 (模型层/实体层)
2. 配置数据库连接字符串
Mybatis中要连接数据库,需要数据库相关参数(MySQL驱动类,登录名,密码,数据库连接字符串)配置。
application.yml:
# 数据库连接配置
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncoding=utf8&useSSL=false
username: root
password: root # 这里写自己的MySQL用户名和密码
driver-class-name: com.mysql.cj.jdbc.Driver
如果自己的密码是例如123456的数字的话,记得加上双引号 password:"123456"
注意事项:如果使用MySQL是5.x之前的使用的是“com.mysql.jdbc.Driver”,如果是大于5.x使用的是"com.mysql.cj.jdbc.Driver"。
IDEA数据库可视化:
配置完成后,在代码左边会出现这样的符号:
点击,会弹出这样的界面,填上自己的用户名和密码,其他保持默认:
点击OK,编译器右侧工具栏的Database就会出现我们配置的MySQL,刚才新建的mybatis数据库和user_info数据表:
可视化:
点击“+”号,Query Console 还可以新建查询:
测试一下:
(学会这招,就不用在IDEA和Navicat之间来回切换了哈哈😄)
3. 写持久层代码
在项目中创建持久层接口UserInfoMapper:

import com.bite.mybatisrevisecode.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")
List<UserInfo> selectAllUser();
}
Mybatis的持久层接口规范一般都叫XxxMapper。
@Mapper注解:表示的是Mybatis中的Mapper接口。
程序运行时,框架会自动生成接口的实现类对象(代理对象),并交给Spring的IOC容器管理。
@Select注解:代表的就是select查询,也就是注解对应方法的具体实现内容。
接口的实现有两种方式:注解 / XML。这里使用的是注解的方式,XML方法后文会介绍到。
4. 单元测试
在创建出来的SpringBoot工程中的test目录下,已经自动帮我们创建好了测试类,我们可以直接用这个测试类来进行测试。

test这个文件夹中的代码和测试人员无关,而是开发人员自己写的测试代码。项目的第一个测试人员,一定是开发者自己。
import com.bite.mybatisrevisecode.mapper.UserInfoMapper;
import com.bite.mybatisrevisecode.model.UserInfo;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
class MybatisReviseCodeApplicationTests {
@Autowired
private UserInfoMapper userInfoMapper;
@Test
void contextLoads() {
List<UserInfo> userInfoList = userInfoMapper.selectAllUser();
System.out.println(userInfoList);
}
}
测试类上添加了注解@SpringBootTest,给测试类在运行时,就会自动加载Spring的运行环境。我们通过@Autowired这个注解注入我们要测试的类,就可以开始进行测试了。
运行结果如下:

可以看到,只有SQL语句中查询的字段对应的属性才有赋值。
在需要测试的Mapper接口中,右键->Generate->Test(也可以使用快捷键 Fn+Alt+Insert ->Test):

选择要测试的代码,点击OK:

测试代码:
import com.bite.mybatisrevisecode.model.UserInfo;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
class UserInfoMapperTest {
@Autowired
private UserInfoMapper userInfoMapper;
@Test
void selectAllUser() {
List<UserInfo> userInfoList = userInfoMapper.selectAllUser();
userInfoList.forEach(System.out::println);
//这种写法能更清晰地查看列表中每个对象的详细信息
}
}
记得加@SpringBootTest注解,加载Spring运行环境。
运行结果如下:

三、Mybatis基础操作
1. 打印日志
在Mybatis当中我们可以借助日志查看到SQL语句的执行、执行传递的参数以及执行结果,在配置文件application.yml中进行配置即可。
mybatis:
configuration: # 配置打印 Mybatis日志
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
重新运行程序,可以看到SQL执行语句、传递参数和执行结果。

2. 查(Select)
我们在上面查询时发现,有几个字段是没有赋值的,只有Java对象属性和数据库字段一模一样时,才会进行赋值。

从运行结果上可以看到,我们SQL语句中,查询了delete_flag,create_time,update_time,但是这几个属性却没有赋值。
原因分析:
当自动映射查询结果时,Mybatis会获取结果中返回的列名并在Java类中查找相同名字的属性(忽略大小写)。这意味着如果发现了ID列和id属性,MyBatis会将列ID的值赋给id属性。
解决方法:
- 起别名
- 结果映射
- 开启驼峰命名
2.1 起别名
在SQL语句中,给列名起别名,保持别名和实体类属性名一样。
@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> selectAllUser2();
SQL语句太长时,使用 “+” 进行字符串拼接。
生成Test后,运行结果如下:

2.2 结果映射
@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> selectAllUser3();
如果其他SQL也希望可以复用这个映射关系,可以给这个Results定义一个名称。
@Select("select id, username, password, age, gender, phone, delete_flag, create_time, " +
"update_time from user_info")
@Results(id = "resultMap", value = {
@Result(column = "delete_flag", property = "deleteFlag"),
@Result(column = "create_time", property = "createTime"),
@Result(column = "update_time", property = "updateTime")
})
List<UserInfo> selectAllUser3();
@Select("select id, username, password, age, gender, phone, delete_flag, create_time, " +
"update_time from user_info where id = 1")
@ResultMap(value = "resultMap")
UserInfo selectByResultMap();
生成Test后,运行结果如下:

2.3 开启驼峰命名(推荐)
通常数据库列使用下划线分割各个单词的方法进行命名,而Java属性一般遵循驼峰命名法约定。为了在这两种命名方式之间启用自动映射,需要将 mapUnderscoreToCamelCase 设置为 true。
mybatis:
configuration:
map-underscore-to-camel-case: true #配置驼峰⾃动转换
Java代码不做任何处理:
@Select("select * from user_info")
List<UserInfo> selectAllUser();
添加上述配置,运行代码:

3. 参数传递
需求:查找id=4的用户,对应的SQL就是:select * from user_info where id = 4
@Select("select * from user_info where id = 4")
UserInfo selectById();
但如果这样写,只能查找id=4的数据,所以SQL语句中的id值不能写成固定数值,需要变为动态的数值。解决方案:在 selectById 方法中添加一个参数(id),将方法中的参数传递给SQL语句。
使用 #{ } 的方式获取方法中的参数。
@Select("select * from user_info where id = #{id}")
UserInfo selectById(Integer id);
添加测试用例:
@Test
void selectById() {
System.out.println(userInfoMapper.selectById(4));
}
运行结果:

如果mapper接口方法形参只有一个普通类型的参数,#{ }里面的属性名可以随便写,如:#{id}、#{value}。建议和参数名保持一致。也可以用@Param设置参数别名。
4. #{ } 和 ${ }
Mybatis 参数赋值有两种方式,本文前面使用了 #{ }进行赋值,接下来我们看下二者的区别。
4.1 #{ } 和 ${ } 的使用
4.1.1 Integer类型的参数
@Select("select * from user_info where id = #{id}")
UserInfo selectById(Integer id);
观察打印的日志:

发现我们输出的SQL语句:select * from user_info where id = ?
我们输入的参数并没有在后面拼接,id的值是使用 ?进行占位。这种SQL我们称之为 “预编译SQL”。
我们把 # 改成 $ 再次运行:
@Select("select * from user_info where id = ${id}")
UserInfo selectById(Integer id);

可以看到,这次的参数是直接拼接在SQL中了。
4.2.2 String类型的参数
@Select("select * from user_info where username = #{username}")
UserInfo selectByName(String username);
观察打印的日志,结果正常返回:

我们把 # 改成 $ 再次运行并观察日志:

可以看到,这次的参数依然是直接拼接在SQL语句中了,但是字符串作为参数时,需要添加引号‘’,使用 ${ }不会拼接 ‘’,导致程序报错。
修改代码如下:
@Select("select * from user_info where username = '${username}'")
UserInfo selectByName(String username);
再次运行,结果正确返回:

从上面两个例子可以看出:
#{}使用的是预编译SQL,通过 ?占位的方式,提前对SQL进行编译,然后把参数填充到SQL语句中。#{}会根据参数类型,自动拼接引号 ‘’。
${}会直接进行字符替换,一起对SQL进行编译。如果参数为字符串,需要加上引号 ‘’。(参数为数字类型时,也可以加上,查询结果不变,但是可能会导致索引失效,性能下降)
4.2 #{ } 和 ${ } 的区别
#{} 和 ${} 的区别就是预编译SQL和即时SQL 的区别。
4.2.1 性能更高
绝大多数情况下,某一条SQL语句可能会被反复调用执行,或者每次执行的时候只有个别的值不同(比如select的where子句值不同,update的set子句值不同,insert的values值不同)。如果每次都需要经过上面的语法解析,SQL优化、SQL编译等,则效率就明显不行了。

预编译SQL,编译一次之后会将编译后的SQL语句缓存起来,后面再次执行这条语句时,不会再次编译(只是输入的参数不同),省去了解析优化等过程,以此来提高效率。
4.2.2 更安全(防止SQL注入)
SQL注入:是通过操作输入的数据来修改事先定义好的SQL语句,以达到执行代码对服务器进行攻击的方法。
由于没有对用户输入进行充分检查,而SQL又是拼接而成,在用户输入参数时,在参数中添加一些SQL关键字,达到改变SQL运行结果的目的,也可以完成恶意攻击。
sql注入代码:'or 1='1
先来看看SQL注入的例子:
@Test
void selectByName() {
System.out.println(userInfoMapper.selectByName("' or 1='1"));
}
注意要把mapper接口对应方法的返回数值类型改成List类型的。

结果依然被正确查询出来了, 其中参数 or 被当做了SQL语句的一部分。
可以看出来,查询的数据并不是自己想要的数据。所以用于查询的字段,尽量使用#{}的查询方式。
5. 增(Insert)
SQL语句:
insert into user_info (username, password, age) values ("user1", "pwd1", "1")
把SQL中的常量替换为动态参数。
Mapper接口:
@Insert("insert into user_info (username, password, age) values (#{username}, #{password}, #{age});")
Integer insertUser(UserInfo userInfo);
直接用UserInfo对象的属性名来获取参数。
测试代码:
@Test
void insertUser() {
UserInfo userInfo = new UserInfo();
userInfo.setUsername("user1");
userInfo.setPassword("pwd1");
userInfo.setAge(1);
System.out.println("影响行数: " + userInfoMapper.insertUser(userInfo));
}
运行结果如下:

刷新user_info数据表,可以看出已成功插入数据:

如果设置了 @Param 属性,#{...}需要使用 参数.属性 来获取。
@Insert("insert into user_info (username, password, age) values (#{userinfo.username}, #{userinfo.password}, #{userinfo.age});") Integer insertUser(@Param("userinfo") UserInfo userInfo);测试:插入 userinfo(user2,pwd2,2)
观察运行结果:
返回主键
Insert 语句默认返回的是受影响的行数。但有些情况下,数据插入后还需要有后续的关联操作,需要获取到新插入数据的id。
如果想要拿到自增id,需要在Mapper接口的方法上添加一个Options的注解。
@Options(useGeneratedKeys = true, keyProperty = "id")
@Insert("insert into user_info (username, password, age) values (#{username}, #{password}, #{age});")
Integer insertUser(UserInfo userInfo);
useGeneratedKeys:这会令使用JDBC的getGeneratedKeys方法来取出由数据库内部生成的主键,默认值:false。
keyProperty:指定能够唯一识别对象的属性,Mybatis会使用getGeneratedKeys的返回值或insert语句的selectKey子元素设置它的值。
测试数据:
@Test
void insertUser() {
UserInfo userInfo = new UserInfo();
userInfo.setUsername("user3");
userInfo.setPassword("pwd3");
userInfo.setAge(3);
System.out.println("影响行数: " + userInfoMapper.insertUser(userInfo) + ", id: " + userInfo.getId());
}
运行结果:


注意:设置useGeneratedKeys=true之后,方法返回值依然是受影响的行数,自增id会设置在上述keyProperty指定的属性中。
6. 删(Delete)
SQL语句:
delete from user_info where id = 5
把SQL中的常量换成动态参数。
Mapper接口:
@Delete("delete from user_info where id = #{id}")
Integer deleteUserById(Integer id);
测试删除id为5的记录:


7. 改(Update)
SQL语句:
update user_info set phone = "12345" where id = 7;
把SQL中的常量换成动态参数。
Mapper接口:
@Update("update user_info set phone = #{phone} where id = #{id};")
Integer updateUserPhoneById(UserInfo userInfo);
运行结果:


四、Mybatis XML配置文件
Mybatis的开发有两种方式:注解和XML。上文学习了注解的方式,接下来介绍XML的方式。
使用Mybatis的注解方式,主要是来完成一些简单的增删改查功能。如果需要实现更复杂的SQL功能,建议使用XML来配置映射语句,也就是将SQL语句写在XML配置文件中。
Mybatis XML的方式需要以下两步:
- 配置数据库连接字符串和Mybatis
- 写持久层代码
1. 配置连接字符串和Mybatis
此步骤需要进行两项设置,数据库连接字符串设置和Mybatis的XML文件配置。
在application.yml文件中配置内容如下:
mybatis:
mapper-locations: classpath:mapper/**Mapper.xml
2. 写持久层代码
持久层代码分两部分:
- 方法定义:Interface
- 方法实现:XXX.xml

2.1 添加Mapper接口
数据持久层的接口定义:
import com.bite.mybatisrevisecode.model.UserInfo;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface UserInfoXMLMapper {
List<UserInfo> selectAll();
}
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.bite.mybatisrevisecode.mapper.UserInfoXMLMapper">
</mapper>
创建UserInfoXMLMapper.xml,路径参考yml中的配置。

查询所有用户的具体实现:
上述配置完成后,在接口中会有一个报错。按 Alt + Enter 展示解决方案,点击第一个 "Generate statement",xml文件则会自动生成方法框架。


在<select></select>标签中填写查询的SQL:
<select id="selectAll" resultType="com.bite.mybatisrevisecode.model.UserInfo">
select * from user_info
</select>
- <mapper>标签:需要指定namespace属性,表示命名空间,值为mapper接口的全限定名,包括全包名.类名。
- <select>查询标签:是用来执行数据库的查询操作的:
- id:是和接口中定义的方法名称一样的,表示对接口的具体实现方法;
- resultType:是返回的数据类型,也就是之前定义的实体类userInfo。

2.3 单元测试
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class UserInfoXMLMapperTest {
@Autowired
private UserInfoXMLMapper userInfoXMLMapper;
@Test
void selectAll() {
userInfoXMLMapper.selectAll().forEach(System.out::println);
}
}
运行结果:

3. 增删改查操作
查询操作上述已经实现,这里不再赘述。
3.1 增(Insert)
接口:
Integer insertUser(UserInfo userInfo);
实现:
<insert id="insertUser">
insert into user_info (username, password, age) values (#{username}, #{password}, #{age})
</insert>
@Param设置参数名称,使用方法和注解类似。
返回自增ID
接口定义不变,Mapper.xml实现 设置useGeneratedKeys和keyProperty属性。
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
insert into user_info (username, password, age) values (#{username}, #{password}, #{age})
</insert>
3.2 删(Delete)
接口:
Integer deleteUserById(Integer id);
实现:
<delete id="deleteUserById">
delete from user_info where id = #{id}
</delete>
3.3 改(Update)
接口:
Integer updateUserById(UserInfo userInfo);
实现:
<update id="updateUserById">
update user_info set username = #{username} where id = #{id}
</update>
五、其他查询操作
1. 多表查询
1.1 数据准备
创建文章表:
-- 创建文章表
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 );
对应实体类:
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;
}
1.2 数据查询
需求:根据uid查询作者的名称等相关信息。
SQL:
SELECT
ta.id,
ta.title,
ta.content,
ta.uid,
tb.username,
tb.password
FROM
article_info ta
LEFT JOIN user_info tb ON ta.uid = tb.id
WHERE
ta.id = 1

补充实体类:
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;
private String username;
private Integer age;
}
接口定义:
import com.bite.Mybatis_demo.model.ArticleInfo;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface ArticleInfoMapper {
ArticleInfo selectArticleInfoById(Integer id);
}
xml实现:
<select id="selectArticleInfoById" resultType="com.bite.Mybatis_demo.model.ArticleInfo">
SELECT
ta.*,
tb.username,
tb.age
FROM article_info ta
LEFT JOIN user_info tb ON ta.uid = tb.id
where ta.id=#{id}
</select>
单元测试:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
class ArticleInfoMapperTest {
@Autowired
private ArticleInfoMapper articleInfoMapper;
@Test
void selectArticleInfoById() {
System.out.println(articleInfoMapper.selectArticleInfoById(1));
}
}
运行结果:

2. 排序功能
尽管${ }会有SQL注入的风险,但是排序功能需要用到${ }。
使用场景:
Mapper实现:
@Select("select * from user_info order by id ${order}")
List<UserInfo> selectUserInfoByOrder(String order);
// order传的是正序或逆序(asc/desc)
使用${order}可以实现排序查询,因为此处order参数为String类型,但是SQL语句中,排序规则是不需要加引号的,所以此时的${order}也不加引号。
而使用#{order}不能实现排序查询,因为asc(desc)前后自动加了引号,会导致sql错误。
除此之外,还有表名作为参数时,也只能使用${}。
3. 模糊查询(Like)
like查询使用#{}报错:
@Select("SELECT * FROM `user_info` WHERE username LIKE %#{userName}%'")
List<UserInfo> selectUserInfoByLike(String userName);
把#{}改成${}可以正确查出来,但是${}存在SQL注入的问题,所以不能直接使用${}。
解决办法:使用mysql的内置函数concat()来处理,实现代码如下:
@Select("SELECT * FROM `user_info` WHERE username LIKE CONCAT('%',#{userName},'%')")
List<UserInfo> selectUserInfoByLike(String userName);
六、数据库连接池
数据库连接池是一种管理数据库连接的技术,用于缓存和复用已建立的连接,避免频繁创建和销毁连接带来的性能开销。通过连接池,应用可以高效获取和释放数据库连接,提升系统吞吐量。

没有使用数据库连接池的情况:每次执行SQL语句,要先创建一个新的连接对象,然后执行SQL语句,SQL语句执行完,再关闭连接对象释放资源.这种重复的创建连接,销毁连接比较消耗资源;
使用数据库连接池的情况:程序启动时,会在数据库连接池中创建一定数量的Connection对象,当客户请求数据库连接池,会从数据库连接池中获取Connection对象,然后执行SQL,SQL语句执行完,再把Connection归还给连接池。
优点:1.减少了网络开销 2.资源重用 3.提升了系统性能
常见数据库连接池实现
- HikariCP:以高性能著称,轻量级且配置简单,支持 JDBC4(SpringBoot默认使用的数据库连接池)。
- Apache DBCP:Apache Commons 项目提供的连接池,稳定性高但性能中等。
- C3P0:老牌连接池,功能全面但性能较低,适合传统应用。
- Druid:阿里巴巴开源,支持监控和扩展,适合企业级应用。
目前比较流行的是:Hikari、Druid。
七、动态SQL
动态SQL是Mybatis的强大特性之一,能够完成不同条件下不同的sql拼接。官方文档
1. <if>标签
对于必填字段和非必填字段,需要使用<if>动态标签来判断。如gender为非必填字段,具体实现如下:
接口定义:
List<UserInfo> selectByCondition(UserInfo userInfo);
XML实现:
<select id="selectByCondition" resultType="com.bite.mybatisrevisecode.model.UserInfo">
select * from user_info
where
age > #{age}
<if test="gender!=null">
and
gender = #{gender}
</if>
</select>
或者使用注解方式(不推荐):把上面的SQL(包括标签)使用<script></script>标签括起来就可以。
2. <trim>标签
如果有多个字段是可选项,一般考虑使用标签结合标签,对多个字段都采取动态生成的方式。
标签有如下属性:
- prefix:表示整个语句块以prefix的值作为前缀
- suffix:表示整个语句块以suffix的值作为后缀
- prefixOverrides:表示整个语句块要去除掉的前缀
- suffixOverrides:表示整个语句块要去除掉的后缀
插入用户信息:xml实现
<insert id="insertUserByCondition">
INSERT INTO user_info
<trim prefixOverrides="," suffixOverrides="," prefix="(" suffix=")">
<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 prefixOverrides="," suffixOverrides="," prefix="(" suffix=")">
<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>
3. <where>标签
需求:传入的用户对象,根据属性做where条件查询,用户对象中属性不为null的,都为查询条件。
xml实现:
<select id="selectByCondition" resultType="com.bite.mybatisrevisecode.model.UserInfo">
select id, username, age, gender, phone, delete_flag, create_time, update_time
from userinfo
<where>
<if test="age != null">
and age = #{age}
</if>
<if test="gender != null">
and gender = #{gender}
</if>
<if test="deleteFlag != null">
and delete_flag = #{deleteFlag}
</if>
</where>
</select>
<where>只会在子元素有内容的情况下才插入where子句,而且会自动去除子句的开头的AND或OR。以上标签也可以使用<trim prefix="where" prefixOverrides="and">替换,但是此种情况下当子元素都没有内容时,where关键字也会保留。
4. <set>标签
需求:根据传入的用户对象属性来更新用户数据,可以使用标签来指定动态内容。
<update id="updateUserByCondition">
update userinfo
<set>
<if test="username != null">
username = #{username},
</if>
<if test="age != null">
age = #{age},
</if>
<if test="deleteFlag != null">
delete_flag = #{deleteFlag},
</if>
</set>
where id = #{id}
</update>
<set>动态的在SQL语句中插入set关键字,并会删掉额外的逗号(用于update语句中)。以上标签也可以使用<trim prefix="set" suffixOverrides=",">替换。
创作不易 麻烦给个三连支持一下吧![]()










2027

被折叠的 条评论
为什么被折叠?



