文章目录
- [1. 什么是MyBatis](#1. 什么是MyBatis)
- [2 MyBatis入门](#2 MyBatis入门)
-
- [2.1 准备工作](#2.1 准备工作)
-
- [2.1.1 创建工程](#2.1.1 创建工程)
- [2.2 配置数据库连接字符串](#2.2 配置数据库连接字符串)
- [2.3 写持久层代码](#2.3 写持久层代码)
- [2.4 单元测试](#2.4 单元测试)
- [3. MyBatis的基础操作](#3. MyBatis的基础操作)
-
- [3.1 打印日志](#3.1 打印日志)
- [3.2 参数传递](#3.2 参数传递)
- [3.3 增(insert)](#3.3 增(insert))
-
- [3.3.1 返回主键](#3.3.1 返回主键)
- [3.4 删(delete)](#3.4 删(delete))
- [3.5 改(update)](#3.5 改(update))
- [3.6 查(select)](#3.6 查(select))
-
- [3.6.1 起别名](#3.6.1 起别名)
- [3.6.2 结果映射](#3.6.2 结果映射)
- [3.6.3 开启驼峰命名(推荐)](#3.6.3 开启驼峰命名(推荐))
- [4. MyBatis XML配置文件](#4. MyBatis XML配置文件)
-
- [4.1 配置连接字符串和MyBatis](#4.1 配置连接字符串和MyBatis)
- [4.2 写持久层代码](#4.2 写持久层代码)
-
- [4.2.1 添加mapper接口](#4.2.1 添加mapper接口)
- [4.2.2 添加UserInfoXMLMapper.xml](#4.2.2 添加UserInfoXMLMapper.xml)
- [4.2.3 单元测试](#4.2.3 单元测试)
- [4.3 增删改查操作](#4.3 增删改查操作)
- 5.其他查询操作
-
- 5.1多表查询
-
- [5.1.1 数据查询](#5.1.1 数据查询)
- [5.2 #{}和{}](#{}和{})
-
- [5.2.1 #{}和{}使用](#{}和{}使用)
-
- [5.2.1.1 Interger类型的参数](#5.2.1.1 Interger类型的参数)
- [5.2.1.1 String类型的参数](#5.2.1.1 String类型的参数)
- [5.2.2 #{}和{}区别](#{}和{}区别)
- [5.3 排序功能](#5.3 排序功能)
- [5.4 like查询](#5.4 like查询)
- [6. 数据库连接池](#6. 数据库连接池)
-
- [6.1 介绍](#6.1 介绍)
- [6.2 使用](#6.2 使用)
- 7.总结
-
- [7.1 MySQL开发企业规范](#7.1 MySQL开发企业规范)
- [7.2 #{} 和{} 区别](#{} 和{} 区别)
1. 什么是MyBatis
- MyBatis是一款优秀的持久层框架,用于简化JDBC的开发。
- 持久层:指的就是持久化操作的层,通常指数据访问层(dao),用来操作数据库的。
- 官网:MyBatis中文网
2 MyBatis入门
2.1 准备工作
Mybatis操作数据库的步骤:
- 准备工作(创建springboot工程、数据库表准备、实体类)
- 引入Mybatis的相关依赖,配置Mybatis(数据库连接信息)
- 编写SQL语句(注解/XML)
- 测试
2.1.1 创建工程
创建springboot工程,并导入mybatis的起步依赖、mysql的驱动包
项目工程创建完成后,自动在pom.xml文件中,导入Mybatis依赖和MySQL驱动依赖。
版本会随着SpringBoot版本发生变化SpringBoot3.X对用MyBatis版本为3.X
对应关系参考:https://mybatis.org/spring-boot-starter/mybatis-spring-boot-autoconfigure/
java
<!--Mybatis 依赖包-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.2</version>
</dependency>
<!--mysql驱动包-->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
2.2 配置数据库连接字符串
MyBatis中需要连接数据库,需要数据库相关参数配置
MySQL驱动类
登录名
密码
数据库连接字符串
如果是application.yml文件,配置内容如下:
java
# 数据库连接配置
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: 111111
driver-class-name: com.mysql.cj.jdbc.Driver
【注意】:如果使用MySQL是5.x之前的使用的是"com.mysql.jdbc.Driver",如果是大于5.x使用的是"com.mysql.cj.jdbc.Driver".
如果是application.properties文件,配置内容如下:
java
#驱动类名称
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
2.3 写持久层代码
java
import com.example.mybatis.model.UserInfo;
import org.apache.ibatis.annotations.*;
import org.springframework.beans.factory.support.ScopeNotActiveException;
import java.util.List;
@Mapper
public interface UserInfoMapper {
@Select("select * from user_info")
List<UserInfo> selectAll();
}
- MyBatis的持久层接口规范一般都叫XxxMapper.
- @Mapper注解:表示的是MyBatis中的Mapper接口。
2.1. 程序运行时, 框架会自动生成接口的实现类对象(代理对象),并交给Spring的IOC容器管理。
2.2. @Select注解:代表的就是select查询,也就是注解对应方法的具体实现内容。
2.4 单元测试
在对应的mapper文件中:鼠标右键-->generate--->test
java
import com.example.mybatis.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 static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
class UserInfoMapperTest {
@Autowired
private UserInfoMapper userInfoMapper;
@Test
void selectAll() {
System.out.println(userInfoMapper.selectAll());
}
}
- 测试类上添加@SpringBootTest注解,该测试类在运行时,就会自动加载Spring的运行环境。
- 通过@Autowired这个注解,注入我们所要测试的mapper类,就可以开始进行测试了。
运行结果:
3. MyBatis的基础操作
3.1 打印日志
在Mybatis当中我们可以借助日志,查看到sql语句的执行、执行传递的参数以及执行结果在配置文件中进行配置即可:
application.yml文件:
java
mybatis:
configuration: # 配置打印MyBatis⽇志
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
重新运行程序,可以看到SQL执行内容,以及传递参数和执行结果:
- 查询语句
- 传递参数及类型
- SQL执行类型
3.2 参数传递
需求:查找id=4的用户,对应的SQL就是:select*fromuser_infowhereid=4
java
@Select("select username, `password`, age, gender, phone from user_info where id= 4 ")
UserInfo queryById();
但是这样的话,只能查找id=4的数据,所以SQL语句中的id值不能写成固定数值,需要变为动态的数值.
解决方案:在queryByld方法中添加一个参数(id),将方法中的参数,传给SQL语句.使用#{}
的方式获取方法中的参数。
java
@Select("select username, `password`, age, gender, phone from user_info where id = #{id} ")
UserInfo queryById();
如果mapper接口方法形参只有一个普通类型的参数,#}里面的属性名可以随便写,如:#{id]、#{value]。建议和参数名保持一致.
添加测试用例
java
@Test
void queryById() {
UserInfo userInfo = userInfoMapper.queryById(4);
System.out.println(userInfo);
}
运行结果:
也可以通过@Param
,设置参数的别名,如果使用@Param
设置别名,#.里面的属性名必须和@Param
设置的一样.
java
@Select("select username, `password`, age, gender, phone from user_info where id = #{userid} ")
UserInfo queryById(@Param("userid") Integer id);
3.3 增(insert)
SQL 语句:
java
insert into user_info (username, `password`, age, gender, phone) values ("zhaoliu","zhaoliu",19,1,"18700001234")
把SQL中的常量替换为动态的参数,Mapper接口:
java
@Insert("insert into user_info (username, `password`, age, gender, phone) values (#{username},#{password},#{age},#{gender},#{phone})")
Integer insert(UserInfo userInfo);
测试代码:
java
@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);
}
运行后,观察数据库执行结果
如果设置了
@Param
属性,#{...}
需要使用参数.属性
的方式来获取。
java
@Insert("insert into user_info (username, `password`, age, gender, phone) values (#{userInfo.username},#{userInfo.password},#{userInfo.age},#{userInfo.gender},#{userInfo.phone})")
Integer insert(@Param("userInfo") UserInfo userInfo);
3.3.1 返回主键
Insert语句默认返回的是受影响的行数.但有些情况下,数据插入之后,还需要有后续的关联操作,需要获取到新插入数据的id.
比如订单系统:当我们下完订单之后,需要通知物流系统、库存系统、结算系统等,这时候就需要拿到订单ID。
如果想要拿到自增id,需要在Mapper接口的方法上添加一个@Options
的注解。
java
@Options(useGeneratedKeys = true, keyProperty = "id")
@Insert("insert into user_info (username, age, gender, phone) values (#{userInfo.username},#{userInfo.age},#{userInfo.gender},#{userInfo.phone})")
Integer insert(@Param("userInfo") UserInfo userInfo);
useGeneratedKeys:这会令MyBats使用JDBC的getGeneratedKeys方法来取出数据库内部生成的主键,默认值:false.
keyProperty:指定能够唯一识别对象的属性,MyBatis会使用getGeneratedKeys的返回值或insert语句的selectKey子元素设置它的值,默认值:未设置(unset)
测试数据:
java
@Test
void insert() {
UserInfo userInfo = new UserInfo();
userInfo.setUsername("zhaoliu");
userInfo.setPassword("zhaoliu");
userInfo.setGender(2);
userInfo.setAge(21);
userInfo.setPhone("18612340005");
Integer count = userInfoMapper.insert(userInfo);
System.out.println("添加数据条数:" +count +", 数据ID:" + userInfo.getId());
}
运行结果:
注意:设置useGeneratedKeys=true
之后,方法返回值依然是受影响的行数,自增id
会设置在上述keyProperty
指定的属性中.
3.4 删(delete)
SQL语句:
java
delete from user_info where id=6
把SQL中的常量替换为动态的参数,Mapper接口:
java
@Delete("delete from user_info where id = #{id}")
void delete(Integer id);
3.5 改(update)
SQL语句:
java
update user_info set username="zhaoliu" where id=5
把SQL中的常量替换为动态的参数,Mapper接口:
java
@Update("update user_info set username=#{username} where id=#{id}")
void update(UserInfo userInfo);
3.6 查(select)
我们在上面查询时发现,有几个字段时没有赋值的,只有Java对象属性和数据库字段一模一样时,才会进行赋值。
java
@Select("select id, username, `password`, age, gender, phone, delete_flag, create_time, update_time from user_info")
List<UserInfo> queryAllUser();
查询结果:
从运行结果上可以看到,我们SQL语句中,查询delete_flag,create_time,update_time,但是这几个属性却没有赋值。
- MyBatis会根据方法返回的结果进行赋值。
- 方法用对象UserInfo接收返回结果,MySQL查询出来数据为一条,就会自动赋值给对象。
- 方法用List< UserInfo >接收返回结果,MySQL查询出来数据为一条或多条时,也会自动赋值给list,但如果MySQL查询返回多条,但是方法使用UserInfo接收,MyBatis执行就会报错。
原因分析:
当自动映射查询结果时,MyBatis会获取结果中返回的列名并在Java类中查找相同名字的属性(忽略大小写)。这意味着如果发现了ID列和id属性,MyBatis会将列ID的值赋给id属性。

有三种解决方法:
- 起别名
- 结果映射
- 开启驼峰命名
3.6.1 起别名
在SQL语句中,给列名起别名,保持别名和实体类属性名一样。
java
@Select("select id, username, `password`, age, gender, phone, delete_flag as deleteFlag, create_time as createTime, update_time as updateTime from user_info")
public List<UserInfo> queryAllUser();
【注意】:SQL语句太长时,使用+号进行字符串拼接。
3.6.2 结果映射
java
@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> queryAllUser();
如果其他SQL,也希望可以复用这个映射关系,可以给这个Result定义一个名称。
java
@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> queryAllUser();
@Select("select id, username, `password`, age, gender, phone, delete_flag, create_time, update_time from user_info where id= #{userid} ")
@ResultMap(value = "resultMap")
UserInfo queryById(@Param("userid") Integer id);
使用id
属性给该result
定义别名,使用@ResulutMap
注解来复用其他定义的Result Map
3.6.3 开启驼峰命名(推荐)
通常数据库列使用蛇形命名法进行命名(下划线分割各个单词),而Java属性一般遵循驼峰命名法约定。
为了在这两种命名方式之间启用自动映射,需要将mapUnderscoreToCamelCasei
设置为true。
java
mybatis:
configuration:
map-underscore-to-camel-case: true #配置驼峰⾃动转换
驼峰命名规则:abc_xyz===>abcXyz
- 表中字段名:abc_xyz
- 类中属性名:abcXyz
Java代码不做任何处理
java
@Select("select id, username, `password`, age, gender, phone, delete_flag, create_time, update_time from user_info")
public List<UserInfo> queryAllUser();
添加上述配置,运行代码:
字段全部进行正确赋值。
4. MyBatis XML配置文件
MyBatis的开发有两种方式:
- 注解
- XML
MyBatis XML的方式需要以下两步:
- 配置数据库连接字符串和MyBatis
- 写持久层代码
4.1 配置连接字符串和MyBatis
application.yml文件:
java
spring:
# 数据库连接配置
datasource:
url: jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: 111111
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
mapper-locations: classpath:mapper/**Mapper.xml
4.2 写持久层代码
持久层代码分为两部分:
- 方法定义Interface
- 方法实现:XXX.xml

4.2.1 添加mapper接口
数据持久层的接口定义:
java
import com.example.demo.model.UserInfo;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface UserInfoXMlMapper {
List<UserInfo> queryAllUser();
}
4.2.2 添加UserInfoXMLMapper.xml
数据持久层的实现,MyBatis的固定xml格式:
java
<?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.example.demo.mapper.UserInfoMapper">
</mapper>
创建UserInfoXMLMapper.xml,路径参考yml文件中的配置。
查询所有用户的具体实现:
java
<?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.example.demo.mapper.UserInfoXMlMapper">
<select id="queryAllUser" resultType="com.example.demo.model.UserInfo">
select username,`password`, age, gender, phone from user_info
</select>
</mapper>
<mapper>
标签:需要指定namespace
属性,表示命名空间,值为mapper接口的全限定名,包括全包名.类名。<select>
查询标签:是用来执行数据库的查询操作的。
id
: 是和Interface
(接口)中定义的方法名称一样,表示对接口的具体实现方法。resultType
: 是返回的数据类型,也就是开头我们定义的实体类。

4.2.3 单元测试
java
@SpringBootTest
class UserInfoMapperTest {
@Autowired
private UserInfoMapper userInfoMapper;
@Test
void queryAllUser() {
List<UserInfo> userInfoList = userInfoMapper.queryAllUser();
System.out.println(userInfoList);
}
}
运行结果如下:
4.3 增删改查操作
4.3.1增(lnsert)
UserInfoMapper接口:
java
Integer insertUser(UserInfo userInfo);
UserInfoMapper.xml实现:
java
<insert id="insertUser">
insert into userinfo (username, `password`, age, gender, phone) values (#{username}, #{password}, #{age},#{gender},#{phone})
</insert>
如果使用@Param
设置参数名称的话,使用方法和注解类似:
UserInfoMapper接口:
java
Integer insertUser(@Param("userInfo") UserInfo userInfo);
UserInfoMapper.xml实现:
java
<insert id="insertUser">
insert into user_info (username, `password`, age, gender, phone) values(#{userInfo.username},#{userInfo.password},#{userInfo.age},#{userInfo.gender},#{userInfo.phone})
</insert>
返回自增id
接口定义不变,Mapper.xml实现设置usegeneratedKeys 和 keyProperty属性:
java
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
insert into user_info (username, `password`, age, gender, phone) values(#{userInfo.username},#{userInfo.password},#{userInfo.age},#{userInfo.gender},#{userInfo.phone})
</insert>
4.3.2删(Delete)
UserInfoMapper接口:
java
Integer deleteUser(Integer id);
UserInfoMapper.xml实现:
java
<delete id="deleteUser">
delete from user_info where id = #{id}
</delete>
4.3.3改(Update)
UserInfoMapper接口:
java
Integer updateUser(UserInfo userInfo);
UserInfoMapper.xml实现:
java
<update id="updateUser">
update user_info set username=#{username} where id=#{id}
</update>
4.3.4查(Select)
同样的,使用XML的方式进行查询,也存在数据封装的问题我们把SQL语句进行简单修改,查询更多的字段内容。
java
<select id="queryAllUser" resultType="com.example.demo.model.UserInfo">
select id, username,`password`, age, gender, phone, delete_flag, create_time, update_time from user_info
</select>
运行结果:
结果显示:deleteFlag,createTime,updateTime也没有进⾏赋值。
解决办法和注解类似:
- 起别名
- 结果映射
- 开启驼峰命名
xml使用结果映射:
Mapper.xml
java
<resultMap id="BaseMap" type="com.example.demo.model.UserInfo">
<id column="id" property="id"></id>
<result column="delete_flag" property="deleteFlag"></result>
<result column="create_time" property="createTime"></result>
<result column="update_time" property="updateTime"></result>
</resultMap>
<select id="queryAllUser" resultMap="BaseMap">
select id, username,`password`, age, gender, phone, delete_flag, create_time, update_time from user_info
</select>

开发中使用注解还是XML的方式?
关于开发中使用哪种模式这个问题,没有明确答案.仁者见仁智者见智,并没有统一的标准,更多是取决于你的团队或者项目经理,项目负责人.
5.其他查询操作
5.1多表查询
5.1.1 数据查询
需求:根据uid查询作者的名称等相关信息.
SQL:
java
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
接口定义:
java
import com.example.demo.model.ArticleInfo;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface ArticleInfoMapper {
@Select("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 = #{id}")
ArticleInfo queryUserByUid(Integer id);
}
如果名称不一致的,采用ResultMap,或者契苾民的方式解决,和单表查询一样。
MyBatis不分单表还是多表,主要就是三部分:SQL、映射关系和实体类。
通过映射关系,把SQL运行结果和实体类关联起来。
5.2 #{}和${}
MyBaits参数赋值有两种方式:#{} 和 ${}。
5.2.1 #{}和${}使用
5.2.1.1 Interger类型的参数
java
@Select("select username, `password`, age, gender, phone from user_info where id= #{id} ")
UserInfo queryById(Integer id);
观察打印的日志:
控制台上输出的sql语句是
java
select username, `password`, age, gender, phone from user_info where id= ?
我们输入的参数并没有在后面拼接,id的值是使用?
进行占位,这种SQL我们称之为"预编译SQL"。
把#{} 换成 ${}:
java
@Select("select username, `password`, age, gender, phone from user_info where id= ${id} ")
UserInfo queryById(Integer id);

使用${},参数是直接拼接在SQL语句中了。
5.2.1.1 String类型的参数
java
@Select("select username, `password`, age, gender, phone from user_info where username= #{name} ")
UserInfo queryByName(String name);

把#{} 换成${} :
java
@Select("select username, `password`, age, gender, phone from user_info where username= ${name} ")
UserInfo queryByName(String name);

可以看到,这次的参数依然是直接拼接在SQL语句中了,但是字符串作为参数时,需要添加引号''
,使用${}
不会拼接引号''
,导致程序报错。
修改代码:
java
@Select("select username, `password`, age, gender, phone from user_info where username= '${name}' ")
UserInfo queryByName(String name);

【总结】:
#{}
使用的是预编译SQL, 通过?
占位的方式,提前对SQL进行编译,然后把参数填充到SQL语句中。#{}
会根据参数类型,自动拼接引号''
${}
会直接进行字符替换,一起对SQL进行编译,如果参数为字符串,需要加上引号''
。
参数为数字类型时,也可以加上,查询结果不变,但是可能会导致索引失效,性能下降。
5.2.2 #{}和${}区别
#{} 和 ${} 的区别就是预编译SQL和即时SQL的区别
1. 性能更高
绝大多数情况下,某一条SQL语句可能会被反复调用执行,或者每次执行的时候只有个别的值不同(比如select的where子句值不同,update的set子句值不同,insert的values值不同).如果每次都需要经过上面的语法解析,SQL优化、SQL编译等,则效率就明显不行了.
预编译SQL,编译一次之后会将编译后的SQL语句缓存起来,后面再次执行这条语句时,不会再次编译(只是输入的参数不同),省去了解析优化等过程,以此来提高效率。
2. 更安全(防止SQL注入)
SQL注入:是通过操作输入的数据来修改事先定义好的SQL语句,以达到执行代码对服务器进行攻击的方法。
由于没有对用户输入进行充分检查,而SQL又是拼接而成,在用户输入参数时,在参数中添加一些SQL关键字,达到改变SQL运行结果的目的,也可以完成恶意攻击。
sql注入代码:' or 1='1
java
@Select("select username, `password`, age, gender, phone from user_info where username= '${name}' ")
List<UserInfo> queryByName(String name);
测试代码:
正常情况:
java
@Test
void queryByName() {
List<UserInfo> userInfos = userInfoMapper.queryByName("admin");
System.out.println(userInfos);
}
运行结果:
2. SQL注入场景
java
@Test
void queryByName() {
List<UserInfo> userInfos = userInfoMapper.queryByName("' or 1='1");
System.out.println(userInfos);
}
运行结果:
结果依然被查询出来了,其中参数or被当做了SQL语句的一部分。
但是,查询的数据并不是自己想要的数据,所以用于查询的字段,尽量使用#{}
预查询的方式。
SQL注入是一种非常常见的数据库攻击手段,SQL注入漏洞也是网络世界中最普遍的漏洞之一。
如果发生在用户登录的场景中,密码输入为
' or1='1
,就可能完成登录(不是一定会发生的场景,需要看登录代码如何写)
5.3 排序功能
${}会有SQL注入的风险,所以我们尽量使用#{}完成查询。
既然如此,是不是$0就没有存在的必要性了呢?
当然不是!!!
java
@Select("select id, username, age, gender, phone, delete_flag, create_time, update_time from user_info order by id ${sort} ")
List<UserInfo> queryAllUserBySort(String sort);
使用${sort} 可以实现排序查询,而使用#{sort} 就不能实现排序查询了.
注意:此处sort参数为String类型,但是SQL语句中,排序规则是不需要加引号
''
的,所以此时的${sort}
也不加引号。
把${}
改成 #{}
java
@Select("select id, username, age, gender, phone, delete_flag, create_time, update_time from user_info order by id #{sort} ")
List<UserInfo> queryAllUserBySort(String sort);
运行结果:
可以发现,当使用#{sort} 查询时,asc前后⾃动给加了引号,导致sql错误。
#{}
会根据参数类型判断是否拼接引号''
如果参数类型为String,就会加上引号。
除此之外,还有表名作为参数时,就会加上引号。
5.4 like查询
like 使⽤#{}报错
java
@Select("select id, username, age, gender, phone, delete_flag, create_time, update_time from user_info where username like '%#{key}%' ")
List<UserInfo> queryAllUserByLike(String key);
把#{}改成 {}可以正确查出来,但是 {}存在SQL注⼊的问题,所以不能直接使⽤${}.
解决办法:使⽤mysql的内置函数concat()来处理,实现代码如下:
java
@Select("select id, username, age, gender, phone, delete_flag, create_time, update_time from user_info where username like concat('%',#{key},'%')")
List<UserInfo> queryAllUserByLike(String key);
6. 数据库连接池
上面的MyBatis使用了数据库连接池技术,避免频繁的创建连接和销毁连接。
6.1 介绍
数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个。
- 没有使用数据库连接池的情况: 每次执行SQL语句,要先创建一个新的连接对象,然后执行SQL语句,SQL语句执行完,再关闭连接对象释放资源,,这种重复的创建连接,销毁连接比较消耗资源。
- **使用数据库连接池的情况:**程序启动时,会在数据库连接池中创建一定数量的Connection对象,当客户请求数据库连接池,会从数据库中获取Connection对象,然后执行SQL,SQL语句执行完,再把Connection归还给连接池。
优点:
- 减少了网络开销
- 资源重用
- 提升了系统的性能。
6.2 使用
常见的数据库连接池:
1.C3P0
2.DBCP
3.Druid
4.Hikari
目前比较流行的是:Hikari、Druid
- Hikari:SpringBoot默认使用的数据库连接池
- Druid
如果我们想把默认的数据库连接池切换为Druid数据库连接池,只需要引入相关依赖即可.
java
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-3-starter</artifactId>
<version>1.2.21</version>
</dependency>
如果SpringBoot版本为2.X,使用druid-spring-boot-starter依赖
java
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.17</version>
</dependency>
运行结果:
7.总结
7.1 MySQL开发企业规范
- 表名,字段名使用小写字母或数字,单词之间以下划线分割.尽量避免出现数字开头或者两个下划线中间只出现数字.数据库字段名的修改代价很大,所以字段名称需要慎重考虑。
MySQL在Windows下不区分大小写,但在Linux下默认是区分大小写.因此,数据库名,表名,字段名都不允许出现任何大写字母,避免节外生枝.
正例:aliyun_admin,rdc_config,level3_name
反例:AliyunAdmin,rdcConfig,level_3_name
2.表必备三字段:id,create_time,update_time
id必为主键,类型为bigintunsigned,单表时自增,步长为1
reate_time,update_time的类型均为datetime类型,create_time表示创建时间,update_time表示更新时间
有同等含义的字段即可,字段名不做强制要求
3.在表查询中,避免使用*作为查询的字段列表,标明需要哪些字段.
- 增加查询分析器解析成本
- 增减字段容易与resultMap配置不一致
- 无用字段增加网络消耗,尤其是text类型的字段
7.2 #{} 和${} 区别
- #{}:预编译处理, ${}:字符直接替换
- #{} 可以防止SQL注⼊,${}存在SQL注入的风险,查询语句中,可以使⽤#{},推荐使用#{}
- 但是⼀些场景,#{}不能完成,比如排序功能,表名,字段名作为参数时,这些情况需要使⽤${}
- 模糊查询虽然${}可以完成,但因为存在SQL注入的问题,所以通常使用mysql内置函数concat来完成