欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 八卦 > 【JavaEE】-- MyBatis操作数据库(1)

【JavaEE】-- MyBatis操作数据库(1)

2025/4/24 11:10:24 来源:https://blog.csdn.net/2303_80341387/article/details/147386776  浏览:    关键词:【JavaEE】-- MyBatis操作数据库(1)

文章目录

  • 1. 什么是MyBatis
  • 2 MyBatis入门
    • 2.1 准备工作
      • 2.1.1 创建工程
    • 2.2 配置数据库连接字符串
    • 2.3 写持久层代码
    • 2.4 单元测试
  • 3. MyBatis的基础操作
    • 3.1 打印日志
    • 3.2 参数传递
    • 3.3 增(insert)
      • 3.3.1 返回主键
    • 3.4 删(delete)
    • 3.5 改(update)
    • 3.6 查(select)
      • 3.6.1 起别名
      • 3.6.2 结果映射
      • 3.6.3 开启驼峰命名(推荐)
  • 4. MyBatis XML配置文件
    • 4.1 配置连接字符串和MyBatis
    • 4.2 写持久层代码
      • 4.2.1 添加mapper接口
      • 4.2.2 添加UserInfoXMLMapper.xml
      • 4.2.3 单元测试
    • 4.3 增删改查操作
      • 4.3.1增(lnsert)
      • 4.3.2删(Delete)
      • 4.3.3改(Update)
      • 4.3.4查(Select)
  • 5.其他查询操作
    • 5.1多表查询
      • 5.1.1 数据查询
    • 5.2 #{}和${}
      • 5.2.1 #{}和${}使用
        • 5.2.1.1 Interger类型的参数
        • 5.2.1.1 String类型的参数
      • 5.2.2 #{}和${}区别
    • 5.3 排序功能
    • 5.4 like查询
  • 6. 数据库连接池
    • 6.1 介绍
    • 6.2 使用
  • 7.总结
    • 7.1 MySQL开发企业规范
    • 7.2 #{} 和${} 区别

1. 什么是MyBatis

  • MyBatis是一款优秀的持久层框架,用于简化JDBC的开发。
  • 持久层:指的就是持久化操作的层,通常指数据访问层(dao),用来操作数据库的。
  • 官网:MyBatis中文网
    在这里插入图片描述

2 MyBatis入门

2.1 准备工作

Mybatis操作数据库的步骤:

  1. 准备工作(创建springboot工程、数据库表准备、实体类)
  2. 引入Mybatis的相关依赖,配置Mybatis(数据库连接信息)
  3. 编写SQL语句(注解/XML)
  4. 测试

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/
在这里插入图片描述

 <!--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文件,配置内容如下:

# 数据库连接配置
spring:datasource:url: jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=trueusername: rootpassword: 111111driver-class-name: com.mysql.cj.jdbc.Driver

【注意】:如果使用MySQL是5.x之前的使用的是"com.mysql.jdbc.Driver",如果是大于5.x使用的是“com.mysql.cj.jdbc.Driver”.

如果是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

2.3 写持久层代码

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();
}
  1. MyBatis的持久层接口规范一般都叫XxxMapper.
  2. @Mapper注解:表示的是MyBatis中的Mapper接口。

2.1. 程序运行时, 框架会自动生成接口的实现类对象(代理对象),并交给Spring的IOC容器管理。
2.2. @Select注解:代表的就是select查询,也就是注解对应方法的具体实现内容。

2.4 单元测试

在对应的mapper文件中:鼠标右键–>generate—>test
在这里插入图片描述

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 {@Autowiredprivate UserInfoMapper userInfoMapper;@Testvoid selectAll() {System.out.println(userInfoMapper.selectAll());}
}
  • 测试类上添加@SpringBootTest注解,该测试类在运行时,就会自动加载Spring的运行环境。
  • 通过@Autowired这个注解,注入我们所要测试的mapper类,就可以开始进行测试了。

运行结果:
在这里插入图片描述

3. MyBatis的基础操作

3.1 打印日志

在Mybatis当中我们可以借助日志,查看到sql语句的执行、执行传递的参数以及执行结果在配置文件中进行配置即可:

application.yml文件:

mybatis:configuration: # 配置打印MyBatis⽇志log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

重新运行程序,可以看到SQL执行内容,以及传递参数和执行结果:
在这里插入图片描述

  1. 查询语句
  2. 传递参数及类型
  3. SQL执行类型

3.2 参数传递

需求:查找id=4的用户,对应的SQL就是:select*fromuser_infowhereid=4

@Select("select username, `password`, age, gender, phone from user_info where id= 4 ")
UserInfo queryById();

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

解决方案:在queryByld方法中添加一个参数(id),将方法中的参数,传给SQL语句.使用#{}的方式获取方法中的参数。

