当前位置: 首页 > news >正文

【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来完成

相关文章:

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

文章目录 1. 什么是MyBatis2 MyBatis入门2.1 准备工作2.1.1 创建工程 2.2 配置数据库连接字符串2.3 写持久层代码2.4 单元测试 3. MyBatis的基础操作3.1 打印日志3.2 参数传递3.3 增&#xff08;insert&#xff09;3.3.1 返回主键 3.4 删&#xff08;delete&#xff09;3.5 改&…...

人工智能大模型备案与服务登记:监管体系的双轨逻辑与实操指南

一、核心差异&#xff1a;监管框架的分层设计 适用范围的本质分野 大模型备案&#xff1a;针对直接向公众提供生成式服务的自研或微调模型&#xff08;如 ChatGPT 类产品&#xff09;&#xff0c;要求模型具备舆论属性或社会动员能力。典型场景包括智能客服、内容创作平台等。大…...

衡石ChatBI:依托开放架构构建技术驱动的差异化数据服务

在当今数字化浪潮中&#xff0c;企业对数据价值的挖掘和利用需求日益增长。BI&#xff08;商业智能&#xff09;工具作为企业获取数据洞察的关键手段&#xff0c;其技术架构的创新与发展至关重要。衡石科技的Chat BI凭借其独特的开放架构&#xff0c;在BI领域脱颖而出&#xff…...

AOSP Android14 Launcher3——RecentsView最近任务数据加载

最近任务是Launcher中的一个重要的功能&#xff0c;显示用户最近使用的应用&#xff0c;并可以快速切换到其中的应用&#xff1b;用户可以通过底部上滑停顿进入最近任务&#xff0c;也可以在第三方应用底部上滑进最近任务。 这两种场景之前的博客也介绍过&#xff0c;本文就不…...

分析型数据库与事务型数据库?核心差异与选型指南

在当今数据驱动的业务环境中&#xff0c;选择合适的数据库架构已成为企业技术决策的关键。然而&#xff0c;面对事务型数据库和分析型数据库的选择&#xff0c;许多技术团队往往陷入困境&#xff1a;日常运行良好的系统在数据量激增时性能骤降&#xff0c;简单的查询在复杂分析…...

Linux-信号

信号是由用户&#xff0c;系统或者进程发送给目标进程的信息&#xff0c;以通知目标进程某个状态的改变或系统异常。 进程分为前台进程和后台进程&#xff0c;对于前台进程我们可以输入特殊的终端字符来给它发送信号&#xff0c;比如输入Ctrlc&#xff0c;发送一个中断信号 系…...

Oracle数据库学习之路-目录

Oracle数据库学习之路 一、安装 &#xff08;一&#xff09;数据库安装步骤详解 &#xff08;二&#xff09;开发工具安装与配置 二、基础语法篇 &#xff08;一&#xff09;基础 SQL 语法详解 &#xff08;二&#xff09;SQL 语法练习与案例分析 三、高级语法篇 &…...

用selenium4 webdriver + java 搭建并完成第一个自动化测试脚本

自动化测试任务&#xff1a; 百度搜索自己的姓名。点击第一个链接&#xff08;或者第二个&#xff09;&#xff0c;在新的页面上&#xff0c;添加断言&#xff0c;验证你的名字是否存在。 实验资料百度网盘下载路径&#xff1a; 链接: https://pan.baidu.com/s/1nVlHX_ivres…...

晨控CK-FR12与欧姆龙NX系列PLC配置EtherNet/IP通讯连接操作手册

晨控CK-FR12系列作为晨控智能工业级别RFID读写器,支持大部分工业协议如RS232、RS485、以太网。支持工业协议Modbus RTU、Modbus TCP、Profinet、EtherNet/lP、EtherCat以及自由协议TCP/IP等。 本期主题&#xff1a;围绕CK-FR12产品的EtherNet/IP通讯协议与欧姆龙PLC进行通讯配置…...

武装Burp Suite工具:RouteVulScan插件_被动扫描发现漏洞.

武装Burp Suite工具&#xff1a;RouteVulScan插件_被动扫描. RouteVulScan 是一款使用Java语言编写&#xff0c;基于Burp Suite API的插件&#xff0c;用于递归地检测潜在的脆弱路径。 该插件通过被动扫描的方式&#xff0c;对路径的各个层级进行深度分析。利用预设的正则表达…...

