【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操作数据库的步骤:
- 准备工作(创建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/
<!--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();
}
- MyBatis的持久层接口规范一般都叫XxxMapper.
- @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执行内容,以及传递参数和执行结果:
- 查询语句
- 传递参数及类型
- 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,但是这几个属性却没有赋值。
- MyBatis会根据方法返回的结果进行赋值。
- 方法用对象UserInfo接收返回结果,MySQL查询出来数据为一条,就会自动赋值给对象。
- 方法用List< UserInfo >接收返回结果,MySQL查询出来数据为一条或多条时,也会自动赋值给list,但如果MySQL查询返回多条,但是方法使用UserInfo接收,MyBatis执行就会报错。
原因分析:
当自动映射查询结果时,MyBatis会获取结果中返回的列名并在Java类中查找相同名字的属性(忽略大小写)。这意味着如果发现了ID列和id属性,MyBatis会将列ID的值赋给id属性。
有三种解决方法:
- 起别名
- 结果映射
- 开启驼峰命名
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的开发有两种方式:
- 注解
- XML
MyBatis XML的方式需要以下两步:
- 配置数据库连接字符串和MyBatis
- 写持久层代码
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 写持久层代码
持久层代码分为两部分:
- 方法定义Interface
- 方法实现: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也没有进⾏赋值。
解决办法和注解类似:
- 起别名
- 结果映射
- 开启驼峰命名
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);
【总结】:
#{}
使用的是预编译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
@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 介绍
数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个。
- 没有使用数据库连接池的情况: 每次执行SQL语句,要先创建一个新的连接对象,然后执行SQL语句,SQL语句执行完,再关闭连接对象释放资源,,这种重复的创建连接,销毁连接比较消耗资源。
- **使用数据库连接池的情况:**程序启动时,会在数据库连接池中创建一定数量的Connection对象,当客户请求数据库连接池,会从数据库中获取Connection对象,然后执行SQL,SQL语句执行完,再把Connection归还给连接池。
优点:
- 减少了网络开销
- 资源重用
- 提升了系统的性能。
6.2 使用
常见的数据库连接池:
1.C3P0
2.DBCP
3.Druid
4.Hikari
目前比较流行的是:Hikari、Druid
- Hikari:SpringBoot默认使用的数据库连接池
- 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开发企业规范
- 表名,字段名使用小写字母或数字,单词之间以下划线分割.尽量避免出现数字开头或者两个下划线中间只出现数字.数据库字段名的修改代价很大,所以字段名称需要慎重考虑。
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来完成
相关文章:
【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 增(insert)3.3.1 返回主键 3.4 删(delete)3.5 改&…...
人工智能大模型备案与服务登记:监管体系的双轨逻辑与实操指南
一、核心差异:监管框架的分层设计 适用范围的本质分野 大模型备案:针对直接向公众提供生成式服务的自研或微调模型(如 ChatGPT 类产品),要求模型具备舆论属性或社会动员能力。典型场景包括智能客服、内容创作平台等。大…...
衡石ChatBI:依托开放架构构建技术驱动的差异化数据服务
在当今数字化浪潮中,企业对数据价值的挖掘和利用需求日益增长。BI(商业智能)工具作为企业获取数据洞察的关键手段,其技术架构的创新与发展至关重要。衡石科技的Chat BI凭借其独特的开放架构,在BI领域脱颖而出ÿ…...
AOSP Android14 Launcher3——RecentsView最近任务数据加载
最近任务是Launcher中的一个重要的功能,显示用户最近使用的应用,并可以快速切换到其中的应用;用户可以通过底部上滑停顿进入最近任务,也可以在第三方应用底部上滑进最近任务。 这两种场景之前的博客也介绍过,本文就不…...
分析型数据库与事务型数据库?核心差异与选型指南
在当今数据驱动的业务环境中,选择合适的数据库架构已成为企业技术决策的关键。然而,面对事务型数据库和分析型数据库的选择,许多技术团队往往陷入困境:日常运行良好的系统在数据量激增时性能骤降,简单的查询在复杂分析…...
Linux-信号
信号是由用户,系统或者进程发送给目标进程的信息,以通知目标进程某个状态的改变或系统异常。 进程分为前台进程和后台进程,对于前台进程我们可以输入特殊的终端字符来给它发送信号,比如输入Ctrlc,发送一个中断信号 系…...
Oracle数据库学习之路-目录
Oracle数据库学习之路 一、安装 (一)数据库安装步骤详解 (二)开发工具安装与配置 二、基础语法篇 (一)基础 SQL 语法详解 (二)SQL 语法练习与案例分析 三、高级语法篇 &…...
用selenium4 webdriver + java 搭建并完成第一个自动化测试脚本
自动化测试任务: 百度搜索自己的姓名。点击第一个链接(或者第二个),在新的页面上,添加断言,验证你的名字是否存在。 实验资料百度网盘下载路径: 链接: 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等。 本期主题:围绕CK-FR12产品的EtherNet/IP通讯协议与欧姆龙PLC进行通讯配置…...
武装Burp Suite工具:RouteVulScan插件_被动扫描发现漏洞.
武装Burp Suite工具:RouteVulScan插件_被动扫描. RouteVulScan 是一款使用Java语言编写,基于Burp Suite API的插件,用于递归地检测潜在的脆弱路径。 该插件通过被动扫描的方式,对路径的各个层级进行深度分析。利用预设的正则表达…...
Selenium+Java 环境搭建
windows电脑环境搭建Chrome浏览器 1.下载 Google Chrome 网络浏览器 (一定要下载官方的!!!) 注:最好下载在浏览器默认的路径 便于查找,而且占内存不是很大 2.查看chrome浏览器的版本 3.下载…...
网易云IP属地可以查看城市吗?深度解析与使用指南
在互联网时代,用户的隐私和数据安全越来越受到关注。许多社交平台和应用都会显示用户的IP属地,以增加透明度和真实性。网易云音乐作为国内领先的音乐平台,也引入了IP属地显示功能。那么,网易云IP属地能否精确到城市?这…...
Cline 之Plan和Act模式
Cline 提供了 "Plan & Act"双模式开发框架。适用在不同的场景。 一、核心模式理念 通过结构化开发流程提升AI编程效率,采用"先规划后执行"的核心理念。 该框架旨在帮助开发者构建更易维护、准确性更高的代码,同时显著缩短开发…...
【Java面试笔记:基础】7.int和Integer有什么区别?
在Java中,int和Integer虽然都用于表示整数值,但它们在本质、用法和特性上有显著差异。 1. int 和 Integer 的区别 int: 原始数据类型:int 是 Java 的 8 个原始数据类型之一,用于表示整数。性能优势:直接存…...
嘻游后台系统与机器人模块结构详解:功能逻辑 + 定制改造实战
作为“嘻游电玩三端组件”系列的最后一篇,本篇将全面剖析平台自带的后台控制系统与机器人行为逻辑模块,包括:用户管理、房间配置、日志系统、机器人规则编排与行为策略扩展等。通过技术视角展示其整体框架与可拓展性,帮助开发者更…...
Linux 网络编程:select、poll 与 epoll 深度解析 —— 从基础到高并发实战
一、IO 多路复用:解决并发 IO 的核心技术 在网络编程中,当需要同时处理大量客户端连接时,传统阻塞式 IO 会导致程序卡在单个操作上,造成资源浪费。IO 多路复用技术允许单线程监听多个文件描述符(FD)&#…...
在统信UOS1060上安装Fail2Ban并通过邮件发送通知
在统信UOS1060上安装Fail2Ban并通过邮件发送通知 Fail2Ban 是一个开源的防止暴力攻击的软件,可以有效保护您的服务器免受频繁的登录失败攻击。本文将指导您如何在统信UOS 1060上安装Fail2Ban,并在IP被封禁后通过邮件发送通知。 步骤 1:查看…...
PyTorch 分布式 DistributedDataParallel (DDP)
在之前的讨论(或者如果你直接跳到这里)中,我们了解了 torch.nn.DataParallel (DP) 作为 PyTorch 多 GPU 训练的入门选项。它简单易用,但其固有的主 GPU 瓶颈、GIL 限制和低效的通信模式,往往让它在实际应用中难以充分发…...
精益数据分析(14/126):基于数据洞察优化产品与运营
精益数据分析(14/126):基于数据洞察优化产品与运营 在创业和数据分析的道路上,我们都在不断摸索前行。我一直希望能和大家共同学习、共同进步,所以今天继续为大家解读《精益数据分析》。这次我们将深入探讨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 框架有内置的模板引擎,它允许你将数据和 HTML 模板结合,动态生成网页内容。 模板引擎基础使用 单模板文件示例 以下是一个简单的使用单个 HTML 模板文件的示例,展示了如何在 Gin 中渲染模板: package mainimport ("g…...
【全解析】深入理解 JavaScript JSON 数据解析
一、JSON 概述 1. 概念 JSON 全称为 JavaScript Object Notation,是一种轻量级的数据交换格式。它是 JavaScript 中用于描述对象数据的语法的扩展。不过并不限于与 JavaScript 一起使用。它采用完全独立于语言的文本格式,这些特性使 JSON 成为理想的数…...
影刀RPA怎么和AI结合,制作自动采集小红书爆款文章+自动用AI改写标题、内容+用AI文生图生成发文图片+自动在小红书上发布文章
环境: 影刀5.26.24 Win10专业版 doubao deepseek r1 wps 问题描述: 影刀RPA怎么和AI结合,制作自动采集小红书爆款文章+自动用AI改写标题、内容+用AI文生图生成发文图片+自动在小红书上发布文章,最后上传到飞书备份 解决方案: 1.主要流程如下: 全局变量设置(关键…...
懒人一键搭建符号执行环境V5K3
0.背景 在写完上一篇文章后发现,其实V5k3的组合也可以使用。Verilator v5.x 系列版本完全支持本项目的编译与仿真。 不同于 v3 版本,Verilator v5 引入了更严格的访问控制机制:要从 Verilator 生成的 C 仿真模型中访问内部信号或变量&#x…...
Java队列(Queue)核心操作与最佳实践:深入解析与面试指南
文章目录 概述一、Java队列核心实现类对比1. LinkedList2. ArrayDeque3. PriorityQueue 二、核心操作API与时间复杂度三、经典使用场景与最佳实践场景1:BFS层序遍历(树/图)场景2:滑动窗口最大值(单调队列) …...
Android 中实现图片翻转动画(卡片翻转效果)
1、简述 通过 ObjectAnimator 和 AnimatorSet 可以实现图片的翻转动画,并在翻转过程中切换图片,同时避免图片被镜像。 ObjectAnimator 是 Android 动画框架中的一个类,用于对对象的属性进行动画效果处理。它通过改变对象的属性值来实现动画效果,非常适合实现复杂的动画,如…...
智能电网第1期 | 工业交换机在变电站自动化系统中的作用
随着智能电网建设的加速推进,变电站自动化系统对通信网络的实时性、可靠性和安全性提出了更高要求。在变电站智能化改造过程中,传统网络架构面临诸多挑战: 多协议兼容难题:继电保护、测控装置等设备通信协议多样,难以统…...
01.浏览器自动化webdriver源码分析之启动函数
日后,网络爬虫也好,数据采集也好,自动化必然是主流。因此,笔者未雨绸缪,在此研究各类自动化源码,希望能够赶上时代,做出一套实用的自动化框架。 这里先研究传统的webdriver中转来进行浏览器自动…...
day35图像处理OpenCV
文章目录 一、图像预处理17 直方图均衡化17.1绘制直方图17.2直方图均衡化1. 自适应直方图均衡化2. 对比度受限的自适应直方图均衡化3. 示例 19 模板匹配 一、图像预处理 17 直方图均衡化 直方图:反映图像像素分布的统计图,横坐标就是图像像素的取值&…...
精益数据分析(15/126):解锁数据分析关键方法,驱动业务增长
精益数据分析(15/126):解锁数据分析关键方法,驱动业务增长 在创业与数据分析的征程中,我们都在努力探寻成功的密码。今天,我依旧带着和大家共同进步的初衷,深入解读《精益数据分析》的相关内容…...
JETBRAINS USER AGREEMENT【2025.4.16】更新用户许可协议
JETBRAIN旗下的各产品更新用户许可协议: 大致跟漂亮国出口管制政策有关,以下是详细内容: 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)
成像 成像过程:三维空间坐标到二维图像坐标的变换 相机矩阵:建立三维到二维的投影关系 相机的使用步骤(模型-视图变换): (1)视图变换 (2)模型变换 (3&…...
通过AI工具或模型创建PPT的不同方式详解,结合 Assistants API、DALL·E 3 等工具的功能对比及表格总结
以下是通过AI工具或模型创建PPT的不同方式详解,结合 Assistants API、DALLE 3 等工具的功能对比及表格总结: 1. 主要实现方式详解 1.1 基于文本生成PPT 工具示例:Microsoft PowerPoint Copilot、Google Workspace(AI-powered D…...
weibo_har鸿蒙微博分享,单例二次封装,鸿蒙微博,微博登录
weibo_har鸿蒙微博分享,单例二次封装,鸿蒙微博 HarmonyOS 5.0.3 Beta2 SDK,原样包含OpenHarmony SDK Ohos_sdk_public 5.0.3.131 (API Version 15 Beta2) 🏆简介 zyl/weibo_har是微博封装使用,支持原生core使用 &a…...
C++ Lambda表达式复习
C Lambda表达式 (C Lambda Expressions: Beginner to Advanced) Lambda表达式是C11引入的一种轻量级匿名函数语法,支持闭包捕获,可以简化代码逻辑,特别是在函数式编程、回调函数和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;/*** 权限工具类(…...
1000 QPS 下 MySQL 性能瓶颈解决方案
当 MySQL 在 1000 QPS 时出现性能瓶颈,需从索引优化、查询逻辑调整、服务器配置调优、架构扩展等多维度综合解决,具体策略如下: 一、索引优化 补充缺失索引 通过慢查询日志定位高频低效 SQL,使用 EXPLAIN 分…...
【MySQL】MySQL 表的增删改查(CRUD)—— 下篇(内含聚合查询、group by和having子句、联合查询、插入查询结果)
目录 1. 插入查询结果 2 聚合查询 (行与行之间运算) count 计算查询结果的行数 sum 求和 avg 求平均值 max 最大值 min 最小值 【小结】 3. group by 子句 分组 where 条件 having 条件 4. 联合查询(多表查询) 内连接…...
简化K8S部署流程:通过Apisix实现蓝绿发布策略详解(上)
本次主题主要目的是为大家讲解蓝绿发布,但是发现文档和内容太长了,对此将文档拆分成了两部分,视频拆分成了好几部分,这样大家刷起来没疲劳感。 第一部分《apisix argorollout 实现蓝绿发布I-使用apisix发布应用》,主要…...
FLV 与 MP4 格式深度剖析:结构、原理
1 FLV格式分析 1.1 定义 FLV(Flash Video)是Adobe公司推出的⼀种流媒体格式,由于其封装后的⾳视频⽂件体积⼩、封装简单等特点,⾮常适合于互联⽹上使⽤。⽬前主流的视频⽹站基本都⽀持FLV。采⽤FLV格式封装的⽂件后缀为.flv FLV封装格式是由⼀个**⽂件…...
k8s的yaml文件里的volume跟volumeMount的区别
volume 是 Pod 级别的资源,用于定义存储卷。它是一个独立于容器的存储资源,可以被一个或多个容器共享使用。volume 的定义位于 Pod 的 spec.volumes 部分。 特点 独立性:volume 是 Pod 的一部分,而不是容器的一部分。它独立于容…...
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协议格式 序号与确认序号窗口大小六个标志位 确认应答机制(ACK)超时重传机制连接管理机制 三次握手四次挥手 流量控制滑动窗口拥塞控制延迟应答捎带应答面向字节流粘包问题TCP异常情况TCP小结基于TCP的应用层协议 TCP协…...
NineData 与飞书深度集成,企业级数据管理审批流程全面自动化
NineData 正式推出与飞书审批系统的深度集成功能,企业用户在 NineData 平台发起的审批工单,将自动推送至审批人的飞书中,审批人可以直接在飞书进行审批并通过/拒绝。该功能实现跨系统协作,带来巨大的审批效率提升,为各…...
WebRTC服务器Coturn服务器中的通信协议
1、概述 作为WebRTC服务器,coturn通信协议主要是STUN和TURN协议 STUN&TURN协议头部都是20个字节,用 Message Type来区分不同的协议 |------2------|------2------|------------4------------|------------------------12-------------------------|-----------…...
4.19除自身以外数组的乘积
我自己的思路,想用双指针, 一个从左边left开始乘,一个从右边right开始乘,如果left,或者right遇到了目标索引i(也就是我们要跨过去的当前元素),那么直接让对应的指针加一,当前元素不参与累积的计算ÿ…...
Anaconda3使用conda进行包管理
一、基础包管理操作 安装包 使用 conda install <包名> 安装指定包,支持多包批量安装和版本指定: conda install numpy # 安装单个包 conda install numpy scipy pandas # 批量安装多个包 conda install numpy1.21 # 指定版本 conda instal…...
媒体关注:联易融聚焦AI+业务,重塑供应链金融生态
近日,供应链金融科技龙头企业联易融科技集团(以下简称“联易融”)发布的公告显示,截至2024年末,公司现金储备达51亿元,同比上一年增加2亿元。公司称,公司经营性现金流保持健康,现金储…...
安装 Conda 环境
安装 Conda 环境:快速指南 什么是 Conda? Conda 是一个开源的跨平台包管理器和环境管理系统,支持 Python、R、Julia 等语言。它广泛用于数据科学和机器学习领域,能够轻松创建、管理和切换开发环境。 安装步骤 1. 安装 Anaconda…...