@Select("select username, `password`, age, gender, phone from user_info where id = #{id} ")
UserInfo queryById();

如果mapper接口方法形参只有一个普通类型的参数,#}里面的属性名可以随便写,如:#{id]、#{value]。建议和参数名保持一致.

添加测试用例

 @Testvoid queryById() {UserInfo userInfo = userInfoMapper.queryById(4);System.out.println(userInfo);}

运行结果:
在这里插入图片描述
也可以通过@Param,设置参数的别名,如果使用@Param设置别名,#.里面的属性名必须和@Param设置的一样.

@Select("select username, `password`, age, gender, phone from user_info where id = #{userid} ")
UserInfo queryById(@Param("userid") Integer id);

3.3 增(insert)

SQL 语句:

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

把SQL中的常量替换为动态的参数,Mapper接口:

 @Insert("insert into user_info (username, `password`, age, gender, phone) values (#{username},#{password},#{age},#{gender},#{phone})")Integer insert(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);
}

运行后,观察数据库执行结果

如果设置了@Param属性,#{...}需要使用参数.属性的方式来获取。

@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的注解。

 @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)

测试数据:

 @Testvoid 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语句:

delete from user_info where id=6

把SQL中的常量替换为动态的参数,Mapper接口:

 @Delete("delete from user_info where id = #{id}")void delete(Integer id);

3.5 改(update)

SQL语句:

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);

3.6 查(select)

我们在上面查询时发现,有几个字段时没有赋值的,只有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,但是这几个属性却没有赋值。

  1. MyBatis会根据方法返回的结果进行赋值。
  2. 方法用对象UserInfo接收返回结果,MySQL查询出来数据为一条,就会自动赋值给对象。
  3. 方法用List< UserInfo >接收返回结果,MySQL查询出来数据为一条或多条时,也会自动赋值给list,但如果MySQL查询返回多条,但是方法使用UserInfo接收,MyBatis执行就会报错。

原因分析:
当自动映射查询结果时,MyBatis会获取结果中返回的列名并在Java类中查找相同名字的属性(忽略大小写)。这意味着如果发现了ID列和id属性,MyBatis会将列ID的值赋给id属性。

在这里插入图片描述

有三种解决方法:

  1. 起别名
  2. 结果映射
  3. 开启驼峰命名

3.6.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")
public List<UserInfo> queryAllUser();

【注意】:SQL语句太长时,使用+号进行字符串拼接。

3.6.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> queryAllUser();

如果其他SQL,也希望可以复用这个映射关系,可以给这个Result定义一个名称。

@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。

mybatis:configuration: map-underscore-to-camel-case: true #配置驼峰⾃动转换

驼峰命名规则:abc_xyz===>abcXyz

  • 表中字段名:abc_xyz
  • 类中属性名:abcXyz

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的开发有两种方式:

  1. 注解
  2. XML

MyBatis XML的方式需要以下两步:

  1. 配置数据库连接字符串和MyBatis
  2. 写持久层代码

4.1 配置连接字符串和MyBatis

application.yml文件:

spring:# 数据库连接配置datasource:url: jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=trueusername: rootpassword: 111111driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:mapper-locations: classpath:mapper/**Mapper.xml

4.2 写持久层代码

持久层代码分为两部分:

  1. 方法定义Interface
  2. 方法实现:XXX.xml

在这里插入图片描述

4.2.1 添加mapper接口

数据持久层的接口定义:

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格式:

<?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文件中的配置。
在这里插入图片描述
查询所有用户的具体实现:

 <?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 单元测试

@SpringBootTest
class UserInfoMapperTest {@Autowiredprivate UserInfoMapper userInfoMapper;@Testvoid queryAllUser() {List<UserInfo> userInfoList = userInfoMapper.queryAllUser();System.out.println(userInfoList);}
}

运行结果如下:
在这里插入图片描述

4.3 增删改查操作

4.3.1增(lnsert)

UserInfoMapper接口:

 Integer insertUser(UserInfo userInfo);

