-- 创建数据库create database if not exists library default character set utf8mb4;-- 使⽤数据库use library;-- 创建表create table if not exists soft_bookrack (book_name varchar(32) NOT NULL,book_author varchar(32) NOT NULL,book_isbn varchar(32) NOT NULL primary key);
以下是 JDBC 操作的具体实现代码:
import javax.sql.DataSource;
import java.awt.print.Book;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;public class JDBC_DEMO {DataSource dataSource;public JDBC_DEMO(DataSource dataSource){dataSource = dataSource;}public void addBook() {Connection connection = null;PreparedStatement stmt = null;try {//获取数据库连接connection = dataSource.getConnection();//创建语句stmt = connection.prepareStatement("insert into soft_bookrack (book_name, book_author, book_isbn) values (?,?,?);");
//参数绑定stmt.setString(1, "Spring in Action");stmt.setString(2, "Craig Walls");stmt.setString(3, "9787115417305");//执⾏语句stmt.execute();
} catch (SQLException e) {e.printStackTrace();}}public void updateBook() {Connection connection = null;PreparedStatement stmt = null;try {//获取数据库连接connection = dataSource.getConnection();//创建语句stmt = connection.prepareStatement("update soft_bookrack set book_author=? where book_isbn=?;");//参数绑定stmt.setString(1, "张卫滨");stmt.setString(2, "9787115417305");//执⾏语句stmt.execute();} catch (SQLException e) {//处理异常信息} finally {//清理资源try {if (stmt != null) {stmt.close();}if (connection != null) {connection.close();}} catch (SQLException e) {
}}}public void queryBook() {Connection connection = null;PreparedStatement stmt = null;ResultSet rs = null;Book book = null;try {//获取数据库连接connection = dataSource.getConnection();//创建语句stmt = connection.prepareStatement(
// "select book_name, book_author, book_isbn from
// soft_bookrack where book_isbn =?"//);//参数绑定stmt.setString(1, "9787115417305");//执⾏语句rs = stmt.executeQuery();if (rs.next()) {book = new Book();book.setName(rs.getString("book_name"));book.setAuthor(rs.getString("book_author"));book.setIsbn(rs.getString("book_isbn"));}System.out.println(book);} catch (SQLException e) {//处理异常信息} finally {//清理资源try {if (rs != null) {rs.close();}if (stmt != null) {stmt.close();}if (connection != null) {connection.close();}} catch (SQLException e) {
}}}
}
1. 什么是MyBatis?

2.MyBatis⼊⻔
2.1 准备⼯作

