MyBatis 操作数据库

本节目标

  1. 使用MyBatis完成简单的增删改查操作, 参数传递.

  2. 掌握MyBatis的两种写法: 注解 和 XML方式

  3. 掌握MyBatis 相关的日志配置

前言

在应用分层学习时, 我们了解到web应用程序一般分为三层,即:Controller、Service、Dao

之前的案例中,请求流程如下: 浏览器发起请求, 先请求Controller, Controller接收到请求之后, 调用 Service进行业务逻辑处理, Service再调用Dao, 但是Dao层的数据是Mock的, 真实的数据应该从数据库中读取

我们学习MySQL数据库时,已经学习了JDBC来操作数据库, 但是JDBC操作太复杂了

1. 什么是MyBatis?

• MyBatis是一款优秀的 持久层 框架,用于简化JDBC的开发

• MyBatis本是 Apache的一个开源项目iBatis,2010年这个项目由apache迁移到了google code,并 且改名为MyBatis 。2013年11月迁移到Github

• 持久层:指的就是持久化操作的层, 通常指数据访问层(dao), 是用来操作数据库的.

简单来说 MyBatis 是更简单完成程序和数据库交互的框架,也就是更简单的操作和读取数据库工具 接下来,我们就通过一个入门程序,让大家感受一下通过Mybatis如何来操作数据库

2. MyBatis入门

Mybatis操作数据库的步骤:

  1. 准备工作(创建springboot工程、数据库表准备、实体类)

  2. 引入Mybatis的相关依赖,配置Mybatis(数据库连接信息)

  3. 编写SQL语句(注解/XML)

  4. 测试

2.1 准备工作

2.1.1 创建工程

创建springboot工程,并导入 mybatis的起步依赖、mysql的驱动包

Mybatis 是一个持久层框架, 具体的数据存储和数据操作还是在MySQL中操作的, 所以需要添加 MySQL驱动

项目工程创建完成后,自动在pom.xml文件中,导入Mybatis依赖和MySQL驱动依赖

java 复制代码
<!--Mybatis 依赖包-->
<dependency>
 <groupId>org.mybatis.spring.boot</groupId>
 <artifactId>mybatis-spring-boot-starter</artifactId>
 <version>3.0.3</version>
</dependency>
<!--mysql驱动包-->
<dependency>
 <groupId>com.mysql</groupId>
 <artifactId>mysql-connector-j</artifactId>
 <scope>runtime</scope>
</dependency>

2.1.2 数据准备

创建用户表, 并创建对应的实体类User