Selenium+Java 环境搭建

windows电脑环境搭建Chrome浏览器 1.下载 Google Chrome 网络浏览器 &#xff08;一定要下载官方的&#xff01;&#xff01;&#xff01;&#xff09; 注&#xff1a;最好下载在浏览器默认的路径 便于查找&#xff0c;而且占内存不是很大 2.查看chrome浏览器的版本 3.下载…...

网易云IP属地可以查看城市吗?深度解析与使用指南

在互联网时代&#xff0c;用户的隐私和数据安全越来越受到关注。许多社交平台和应用都会显示用户的IP属地&#xff0c;以增加透明度和真实性。网易云音乐作为国内领先的音乐平台&#xff0c;也引入了IP属地显示功能。那么&#xff0c;网易云IP属地能否精确到城市&#xff1f;这…...

Cline 之Plan和Act模式

Cline 提供了 "Plan & Act"双模式开发框架。适用在不同的场景。 一、核心模式理念 通过结构化开发流程提升AI编程效率&#xff0c;采用"先规划后执行"的核心理念。 该框架旨在帮助开发者构建更易维护、准确性更高的代码&#xff0c;同时显著缩短开发…...

【Java面试笔记:基础】7.int和Integer有什么区别?

在Java中&#xff0c;int和Integer虽然都用于表示整数值&#xff0c;但它们在本质、用法和特性上有显著差异。 1. int 和 Integer 的区别 int&#xff1a; 原始数据类型&#xff1a;int 是 Java 的 8 个原始数据类型之一&#xff0c;用于表示整数。性能优势&#xff1a;直接存…...

嘻游后台系统与机器人模块结构详解:功能逻辑 + 定制改造实战

作为“嘻游电玩三端组件”系列的最后一篇&#xff0c;本篇将全面剖析平台自带的后台控制系统与机器人行为逻辑模块&#xff0c;包括&#xff1a;用户管理、房间配置、日志系统、机器人规则编排与行为策略扩展等。通过技术视角展示其整体框架与可拓展性&#xff0c;帮助开发者更…...

Linux 网络编程:select、poll 与 epoll 深度解析 —— 从基础到高并发实战

一、IO 多路复用&#xff1a;解决并发 IO 的核心技术 在网络编程中&#xff0c;当需要同时处理大量客户端连接时&#xff0c;传统阻塞式 IO 会导致程序卡在单个操作上&#xff0c;造成资源浪费。IO 多路复用技术允许单线程监听多个文件描述符&#xff08;FD&#xff09;&#…...

在统信UOS1060上安装Fail2Ban并通过邮件发送通知

在统信UOS1060上安装Fail2Ban并通过邮件发送通知 Fail2Ban 是一个开源的防止暴力攻击的软件&#xff0c;可以有效保护您的服务器免受频繁的登录失败攻击。本文将指导您如何在统信UOS 1060上安装Fail2Ban&#xff0c;并在IP被封禁后通过邮件发送通知。 步骤 1&#xff1a;查看…...

PyTorch 分布式 DistributedDataParallel (DDP)

在之前的讨论&#xff08;或者如果你直接跳到这里&#xff09;中&#xff0c;我们了解了 torch.nn.DataParallel (DP) 作为 PyTorch 多 GPU 训练的入门选项。它简单易用&#xff0c;但其固有的主 GPU 瓶颈、GIL 限制和低效的通信模式&#xff0c;往往让它在实际应用中难以充分发…...

精益数据分析(14/126):基于数据洞察优化产品与运营

精益数据分析&#xff08;14/126&#xff09;&#xff1a;基于数据洞察优化产品与运营 在创业和数据分析的道路上&#xff0c;我们都在不断摸索前行。我一直希望能和大家共同学习、共同进步&#xff0c;所以今天继续为大家解读《精益数据分析》。这次我们将深入探讨HighScore …...

flutter 插件收集

2025年 1月10号Flutter插件手机 声音转文字 speech_to_text | Flutter package 文字转声音 flutter_tts | Flutter package 堆栈信息 stack_trace | Dart package 跳转到app设置里面 app_settings | Flutter package 轻松的动画 animations | Flutter package 日志打印 t…...