版本会随着SpringBoot 版本发⽣变化, ⽆需关注
<!--Mybatis 依赖包 -->< dependency >< groupId >org.mybatis.spring.boot</ groupId >< artifactId >mybatis-spring-boot-starter</ artifactId >< version >2.3.1</ version ></ dependency ><!--mysql驱动包-->
< dependency >< groupId >com.mysql</ groupId >< artifactId >mysql-connector-j</ artifactId >< scope >runtime</ scope ></ dependency >
2.1.2 数据准备
create database mybatis_test;
use mybatis_test;创建表[⽤⼾表]
CREATE TABLE `userinfo` (`id` INT ( 11 ) NOT NULL AUTO_INCREMENT,`username` VARCHAR ( 127 ) NOT NULL,`password` VARCHAR ( 127 ) NOT NULL,`age` TINYINT ( 4 ) NOT NULL,`gender` TINYINT ( 4 ) DEFAULT '0' COMMENT '1-男 2-⼥ 0-默认',`phone` VARCHAR ( 15 ) DEFAULT NULL,`delete_flag` TINYINT ( 4 ) DEFAULT 0 COMMENT '0-正常, 1-删除',`create_time` DATETIME DEFAULT now(),`update_time` DATETIME DEFAULT now(),PRIMARY KEY ( `id` )
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4;添加⽤⼾信息
INSERT INTO mybatis_test.userinfo ( username, `password`, age, gender, phone )
VALUES ( 'admin', 'admin', 18, 1, '18612340001' );
INSERT INTO mybatis_test.userinfo ( username, `password`, age, gender, phone )
VALUES ( 'zhangsan', 'zhangsan', 18, 1, '18612340002' );
INSERT INTO mybatis_test.userinfo ( username, `password`, age, gender, phone )
VALUES ( 'lisi', 'lisi', 18, 1, '18612340003' );
INSERT INTO mybatis_test.userinfo ( username, `password`, age, gender, phone )
VALUES ( 'wangwu', 'wangwu', 18, 1, '18612340004' );
实体类的属性名与表中的字段名⼀⼀对应
package com.example.demo;import lombok.Data;import java.util.Date;@Data
public class UserInfo {private Integer id;private String username;private String password;private Integer age;private Integer gender;private String phone;private Integer deleteFlag;private Date createTime;private Date updateTime;
}
2.2 配置数据库连接字符串
Mybatis中要连接数据库,需要数据库相关参数配置
• MySQL驱动类• 登录名• 密码• 数据库连接字符串
# 数据库连接配置spring:datasource:url: jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncoding=utf8&useSSL=falseusername: rootpassword: rootdriver-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# 数据库连接的 urlspring.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 写持久层代码

package mapper;import com.example.demo.UserInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;import java.util.List;@Mapper
public interface UserInfoMapper {@Select("select username, `password`, age, gender, phone from userinfo")public List<UserInfo> queryAllUser();
}
Mybatis的持久层接⼝规范⼀般都叫 XxxMapper@Mapper注解:表⽰是MyBatis中的Mapper接⼝• 程序运⾏时, 框架会⾃动⽣成接⼝的实现类对象(代理对象),并给交Spring的IOC容器管理• @Select注解:代表的就是select查询,也就是注解对应⽅法的具体实现内容
2.4 单元测试
package com.example.demo;import mapper.UserInfoMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import java.util.List;@SpringBootTest
class SpringMvcApplicationTests {
@AutowiredUserInfoMapper userInfoMapper;@Testvoid contextLoads() {List<UserInfo> userInfoList = userInfoMapper.queryAllUser();System.out.println(userInfoList);}}


package com.example.demo.mapper;import com.example.demo.UserInfo;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import java.util.List;import static org.junit.jupiter.api.Assertions.*;@SpringBootTest
class UserInfoMapperTest {@AutowiredUserInfoMapper userInfoMapper;@Testvoid queryAllUser() {List<UserInfo> userInfoList = userInfoMapper.queryAllUser();System.out.println(userInfoList);}
}

3.MyBatis的基础操作
3.1 打印⽇志
mybatis:configuration: # 配置打印 MyBatis ⽇志log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
如果是application.properties, 配置内容如下:
# 指定 mybatis 输出⽇志的位置 , 输出控制台mybatis.configuration.log-impl = org.apache.ibatis.logging.stdout.StdOutImpl
注意: 后续配置项, 默认只提供⼀种, 请⾃⾏进⾏配置项转换.
3.2 参数传递
@Select("select username, `password`, age, gender, phone from userinfo whereid= 4 ")UserInfo queryById ();
@Select("select username, `password`, age, gender, phone from userinfo whereid= #{id} ")UserInfo queryById (Integer id);
如果mapper接⼝⽅法形参只有⼀个普通类型的参数,#{…} ⾥⾯的属性名可以随便写,如:#{id}、#{value}。建议和参数名保持⼀致
@Select("select username, `password`, age, gender, phone from userinfo whereid= #{userid} ")UserInfo queryById ( @Param("userid") Integer id);
3.3 增(Insert)
insert into userinfo (username, `password`, age, gender, phone) values("zhaoliu","zhaoliu", 19 , 1 ,"18700001234")
@Insert("insert into userinfo (username, `password`, age, gender, phone)values (#{username},#{password},#{age},#{gender},#{phone})")Integer insert (UserInfo userInfo);
@Testvoid 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 userinfo (username, `password`, age, gender, phone)values (#{userinfo.username},#{userinfo.password},#{userinfo.age},#{userinfo.gender},#{userinfo.phone})")Integer insert ( @Param("userinfo") UserInfo userInfo);
返回主键
⽐如订单系统当我们下完订单之后, 需要通知物流系统, 库存系统, 结算系统等, 这时候就需要拿到订单ID
如果想要拿到⾃增id, 需要在Mapper接⼝的⽅法上添加⼀个Options的注解.
@Options(useGeneratedKeys = true, keyProperty = "id")@Insert("insert into userinfo (username, age, gender, phone) values (#{userinfo.username},#{userinfo.age},#{userinfo.gender},#{userinfo.phone})")Integer insert ( @Param("userinfo") UserInfo userInfo);
@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());
}
3.4 删(Delete)
SQL 语句:
delete from userinfo where id= 6
@Delete("delete from userinfo where id = #{id}")
void delete(Integer id);
测试用例:
@Testvoid delete() {userInfoMapper.delete(1);}
3.5 改(Update)
SQL 语句:
update userinfo set username="zhaoliu" where id=5
@Update("update userinfo set username=#{username} where id=#{id}")void update (UserInfo userInfo);
@Testvoid update() {UserInfo userInfo = new UserInfo();userInfo.setId(2);userInfo.setUsername("lyj");
userInfoMapper.update(userInfo);}
3.6 查(Select)
@Select("select id, username, `password`, age, gender, phone, delete_flag,create_time, update_time from userinfo")List<UserInfo> queryAllUser ();
查询结果:
MyBatis 会根据⽅法的返回结果进⾏赋值.⽅法⽤对象 UserInfo接收返回结果, MySQL 查询出来数据为⼀条, 就会⾃动赋值给对象.⽅法⽤List<UserInfo>接收返回结果, MySQL 查询出来数据为⼀条或多条时, 也会⾃动赋值给List.但如果MySQL 查询返回多条, 但是⽅法使⽤UserInfo接收, MyBatis执⾏就会报错.
原因分析:

3.6.1 起别名
@Select("select id, username, `password`, age, gender, phone, delete_flag asdeleteFlag, " +"create_time as createTime, update_time as updateTime from userinfo")public List<UserInfo> queryAllUser ();
SQL语句太⻓时, 使⽤加号 + 进⾏字符串拼接
3.6.2 结果映射
@Select("select id, username, `password`, age, gender, phone, delete_flag,create_time, update_time from userinfo")@Results({@Result(column = "delete_flag",property = "deleteFlag"),@Result(column = "create_time",property = "createTime"),@Result(column = "update_time",property = "updateTime")})List<UserInfo> queryAllUser ();
如果其他SQL, 也希望可以复⽤这个映射关系, 可以给这个Results定义⼀个名称
@Select("select id, username, `password`, age, gender, phone, delete_flag,create_time, update_time from userinfo")@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 userinfo where id= #{userid} ")@ResultMap(value = "resultMap")UserInfo queryById ( @Param("userid") Integer id);
使⽤ id 属性给该 Results 定义别名, 使⽤ @ResultMap 注解来复⽤其他定义的 ResultMap.
3.6.3 开启驼峰命名(推荐)
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 asdeleteFlag, " +"create_time as createTime, update_time as updateTime from userinfo")public List<UserInfo> queryAllUser ();
字段全部进⾏正确赋值.
4. MyBatis XML配置⽂件
使⽤Mybatis的注解⽅式,主要是来完成⼀些简单的增删改查功能. 如果需要实现复杂的SQL功能,建议使⽤XML来配置映射语句,也就是将SQL语句写在XML配置⽂件中.
MyBatis XML的⽅式需要以下两步:
4.1 配置连接字符串和MyBatis
# 数据库连接配置spring:datasource:url: jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncoding=utf8&useSSL=falseusername: rootpassword: rootdriver-class-name: com.mysql.cj.jdbc.Driver# 配置 mybatis xml 的⽂件路径,在 resources/mapper 创建所有表的 xml ⽂件mybatis:mapper-locations: classpath:mapper/**Mapper.xml
如果是application.properties⽂件, 配置内容如下:
# 驱动类名称spring.datasource.driver-class-name = com.mysql.cj.jdbc.Driver# 数据库连接的 urlspring.datasource.url = jdbc:mysql://127.0.0.1:3306/mybatis_test?characterEncoding=utf8&useSSL=false# 连接数据库的⽤⼾名spring.datasource.username = root# 连接数据库的密码spring.datasource.password = root# 配置 mybatis xml 的⽂件路径,在 resources/mapper 创建所有表的 xml ⽂件mybatis.mapper-locations = classpath:mapper/**Mapper.xml
4.2 写持久层代码

4.2.1 添加 mapper 接⼝
import com.example.demo.model.UserInfo;import org.apache.ibatis.annotations.Mapper;import java.util.List;@Mapperpublic interface UserInfoXMlMapper {List<UserInfo> queryAllUser ();}
4.2.2 添加 UserInfoXMLMapper.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.UserInfoMapper"><select id="queryAllUser" resultType="com.example.demo.model.UserInfo">select username,`password`, age, gender, phone from userinfo</select> </mapper>