UserInfoMapper.xml实现:

 <insert id="insertUser">insert into userinfo (username, `password`, age, gender, phone) values (#{username}, #{password}, #{age},#{gender},#{phone})</insert>

如果使用@Param设置参数名称的话,使用方法和注解类似:

UserInfoMapper接口:

Integer insertUser(@Param("userInfo") UserInfo userInfo);

UserInfoMapper.xml实现:

<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属性:

<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接口:

Integer deleteUser(Integer id);

UserInfoMapper.xml实现:

<delete id="deleteUser">delete from user_info where id = #{id}
</delete>

4.3.3改(Update)

UserInfoMapper接口:

Integer updateUser(UserInfo userInfo);

UserInfoMapper.xml实现:

<update id="updateUser">update user_info set username=#{username} where id=#{id}
</update>

4.3.4查(Select)

同样的,使用XML的方式进行查询,也存在数据封装的问题我们把SQL语句进行简单修改,查询更多的字段内容。

<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也没有进⾏赋值。

解决办法和注解类似:

  1. 起别名
  2. 结果映射
  3. 开启驼峰命名

xml使用结果映射:

Mapper.xml

<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:

SELECTta.id,ta.title,ta.content,ta.uid,tb.username,tb.age,tb.gender 
FROMarticleinfo taLEFT JOIN user_info tb ON ta.uid = tb.id 
WHEREta.id =1

接口定义:

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类型的参数
@Select("select username, `password`, age, gender, phone from user_info where id= #{id} ")
UserInfo queryById(Integer id);

观察打印的日志:
在这里插入图片描述
控制台上输出的sql语句是

 select username, `password`, age, gender, phone from user_info where id= ?

我们输入的参数并没有在后面拼接,id的值是使用进行占位,这种SQL我们称之为“预编译SQL”。

把#{} 换成 ${}:

@Select("select username, `password`, age, gender, phone from user_info where id= ${id} ")
UserInfo queryById(Integer id);

在这里插入图片描述
使用${},参数是直接拼接在SQL语句中了。

5.2.1.1 String类型的参数
@Select("select username, `password`, age, gender, phone from user_info where username= #{name} ")
UserInfo queryByName(String name);

在这里插入图片描述

把#{} 换成${} :

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

在这里插入图片描述
可以看到,这次的参数依然是直接拼接在SQL语句中了,但是字符串作为参数时,需要添加引号'',使用${} 不会拼接引号'',导致程序报错。

修改代码:

@Select("select username, `password`, age, gender, phone from user_info where username= '${name}' ")
UserInfo queryByName(String name);

在这里插入图片描述

【总结】:

  1. #{}使用的是预编译SQL, 通过占位的方式,提前对SQL进行编译,然后把参数填充到SQL语句中。#{}会根据参数类型,自动拼接引号''
  2. ${} 会直接进行字符替换,一起对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

@Select("select username, `password`, age, gender, phone from user_info where username= '${name}' ")
List<UserInfo> queryByName(String name);

测试代码:
正常情况:

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

运行结果:
在这里插入图片描述

2. SQL注入场景

@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就没有存在的必要性了呢?
当然不是!!!

 @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}也不加引号。

${} 改成 #{}

@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 使⽤#{}报错

@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()来处理,实现代码如下:

@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 介绍

数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个。
在这里插入图片描述

  1. 没有使用数据库连接池的情况: 每次执行SQL语句,要先创建一个新的连接对象,然后执行SQL语句,SQL语句执行完,再关闭连接对象释放资源,,这种重复的创建连接,销毁连接比较消耗资源。
  2. **使用数据库连接池的情况:**程序启动时,会在数据库连接池中创建一定数量的Connection对象,当客户请求数据库连接池,会从数据库中获取Connection对象,然后执行SQL,SQL语句执行完,再把Connection归还给连接池。

优点:

  1. 减少了网络开销
  2. 资源重用
  3. 提升了系统的性能。

6.2 使用

常见的数据库连接池:
1.C3P0
2.DBCP
3.Druid
4.Hikari

目前比较流行的是:Hikari、Druid

  1. Hikari:SpringBoot默认使用的数据库连接池
    在这里插入图片描述
  2. Druid
    如果我们想把默认的数据库连接池切换为Druid数据库连接池,只需要引入相关依赖即可.
<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依赖

<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.17</version>
</dependency>

运行结果:
在这里插入图片描述

7.总结

7.1 MySQL开发企业规范

  1. 表名,字段名使用小写字母或数字,单词之间以下划线分割.尽量避免出现数字开头或者两个下划线中间只出现数字.数据库字段名的修改代价很大,所以字段名称需要慎重考虑。

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.在表查询中,避免使用*作为查询的字段列表,标明需要哪些字段.

  1. 增加查询分析器解析成本
  2. 增减字段容易与resultMap配置不一致
  3. 无用字段增加网络消耗,尤其是text类型的字段

7.2 #{} 和${} 区别

  1. #{}:预编译处理, ${}:字符直接替换
  2. #{} 可以防止SQL注⼊,${}存在SQL注入的风险,查询语句中,可以使⽤#{},推荐使用#{}
  3. 但是⼀些场景,#{}不能完成,比如排序功能,表名,字段名作为参数时,这些情况需要使⽤${}
  4. 模糊查询虽然${}可以完成,但因为存在SQL注入的问题,所以通常使用mysql内置函数concat来完成

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词