WPF特性分析

文章目录 WPF特性全面分析与性能优化指南引言WPF核心特性1. 声明式UI与XAML2. 硬件加速渲染3. 数据绑定与MVVM4. 样式与模板5. 动画系统 WPF与其他框架比较WPF vs. WinFormsWPF vs. UWPWPF vs. MAUI WPF性能优化最佳实践1. 内存管理优化2. UI虚拟化3. 使用冻结对象4. 减少视觉树…...

3.1goweb框架gin下

Gin 框架有内置的模板引擎&#xff0c;它允许你将数据和 HTML 模板结合&#xff0c;动态生成网页内容。 模板引擎基础使用 单模板文件示例 以下是一个简单的使用单个 HTML 模板文件的示例&#xff0c;展示了如何在 Gin 中渲染模板&#xff1a; package mainimport ("g…...

【全解析】深入理解 JavaScript JSON 数据解析

一、JSON 概述 1. 概念 JSON 全称为 JavaScript Object Notation&#xff0c;是一种轻量级的数据交换格式。它是 JavaScript 中用于描述对象数据的语法的扩展。不过并不限于与 JavaScript 一起使用。它采用完全独立于语言的文本格式&#xff0c;这些特性使 JSON 成为理想的数…...

影刀RPA怎么和AI结合,制作自动采集小红书爆款文章+自动用AI改写标题、内容+用AI文生图生成发文图片+自动在小红书上发布文章