4.3 增删改查操作
4.3.1 增(Insert)
Integer insertUser (UserInfo userInfo);
< insert id = "insertUser" >insert into userinfo (username, `password`, age, gender, phone) values (#{username}, #{password}, #{age},#{gender},#{phone})</ insert >
Integer insertUser ( @Param("userinfo") UserInfo userInfo);
< insert id = "insertUser" >insert into userinfo (username, `password`, age, gender, phone) values(#{userinfo.username},#{userinfo.password},#{userinfo.age},#{userinfo.gender},#{userinfo.phone})</ insert >
< insert id = "insertUser" useGeneratedKeys = "true" keyProperty = "id" >insert into userinfo (username, `password`, age, gender, phone) values(#{userinfo.username},#{userinfo.password},#{userinfo.age},#{userinfo.gender},#{userinfo.phone})</ insert >
4.3.2 删(Delete)
Integer deleteUser (Integer id);
< delete id = "deleteUser" >delete from userinfo where id = #{id}</ delete >
4.3.3 改(Update)
Integer updateUser (UserInfo userInfo);
< update id = "updateUser" >update userinfo set username=#{username} where id=#{id}</ update >
4.3.4 查(Select)
< select id = "queryAllUser" resultType = "com.example.demo.model.UserInfo" >select id, username,`password`, age, gender, phone, delete_flag,create_time, update_time from userinfo</ select >
<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="queryAllUser2" resultMap="BaseMap">select id, username,`password`, age, gender, phone, delete_flag,create_time, update_time from userinfo </select>


5.2 #{} 和 ${}
5.2.1 #{} 和${} 使⽤
@Select("select username, `password`, age, gender, phone from userinfo whereid= #{id} ")UserInfo queryById (Integer id);
观察我们打印的日志:
发现我们输出的SQL语句:
select username, `password`, age, gender, phone from userinfo where id= ?
我们输⼊的参数并没有在后⾯拼接,id的值是使⽤ ? 进⾏占位. 这种SQL 我们称之为"预编译SQL"
@Select("select username, `password`, age, gender, phone from userinfo whereid= ${id} ")UserInfo queryById (Integer id);
可以看到, 这次的参数是直接拼接在SQL语句中了.
@Select("select username, `password`, age, gender, phone from userinfo whereusername= #{name} ")UserInfo queryByName (String name);
观察我们打印的⽇志, 结果正常返回
我们把 #{} 改成 ${} 再观察打印的⽇志:
@Select("select username, `password`, age, gender, phone from userinfo whereusername= ${name} ")UserInfo queryByName (String name);
@Select("select username, `password`, age, gender, phone from userinfo whereusername= '${name}' ")UserInfo queryByName (String name);
再次运⾏, 结果正常返回:
参数为数字类型时, 也可以加上, 查询结果不变, 但是可能会导致索引失效, 性能下降
5.2.2 #{} 和 ${}区别
简单回顾:当客⼾发送⼀条SQL语句给服务器后, ⼤致流程如下:1. 解析语法和语义, 校验SQL语句是否正确2. 优化SQL语句, 制定执⾏计划3. 执⾏并返回结果⼀条 SQL如果⾛上述流程处理, 我们称之为 Immediate Statements(即时 SQL)

由于没有对⽤⼾输⼊进⾏充分检查,⽽SQL⼜是拼接⽽成,在⽤⼾输⼊参数时,在参数中添加⼀些SQL关键字,达到改变SQL运⾏结果的⽬的,也可以完成恶意攻击。
sql 注⼊代码: ' or 1='1
@Select("select username, `password`, age, gender, phone from userinfo whereusername= '${name}' ")List<UserInfo> queryByName (String name);
@Testvoid queryByName () {List<UserInfo> userInfos = userInfoMapper.queryByName( "admin" );System.out.println(userInfos);}
结果运⾏正常
SQL注⼊场景:
@Testvoid queryByName () {List<UserInfo> userInfos = userInfoMapper.queryByName( "' or 1='1" );System.out.println(userInfos);}
结果依然被正确查询出来了, 其中参数 or被当做了SQL语句的⼀部分.
可以看出来,查询的数据并不是⾃⼰想要的数据.所以⽤于查询的字段,尽量使⽤ #{} 预查询的⽅式.
SQL注⼊是⼀种⾮常常⻅的数据库攻击⼿段,SQL注⼊漏洞也是⽹络世界中最普遍的漏洞之⼀. 如果发⽣在⽤⼾登录的场景中,密码输⼊为 需要看登录代码如何写) 控制层:UserController
' or 1='1 ,就可能完成登录(不是⼀定会发⽣的场景,需要看登录代码如何写)
控制层:UserController
import com.example.demo.model.UserInfo;import com.example.demo.service.UserService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;@RestControllerpublic class UserController {@Autowiredprivate UserService userService;@RequestMapping("/login")public boolean login(String name, String password) {UserInfo userInfo = userService.queryUserByPassword(name, password);if (userInfo != null) {return true;}return false;}}
业务层:UserService
import com.example.demo.mapper.UserInfoMapper;import com.example.demo.model.UserInfo;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import java.util.List;@Servicepublic class UserService {@Autowiredprivate UserInfoMapper userInfoMapper;public UserInfo queryUserByPassword(String name, String password) {List<UserInfo> userInfos = userInfoMapper.queryUserByPassword(name,
password);if (userInfos != null && userInfos.size() > 0) {return userInfos.get(0);}return null;}}
数据层:UserInfoMapper
import com.example.demo.model.UserInfo;import org.apache.ibatis.annotations.*;
import java.util.List;@Mapperpublic interface UserInfoMapper {@Select("select username, `password`, age, gender, phone from userinfo
where username= '${name}' and password='${password}' ")List<UserInfo> queryUserByPassword(String name, String password);}
启动服务,访问:http://127.0.0.1:8080/login?name=admin&password=admin
程序正常运⾏
接下来访问SQL注⼊的代码: password设置为 ' or 1='1
password设置为 ' or 1='1 http://127.0.0.1:8080/loginname=admin&password=%27%20or%201=%271
5.3 排序功能
从上⾯的例⼦中,可以得出结论:${}会有SQL注⼊的⻛险,所以我们尽量使⽤#{}完成查询 既然如此,是不是${}就没有存在的必要性了呢? 当然不是.
接下来我们看下${}的使⽤场景
Mapper实现
@Select("select id, username, age, gender, phone, delete_flag, create_time, update_time " + "from userinfo order by id ${sort} ") List queryAllUserBySort(String sort);
使⽤ ${sort} 可以实现排序查询,⽽使⽤ #{sort} 就不能实现排序查询了.
注意:此处sort参数为String类型,但是SQL语句中,排序规则是不需要加引号 ${sort} 也不加引号.
我们把 ${} 改成 #{}
@Select("select id, username, age, gender, phone, delete_flag, create_time, update_time " + "from userinfo order by id #{sort} ") List queryAllUserBySort(String sort);
运⾏结果:
可以发现,当使⽤ #{sort} 查询时,asc前后⾃动给加了引号,导致sql错误.
#{} 会根据参数类型判断是否拼接引号 ''
除此之外,还有表名作为参数时,也只能使⽤ ${}.
5.4 like查询
like 使⽤#{}报错
@Select("select id, username, age, gender, phone, delete_flag, create_time, update_time " + "from userinfo where username like '%#{key}%' ") List queryAllUserByLike(String key);
把#{}改成${}可以正确查出来,但是${}存在SQL注⼊的问题,所以不能直接使⽤${}. 解决办法:使⽤mysql的内置函数concat()来处理,实现代码如下:
@Select("select id, username, age, gender, phone, delete_flag, create_time, update_time " + "from userinfo where username like concat('%',#{key},'%')") List queryAllUserByLike(String key);
6. 数据库连接池
在上⾯Mybatis的讲解中,我们使⽤了数据库连接池技术,避免频繁的创建连接,销毁连接.
6.1介绍
数据库连接池负责分配、管理和释放数据库连接,它允许应⽤程序重复使⽤⼀个现有的数据库连接, ⽽不是再重新建⽴⼀个.
没有使⽤数据库连接池的情况:每次执⾏SQL语句,要先创建⼀个新的连接对象,然后执⾏SQL语句,SQL 语句执⾏完,再关闭连接对象释放资源.这种重复的创建连接,销毁连接⽐较消耗资源
使⽤数据库连接池的情况:程序启动时,会在数据库连接池中创建⼀定数量的Connection对象,当客⼾ 请求数据库连接池,会从数据库连接池中获取Connection对象,然后执⾏SQL,SQL语句执⾏完,再把 Connection归还给连接池
优点:
1. 减少了⽹络开销
2. 资源重⽤
3. 提升了系统的性能
6.2 使⽤
常⻅的数据库连接池:
• C3P0
• DBCP
• Druid
• Hikari
⽬前⽐较流⾏的是Hikari,Druid
1. Hikari : SpringBoot默认使⽤的数据库连接池
Hikari 是⽇语"光"的意思(ひかり),Hikari也是以追求性能极致为⽬标
2. Druid
如果我们想把默认的数据库连接池切换为Druid数据库连接池,只需要引⼊相关依赖即可.
<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.17</version> </dependency>
参考官⽅地址:https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter • Druid连接池是阿⾥巴巴开源的数据库连接池项⽬ • 功能强⼤,性能优秀,是Java语⾔最好的数据库连接池之⼀ • 学习⽂档:https://github.com/alibaba/druid/wiki/%E9%A6%96%E9%A1%B5
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 create_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来完成