java 复制代码
-- 使⽤数据数据 
USE mybatis_test;
-- 创建表[⽤⼾表] 
DROP TABLE IF EXISTS user_info;
CREATE TABLE `user_info` (
 `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() ON UPDATE now(),
 PRIMARY KEY ( `id` ) 
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4; 
-- 添加⽤⼾信息 
INSERT INTO mybatis_test.user_info( username, `password`, age, gender, phone )
VALUES ( 'admin', 'admin', 18, 1, '18612340001' );
INSERT INTO mybatis_test.user_info( username, `password`, age, gender, phone )
VALUES ( 'zhangsan', 'zhangsan', 18, 1, '18612340002' );
INSERT INTO mybatis_test.user_info( username, `password`, age, gender, phone )
VALUES ( 'lisi', 'lisi', 18, 1, '18612340003' );
INSERT INTO mybatis_test.user_info( username, `password`, age, gender, phone )
VALUES ( 'wangwu', 'wangwu', 18, 1, '18612340004' );

创建对应的实体类 UserInfo

实体类的属性名与表中的字段名一一对应

java 复制代码
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驱动类 • 登录名 • 密码 • 数据库连接字符串

如果是application.yml文件, 配置内容如下:

java 复制代码
# 数据库连接配置
spring:
 datasource:
 url: jdbc:mysql://127.0.0.1:3306/mybatis_test?
characterEncoding=utf8&useSSL=false
 username: root
 password: root
 driver-class-name: com.mysql.cj.jdbc.Driver

2.3 写持久层代码

java 复制代码
import com.example.demo.model.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 user_info")
 public List<UserInfo> queryAllUser();
}

Mybatis的持久层接口规范一般都叫 XxxMapper

@Mapper注解:表示是MyBatis中的Mapper接口

• 程序运行时, 框架会自动生成接口的实现类对象(代理对象),并给交Spring的IOC容器管理

• @Select注解:代表的就是select查询,也就是注解对应方法的具体实现内容

2.4 单元测试

在创建出来的SpringBoot工程中,在src下的test目录下,已经自动帮我们创建好了测试类 ,我们可以 直接使用这个测试类来进行测试

java 复制代码
import com.example.demo.mapper.UserInfoMapper;
import com.example.demo.model.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;
@SpringBootTest
class DemoApplicationTests {
 @Autowired
 private UserInfoMapper userInfoMapper;
 @Test
 void contextLoads() {
 List<UserInfo> userInfoList = userInfoMapper.queryAllUser();
 System.out.println(userInfoList);
 }
}

返回结果中, 可以看到, 只有SQL语句中查询的列对应的属性才有赋值

使用Idea 自动生成测试类

除此之外, 也可以使用Idea自动生成测试类

  1. 在需要测试的Mapper接口中, 右键 -> Generate -> Test

  2. 书写测试代码

java 复制代码
@SpringBootTest
class UserInfoMapperTest {

    @Autowired
    private UserInfoMapper userInfoMapper;
    @Test
    void queryAllUser() {
        List<UserInfo> userInfos = userInfoMapper.queryAllUser();
        System.out.println(userInfos);
    }


}

记得加 @SpringBootTest 注解, 加载Spring运行环境

3. MyBatis的基础操作

上面我们学习了Mybatis的查询操作, 接下来我们学习MyBatis的增, 删, 改操作 在学习这些操作之前, 我们先来学习MyBatis日志打印

3.1 打印日志

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

在配置文件中进行配置即可

java 复制代码
mybatis:
 configuration: # 配置打印 MyBatis日志
 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

重新运行的结果

3.2 参数传递

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

java 复制代码
@Select("select username, `password`, age, gender, phone from user_info whereid= 4 ")
UserInfo queryById();

但是这样的话, 只能查找id=4 的数据, 所以SQL语句中的id值不能写成固定数值,需要变为动态的数值 解决方案:在queryById方法中添加一个参数(id),将方法中的参数,传给SQL语句 使用 #{} 的方式获取方法中的参数

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

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

添加测试用例

java 复制代码
@Test
void queryById() {
 UserInfo userInfo = userInfoMapper.queryById(4);
 System.out.println(userInfo);
}

也可以通过 @Param , 设置参数的别名, 如果使用 @Param 设置别名, #{...}里面的属性名必须和 @Param 设置的一样

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

3.3 增(Insert)

SQL 语句

java 复制代码
insert into user_info (username, `password`, age, gender, phone) values
("zhaoliu","zhaoliu",19,1,"18700001234")

把SQL中的常量替换为动态的参数

Mapper接口

java 复制代码
@Insert("insert into user_info (username, `password`, age, gender, phone)
values (#{username},#{password},#{age},#{gender},#{phone})")
Integer insert(UserInfo userInfo);

直接使用UserInfo对象的属性名来获取参数

测试代码:

java 复制代码
@Test
void insert() {
 UserInfo userInfo = new UserInfo();
 userInfo.setUsername("zhaoliu");
 userInfo.setPassword("zhaoliu");
 userInfo.setGender(2);
 userInfo.setAge(21);
 userInfo.setPhone("18612340005");
 userInfoMapper.insert(userInfo);
}

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

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

java 复制代码
@Insert("insert into user_info (username, `password`, age, gender, phone)
values (#{userInfo.username},#{userInfo.password},#{userInfo.age},#
{userInfo.gender},#{userInfo.phone})")
Integer insert(@Param("userInfo") UserInfo userInfo);

返回主键

Insert 语句默认返回的是 受影响的行数

但有些情况下, 数据插入之后, 还需要有后续的关联操作, 需要获取到新插入数据的id

如果想要拿到自增id, 需要在Mapper接口的方法上添加一个Options的注解

java 复制代码
@Options(useGeneratedKeys = true, keyProperty = "id")
@Insert("insert into user_info (username, age, gender, phone) values (#
{userInfo.username},#{userInfo.age},#{userInfo.gender},#{userInfo.phone})")
Integer insert(@Param("userInfo") UserInfo userInfo);

测试数据

java 复制代码
@Test
    void insert() {
        UserInfo userInfo=new UserInfo();
        userInfo.setUsername("zhaoliu");
        userInfo.setPassword("zhaoliu");
        userInfo.setGender(2);
        userInfo.setAge(21);
        userInfo.setPhone("18612340005");
        Integer count = userInfoMapper.insert(userInfo);
        System.out.println("添加数据条数:" +count +", 数据ID:" + userInfo.getId());
    }

注意: 设置 useGeneratedKeys=true 之后, 方法返回值依然是受影响的行数, 自增id 会设置在上 述 keyProperty 指定的属性中

3.4 删(Delete)

SQL 语句

java 复制代码
delete from user_info where id=6

Mapper接口

java 复制代码
@Delete("delete from user_info where id = #{id}")
void delete(Integer id);

3.5 改(Update)

java 复制代码
update user_info set username="zhaoliu" where id=5

Mapper接口

java 复制代码
@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 会获取结果中返回的列名并在 Java 类中查找相同名字的属性(忽略大小写)。 这意味着如果发现了 ID 列和 id 属性,MyBatis 会将列 ID 的值赋给 id 属性

解决办法

  1. 起别名

  2. 结果映射

  3. 开启驼峰命名

3.6.1 起别名

在SQL语句中,给列名起别名,保持别名和实体类属性名一样

java 复制代码
@Select("select id, username, `password`, age, gender, phone, delete_flag as
deleteFlag, " +
 "create_time as createTime, update_time as updateTime from user_info")
public List<UserInfo> queryAllUser();

SQL语句太长时, 使用加号 + 进行字符串拼接

3.6.2 结果映射

java 复制代码
@Select("select id, username, `password`, age, gender, phone, delete_flag,
create_time, update_time from user_info")
@Results({
 @Result(column = "delete_flag",property = "deleteFlag"),
 @Result(column = "create_time",property = "createTime"),
 @Result(column = "update_time",property = "updateTime")
})
List<UserInfo> queryAllUser();

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

java 复制代码
@Select("select id, username, `password`, age, gender, phone, delete_flag,
create_time, update_time from user_info")
@Results(id = "resultMap",value = {
 @Result(column = "delete_flag",property = "deleteFlag"),
 @Result(column = "create_time",property = "createTime"),
 @Result(column = "update_time",property = "updateTime")
})
List<UserInfo> queryAllUser();
@Select("select id, username, `password`, age, gender, phone, delete_flag,
create_time, update_time " +
 "from user_info where id= #{userid} ")
@ResultMap(value = "resultMap")
UserInfo queryById(@Param("userid") Integer id);

使用 id 属性给该 Results 定义别名, 使用 @ResultMap 注解来复用其他定义的 ResultMap

3.6.3 开启驼峰命名(推荐)

通常数据库列使用蛇形命名法进行命名(下划线分割各个单词), 而 Java 属性一般遵循驼峰命名法约定. 为了在这两种命名方式之间启用自动映射,需要将 mapUnderscoreToCamelCase 设置为 true

java 复制代码
mybatis:
  configuration:
    map-underscore-to-camel-case: true #配置驼峰自动转换

驼峰命名规则: abc_xyz => abcXyz

• 表中字段名:abc_xyz

• 类中属性名:abcXyz

Java 代码不做任何处理

java 复制代码
@Select("select id, username, `password`, age, gender, phone, delete_flag, " +
 "create_time, update_time from user_info")
public List<UserInfo> queryAllUser();

添加上述配置, 运行代码:

4. MyBatis XML配置文件

Mybatis的开发有两种方式

  1. 注解

  2. XML

上面学习了注解的方式, 接下来我们学习XML的方式

使用Mybatis的注解方式,主要是来完成一些简单的增删改查功能. 如果需要实现复杂的SQL功能,建 议使用XML来配置映射语句,也就是将SQL语句写在XML配置文件中

MyBatis XML的方式需要以下两步

  1. 配置数据库连接字符串和MyBatis

  2. 写持久层代码

4.1 配置连接字符串和MyBatis

此步骤需要进行两项设置,数据库连接字符串设置和 MyBatis 的 XML 文件配置。

如果是application.yml文件, 配置内容如下:

java 复制代码
# 数据库连接配置
spring:
 datasource:
 url: jdbc:mysql://127.0.0.1:3306/mybatis_test?
characterEncoding=utf8&useSSL=false
 username: root
 password: root
 driver-class-name: com.mysql.cj.jdbc.Driver
# 配置 mybatis xml 的文件路径,在 resources/mapper 创建所有表的 xml 文件
mybatis:
 mapper-locations: classpath:mapper/**Mapper.xml

4.2 写持久层代码

持久层代码分两部分

  1. 方法定义 Interface

  2. 方法实现: XXX.xml

4.2.1 添加 mapper 接口

数据持久层的接口定义:

java 复制代码
import com.example.demo.model.UserInfo;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface UserInfoXMlMapper {
 List<UserInfo> queryAllUser();
}

4.2.2 添加 UserInfoXMLMapper.xml

数据持久成的实现,MyBatis 的固定 xml 格式

java 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserInfoMapper">

</mapper>

查询所有用户的具体实现

java 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserInfoXMlMapper">
 <select id="queryAllUser" resultType="com.example.demo.model.UserInfo">
 select username,`password`, age, gender, phone from user_info
 </select>
</mapper>

以下是对以上标签的说明

• 标签:需要指定 namespace 属性,表示命名空间,值为 mapper 接口的全限定 名,包括全包名.类名。

• 查询标签:是用来执行数据库的查询操作的:

4.2.3 单元测试

java 复制代码
@SpringBootTest
class UserInfoMapperTest {
 @Autowired
 private UserInfoMapper userInfoMapper;
 @Test
 void queryAllUser() {
 List<UserInfo> userInfoList = userInfoMapper.queryAllUser();
 System.out.println(userInfoList);
 }

}

4.3 增删改查操作

接下来,我们来实现一下用户的增加、删除和修改的操作

4.3.1 增(Insert)

UserInfoMapper接口

java 复制代码
Integer insertUser(UserInfo userInfo);

UserInfoMapper.xml实现

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

返回自增 id

java 复制代码
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
 insert into user_info (username, `password`, age, gender, phone) values
 (#{userInfo.username},#{userInfo.password},#{userInfo.age},#
{userInfo.gender},#{userInfo.phone})
</insert>

4.3.2 删(Delete)

UserInfoMapper接口

java 复制代码
Integer deleteUser(Integer id);

UserInfoMapper.xml实现

java 复制代码
<delete id="deleteUser">
 delete from user_info where id = #{id}
</delete>

4.3.3 改(Update)

UserInfoMapper接口

java 复制代码
Integer updateUser(UserInfo userInfo);

UserInfoMapper.xml实现

java 复制代码
<update id="updateUser">
 update user_info set username=#{username} where id=#{id}
</update>

4.3.4 查(Select)

同样的, 使用XML 的方式进行查询, 也存在数据封装的问题

我们把SQL语句进行简单修改, 查询更多的字段内容

java 复制代码
select id="queryAllUser" resultType="com.example.demo.model.UserInfo">
 select id, username,`password`, age, gender, phone, delete_flag,
create_time, update_time from user_info
</select>

5. 其他查询操作

5.1 多表查询

多表查询和单表查询类似, 只是SQL不同而已

5.1.1 准备工作

上面建了一张用户表, 我们再来建一张文章表, 进行多表关联查询

数据准备

java 复制代码
DROP TABLE IF EXISTS articleinfo;

CREATE TABLE articleinfo (
 id INT PRIMARY KEY auto_increment,
 title VARCHAR ( 100 ) NOT NULL,
 content TEXT NOT NULL,
 uid INT NOT NULL,
 delete_flag TINYINT ( 4 ) DEFAULT 0 COMMENT '0-正常, 1-删除',
 create_time DATETIME DEFAULT now(),
 update_time DATETIME DEFAULT now()
) DEFAULT charset 'utf8mb4';
-- 插入测试数据
INSERT INTO articleinfo ( title, content, uid ) VALUES ( 'Java', 'Java正文', 1
);

对应Model:

java 复制代码
import lombok.Data;
import java.util.Date;
@Data
public class ArticleInfo {
 private Integer id;
 private String title;
 private String content;
 private Integer uid;
 private Integer deleteFlag;
 private Date createTime;
 private Date updateTime;
}

5.1.2 数据查询

需求: 根据uid查询作者的名称等相关信息

SQL:

java 复制代码
SECT
 ta.id,
 ta.title,
 ta.content,
 ta.uid,
 tb.username,
 tb.age,
 tb.gender
FROM
 articleinfo ta
 LEFT JOIN user_info tb ON ta.uid = tb.id
WHERE
 ta.id =1

补充实体类:

java 复制代码
@Data
public class ArticleInfo {
 private Integer id;
 private String title;
 private String content;
 private Integer uid;
 private Integer deleteFlag;
 private Date createTime;
 private Date updateTime;
 //用户相关信息
 private String username;
 private Integer age;
 private Integer gender;
}

接口定义:

java 复制代码
import com.example.demo.model.ArticleInfo;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface ArticleInfoMapper {
 @Select("SELECT
ta.id,ta.title,ta.content,ta.uid,tb.username,tb.age,tb.gender " +
 "FROM articleinfo ta LEFT JOIN user_info tb ON ta.uid = tb.id " +
 "WHERE ta.id = #{id}")
 ArticleInfo queryUserByUid(Integer id);
}

5.2 #{} 和 ${}

MyBatis 参数赋值有两种方式, 咱们前面使用了 #{} 进行赋值, 接下来我们看下二者的区别

5.2.1 #{} 和${} 使用

1. 先看Interger类型的参数

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

发现我们输出的SQL语句

java 复制代码
select username, `password`, age, gender, phone from user_info where id= ?

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

我们把 #{} 改成 ${} 再观察打印的日志

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

2. 接下来我们再看String类型的参数

java 复制代码
@Select("select username, `password`, age, gender, phone from user_info where
username= #{name} ")
UserInfo queryByName(String name);

我们把 #{} 改成 ${} 再观察打印的日志

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

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

修改代码如下:

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

再次运行, 结果正常返回

从上面两个例子可以看出:

5.2.2 #{} 和 ${}区别

#{} 和 ${} 的区别就是预编译SQL和即时SQL 的区别

  1. 性能更高

  2. 更安全(防止SQL注入)

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

正常访问情况

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

结果运行正常

SQL注入场景:

java 复制代码
@Test
void queryByName() {
 List<UserInfo> userInfos = userInfoMapper.queryByName("' or 1='1");
 System.out.println(userInfos);
}

结果依然被正确查询出来了, 其中参数 or被当做了SQL语句的一部分

可以看出来, 查询的数据并不是自己想要的数据. 所以用于查询的字段,尽量使用 #{} 预查询的方式

5.3 排序功能

从上面的例子中, 可以得出结论: ${} 会有SQL注入的风险, 所以我们尽量使用#{}完成查询

既然如此, 是不是 ${} 就没有存在的必要性了呢

可以发现, 当使用 #{sort} 查询时, asc 前后自动给加了引号, 导致 sql 错误

除此之外, 还有表名作为参数时, 也只能使用 ${}

5.4 like 查询

like 使用 #{} 报错

java 复制代码
@Select("select id, username, age, gender, phone, delete_flag, create_time,
update_time " +
 "from user_info where username like '%#{key}%' ")
List<UserInfo> queryAllUserByLike(String key);

把 #{} 改成 {} 可以正确查出来, 但是{}存在SQL注入的问题, 所以不能直接使用 ${}.

解决办法: 使用 mysql 的内置函数 concat() 来处理,实现代码如下:

java 复制代码
@Select("select id, username, age, gender, phone, delete_flag, create_time,
update_time " +
 "from user_info where username like concat('%',#{key},'%')")
List<UserInfo> queryAllUserByLike(String key);

6. 数据库连接池

在上面Mybatis的讲解中, 我们使用了数据库连接池技术, 避免频繁的创建连接, 销毁连接

下面我们来了解下数据库连接池

6.1 介绍

数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接, 而不是再重新建立一个

没有使用数据库连接池的情况: 每次执行SQL语句, 要先创建一个新的连接对象, 然后执行SQL语句, SQL 语句执行完, 再关闭连接对象释放资源. 这种重复的创建连接, 销毁连接比较消耗资源

使用数据库连接池的情况: 程序启动时, 会在数据库连接池中创建一定数量的Connection对象, 当客户 请求数据库连接池, 会从数据库连接池中获取Connection对象, 然后执行SQL, SQL语句执行完, 再把 Connection归还给连接池

优点:

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

6.2 使用

常见的数据库连接池

• C3P0 • DBCP • Druid • Hikari

目前比较流行的是 Hikari, Druid

  1. Hikari : SpringBoot默认使用的数据库连接池
  1. Druid

如果我们想把默认的数据库连接池切换为Druid数据库连接池, 只需要引入相关依赖即可

java 复制代码
<dependency>
 <groupId>com.alibaba</groupId>
 <artifactId>druid-spring-boot-3-starter</artifactId>
 <version>1.2.21</version>
</dependency>

如果SpringBoot版本为2.X, 使用druid-spring-boot-starter 依赖

java 复制代码
<dependency>
 <groupId>com.alibaba</groupId>
 <artifactId>druid-spring-boot-starter</artifactId>
 <version>1.1.17</version>
</dependency>

7. 总结

7.1 MySQL 开发企业规范

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

  2. 表必备三字段: id, create_time, update_time

  3. 在表查询中, 避免使用 * 作为查询的字段列表, 标明需要哪些字段

7.2 #{} 和${} 区别

  1. #{}:预编译处理, ${}:字符直接替换

  2. #{} 可以防止SQL注入, ${}存在SQL注入的风险, 查询语句中, 可以使用 #{} ,推荐使用 #{}

  3. 但是一些场景, #{} 不能完成, 比如 排序功能, 表名, 字段名作为参数时, 这些情况需要使用${}

相关推荐
djjdjdjdjjdj2 小时前
如何利用 watchEffect 实现在线人数实时统计?Socket 与响应式结合
jvm·数据库·python
专注VB编程开发20年2 小时前
为何Win内置SQLite却缺驱动?微软只为保住Access中小企业市场,office码头
数据库·microsoft·sqlite
算是难了2 小时前
TypeORM vs Prisma
数据库·typescript·node.js
m0_716430072 小时前
HTML函数能否用触控板高效编写_触控硬件操作体验评估【汇总】
jvm·数据库·python
2401_835956812 小时前
Golang怎么安全关闭channel_Golang channel关闭教程【通俗】
jvm·数据库·python
Absurd5872 小时前
golang如何实现MQTT主题通配符路由_golang MQTT主题通配符路由实现策略
jvm·数据库·python
m0_674294642 小时前
宝塔面板如何设置网站强制HTTPS_配置Nginx自动跳转规则
jvm·数据库·python
qq_424098562 小时前
HTML函数开发用可拆卸键盘设计实用吗_模块化硬件体验评估【指南】
jvm·数据库·python
Wyz201210242 小时前
CSS如何实现Less颜色函数自动计算渐变_使用lighten与darken实现视觉反馈
jvm·数据库·python