环境: 影刀5.26.24 Win10专业版 doubao deepseek r1 wps 问题描述: 影刀RPA怎么和AI结合,制作自动采集小红书爆款文章+自动用AI改写标题、内容+用AI文生图生成发文图片+自动在小红书上发布文章,最后上传到飞书备份 解决方案: 1.主要流程如下: 全局变量设置(关键…...

懒人一键搭建符号执行环境V5K3

0.背景 在写完上一篇文章后发现&#xff0c;其实V5k3的组合也可以使用。Verilator v5.x 系列版本完全支持本项目的编译与仿真。 不同于 v3 版本&#xff0c;Verilator v5 引入了更严格的访问控制机制&#xff1a;要从 Verilator 生成的 C 仿真模型中访问内部信号或变量&#x…...

Java队列(Queue)核心操作与最佳实践:深入解析与面试指南

文章目录 概述一、Java队列核心实现类对比1. LinkedList2. ArrayDeque3. PriorityQueue 二、核心操作API与时间复杂度三、经典使用场景与最佳实践场景1&#xff1a;BFS层序遍历&#xff08;树/图&#xff09;场景2&#xff1a;滑动窗口最大值&#xff08;单调队列&#xff09; …...

Android 中实现图片翻转动画(卡片翻转效果)

1、简述 通过 ObjectAnimator 和 AnimatorSet 可以实现图片的翻转动画,并在翻转过程中切换图片,同时避免图片被镜像。 ObjectAnimator 是 Android 动画框架中的一个类,用于对对象的属性进行动画效果处理。它通过改变对象的属性值来实现动画效果,非常适合实现复杂的动画,如…...

智能电网第1期 | 工业交换机在变电站自动化系统中的作用

随着智能电网建设的加速推进&#xff0c;变电站自动化系统对通信网络的实时性、可靠性和安全性提出了更高要求。在变电站智能化改造过程中&#xff0c;传统网络架构面临诸多挑战&#xff1a; 多协议兼容难题&#xff1a;继电保护、测控装置等设备通信协议多样&#xff0c;难以统…...

01.浏览器自动化webdriver源码分析之启动函数

日后&#xff0c;网络爬虫也好&#xff0c;数据采集也好&#xff0c;自动化必然是主流。因此&#xff0c;笔者未雨绸缪&#xff0c;在此研究各类自动化源码&#xff0c;希望能够赶上时代&#xff0c;做出一套实用的自动化框架。 这里先研究传统的webdriver中转来进行浏览器自动…...

day35图像处理OpenCV

文章目录 一、图像预处理17 直方图均衡化17.1绘制直方图17.2直方图均衡化1. 自适应直方图均衡化2. 对比度受限的自适应直方图均衡化3. 示例 19 模板匹配 一、图像预处理 17 直方图均衡化 直方图&#xff1a;反映图像像素分布的统计图&#xff0c;横坐标就是图像像素的取值&…...

精益数据分析(15/126):解锁数据分析关键方法,驱动业务增长

精益数据分析&#xff08;15/126&#xff09;&#xff1a;解锁数据分析关键方法&#xff0c;驱动业务增长 在创业与数据分析的征程中&#xff0c;我们都在努力探寻成功的密码。今天&#xff0c;我依旧带着和大家共同进步的初衷&#xff0c;深入解读《精益数据分析》的相关内容…...

JETBRAINS USER AGREEMENT【2025.4.16】更新用户许可协议

JETBRAIN旗下的各产品更新用户许可协议&#xff1a; 大致跟漂亮国出口管制政策有关&#xff0c;以下是详细内容&#xff1a; JETBRAINS USER AGREEMENT Version 2.0, effective as of April 16, 2025 THIS IS A LEGAL AGREEMENT. BY CLICKING ON THE "I AGREE" (OR…...

【数字图像处理】立体视觉基础(1)

成像 成像过程&#xff1a;三维空间坐标到二维图像坐标的变换 相机矩阵&#xff1a;建立三维到二维的投影关系 相机的使用步骤&#xff08;模型-视图变换&#xff09;&#xff1a; &#xff08;1&#xff09;视图变换 &#xff08;2&#xff09;模型变换 &#xff08;3&…...

通过AI工具或模型创建PPT的不同方式详解,结合 Assistants API、DALL·E 3 等工具的功能对比及表格总结

以下是通过AI工具或模型创建PPT的不同方式详解&#xff0c;结合 Assistants API、DALLE 3 等工具的功能对比及表格总结&#xff1a; 1. 主要实现方式详解 1.1 基于文本生成PPT 工具示例&#xff1a;Microsoft PowerPoint Copilot、Google Workspace&#xff08;AI-powered D…...

weibo_har鸿蒙微博分享,单例二次封装,鸿蒙微博,微博登录

weibo_har鸿蒙微博分享&#xff0c;单例二次封装&#xff0c;鸿蒙微博 HarmonyOS 5.0.3 Beta2 SDK&#xff0c;原样包含OpenHarmony SDK Ohos_sdk_public 5.0.3.131 (API Version 15 Beta2) &#x1f3c6;简介 zyl/weibo_har是微博封装使用&#xff0c;支持原生core使用 &a…...

C++ Lambda表达式复习

C Lambda表达式 (C Lambda Expressions: Beginner to Advanced) Lambda表达式是C11引入的一种轻量级匿名函数语法&#xff0c;支持闭包捕获&#xff0c;可以简化代码逻辑&#xff0c;特别是在函数式编程、回调函数和STL算法场景中尤为常用。本文将从基础语法到高级应用&#x…...

鸿蒙NEXT开发权限工具类(申请授权相关)(ArkTs)

import abilityAccessCtrl, { Permissions } from ohos.abilityAccessCtrl; import { bundleManager, common, PermissionRequestResult } from kit.AbilityKit; import { BusinessError } from ohos.base; import { ToastUtil } from ./ToastUtil;/*** 权限工具类&#xff08;…...

1000 QPS 下 MySQL 性能瓶颈解决方案

当 MySQL 在 1000 QPS 时出现性能瓶颈&#xff0c;需从‌索引优化‌、‌查询逻辑调整‌、‌服务器配置调优‌、‌架构扩展‌等多维度综合解决&#xff0c;具体策略如下&#xff1a; 一、索引优化 补充缺失索引‌ 通过慢查询日志定位高频低效 SQL&#xff0c;使用 EXPLAIN 分…...

【MySQL】MySQL 表的增删改查(CRUD)—— 下篇(内含聚合查询、group by和having子句、联合查询、插入查询结果)

目录 1. 插入查询结果 2 聚合查询 &#xff08;行与行之间运算&#xff09; count 计算查询结果的行数 sum 求和 avg 求平均值 max 最大值 min 最小值 【小结】 3. group by 子句 分组 where 条件 having 条件 4. 联合查询&#xff08;多表查询&#xff09; 内连接…...

简化K8S部署流程:通过Apisix实现蓝绿发布策略详解(上)

本次主题主要目的是为大家讲解蓝绿发布&#xff0c;但是发现文档和内容太长了&#xff0c;对此将文档拆分成了两部分&#xff0c;视频拆分成了好几部分&#xff0c;这样大家刷起来没疲劳感。 第一部分《apisix argorollout 实现蓝绿发布I-使用apisix发布应用》&#xff0c;主要…...

FLV 与 MP4 格式深度剖析:结构、原理

1 FLV格式分析 1.1 定义 FLV(Flash Video)是Adobe公司推出的⼀种流媒体格式&#xff0c;由于其封装后的⾳视频⽂件体积⼩、封装简单等特点&#xff0c;⾮常适合于互联⽹上使⽤。⽬前主流的视频⽹站基本都⽀持FLV。采⽤FLV格式封装的⽂件后缀为.flv FLV封装格式是由⼀个**⽂件…...

k8s的yaml文件里的volume跟volumeMount的区别

volume 是 Pod 级别的资源&#xff0c;用于定义存储卷。它是一个独立于容器的存储资源&#xff0c;可以被一个或多个容器共享使用。volume 的定义位于 Pod 的 spec.volumes 部分。 特点 独立性&#xff1a;volume 是 Pod 的一部分&#xff0c;而不是容器的一部分。它独立于容…...

Git常用操作命令

配置 Git git config --global user.name "Your Name": 设置用户名。git config --global user.email "your_emailexample.com": 设置用户邮箱。 初始化和克隆仓库 git init: 初始化一个新的 Git 仓库。git clone [URL]: 克隆一个远程仓库到本地。 git cl…...

09.传输层协议 ——— TCP协议

文章目录 TCP协议 谈谈可靠性TCP协议格式 序号与确认序号窗口大小六个标志位 确认应答机制&#xff08;ACK&#xff09;超时重传机制连接管理机制 三次握手四次挥手 流量控制滑动窗口拥塞控制延迟应答捎带应答面向字节流粘包问题TCP异常情况TCP小结基于TCP的应用层协议 TCP协…...

NineData 与飞书深度集成,企业级数据管理审批流程全面自动化

NineData 正式推出与飞书审批系统的深度集成功能&#xff0c;企业用户在 NineData 平台发起的审批工单&#xff0c;将自动推送至审批人的飞书中&#xff0c;审批人可以直接在飞书进行审批并通过/拒绝。该功能实现跨系统协作&#xff0c;带来巨大的审批效率提升&#xff0c;为各…...

WebRTC服务器Coturn服务器中的通信协议

1、概述 作为WebRTC服务器&#xff0c;coturn通信协议主要是STUN和TURN协议 STUN&TURN协议头部都是20个字节,用 Message Type来区分不同的协议 |------2------|------2------|------------4------------|------------------------12-------------------------|-----------…...

4.19除自身以外数组的乘积

我自己的思路&#xff0c;想用双指针&#xff0c; 一个从左边left开始乘&#xff0c;一个从右边right开始乘&#xff0c;如果left,或者right遇到了目标索引i(也就是我们要跨过去的当前元素)&#xff0c;那么直接让对应的指针加一&#xff0c;当前元素不参与累积的计算&#xff…...

Anaconda3使用conda进行包管理

一、基础包管理操作 ‌安装包‌ 使用 conda install <包名> 安装指定包&#xff0c;支持多包批量安装和版本指定&#xff1a; conda install numpy # 安装单个包 conda install numpy scipy pandas # 批量安装多个包 conda install numpy1.21 # 指定版本 conda instal…...

媒体关注:联易融聚焦AI+业务,重塑供应链金融生态

近日&#xff0c;供应链金融科技龙头企业联易融科技集团&#xff08;以下简称“联易融”&#xff09;发布的公告显示&#xff0c;截至2024年末&#xff0c;公司现金储备达51亿元&#xff0c;同比上一年增加2亿元。公司称&#xff0c;公司经营性现金流保持健康&#xff0c;现金储…...

安装 Conda 环境

安装 Conda 环境&#xff1a;快速指南 什么是 Conda&#xff1f; Conda 是一个开源的跨平台包管理器和环境管理系统&#xff0c;支持 Python、R、Julia 等语言。它广泛用于数据科学和机器学习领域&#xff0c;能够轻松创建、管理和切换开发环境。 安装步骤 1. 安装 Anaconda…...