点评项目(Redis中间件)数据操作相关知识总结
缓存简介
无论在哪一层,缓存都遵循着类似的原则:
检查:当需要获取数据时,首先检查高速的缓存中是否存在。
命中:如果存在(缓存命中 - Cache Hit),直接从缓存中读取,速度极快。
未命中:如果不存在(缓存未命中 - Cache Miss),则从速度较慢的主存储中读取数据,同时将一份数据副本存入缓存中,以便下次使用。
计算机组成相关知识点
内存 (Memory / RAM - Random Access Memory)
比喻:工厂的工作台。工人在生产产品(CPU在处理数据)时,需要把原材料和工具都放在手边的工作台上。这个工作台的大小决定了能同时摆放多少东西,直接影响工作效率。
官方解释:内存是计算机的主存储器,也叫随机存取存储器。它的特点是:
高速:比磁盘快成千上万倍,CPU可以直接与之高速交换数据。
易失性:需要持续供电来保存数据。一旦断电,工作台上所有东西(数据)都会消失。
作用:用来临时存放正在运行的操作系统、应用程序和当前正在处理的数据。当你打开一个软件或文件时,它们就从磁盘被加载到内存中,CPU再从内存中读取数据进行处理。
关键点:内存决定了你的电脑能同时流畅运行多少个程序。8GB内存的工作台比4GB的大,能同时放下更多东西而不需要频繁地收拾(与磁盘交换)。
磁盘 (Disk / Storage)
比喻:仓库后方的大货架。这里存放着所有的原材料、成品和工具。货架很大,能放很多东西,但取用东西需要走过去拿,速度很慢。
官方解释:磁盘是计算机的外部存储器或辅助存储器,主要用于长期持久化地保存数据。它的特点是:
非易失性:断电后数据不会丢失。你的操作系统、软件、文档、照片等都保存在这里。
速度慢:相比内存,它的读写速度非常慢(机械硬盘尤甚)。
容量大:通常以GB、TB为单位,远大于内存容量。
常见类型:
HDD (Hard Disk Drive, 机械硬盘):像老式的唱片机,通过磁头在旋转的盘片上读写数据。优点是便宜、容量大;缺点是速度慢、怕震动、有噪音。
SSD (Solid State Drive, 固态硬盘):像超大号的U盘,使用闪存芯片存储数据。优点是速度极快(远超HDD,接近内存)、抗震、无噪音;缺点是价格相对较高(但已大幅下降),寿命有写入次数限制(但对普通用户影响很小)。
关键点:磁盘(特别是SSD)的速度极大影响了电脑的开机速度、软件启动速度和文件加载速度。现在,SSD几乎是提升电脑体验最有效的升级。
缓存 (Cache)
比喻:工作台旁边的一个小工具盒。工人(CPU)最常用的一些螺丝刀和零件(数据)会从工作台(内存)上拿过来,放在这个触手可及的小盒子里,用的时候几乎不用弯腰,极大提高了效率。
官方解释:缓存是一小块高速存储器,它的存在是为了弥补CPU超高速和内存相对较慢之间的速度差距。
位置:通常直接集成在CPU内部或非常靠近CPU的地方。
速度:比内存快得多,但容量很小(KB或MB级别)。
原理:CPU要读取数据时,首先去缓存里找(这叫做“缓存命中”),如果找到了就直接使用,速度极快。如果没找到(“缓存未命中”),才去内存里取,同时会把这份数据及其周围的数据也复制一份到缓存里,因为根据“局部性原理”,CPU接下来很可能还会用到它们。
层级:通常分为L1、L2、L3三级缓存,L1最小最快,L3最大最慢(但依然远快于内存)。
关键点:缓存是CPU高效工作的关键,它的设计直接影响了CPU的性能。
数据库索引 (Database Index)
比喻:教科书最后的“索引”页。你想在一本厚厚的教科书里找到“缓存”这个概念,一页一页翻(全表扫描)非常慢。但如果你先翻到书后的索引,它会告诉你“缓存:第123页”,你就能直接翻到那一页,效率极高。
官方解释:索引是数据库管理系统中一种特殊的查找表,它就像是数据的目录。它能够极大地加快数据库的查询速度。
本质:索引是一种数据结构(最常用的是B-Tree/B+Tree),它保存了表中一列或多列的值与其物理存储位置的映射关系。
工作原理:
没有索引:数据库要执行
SELECT * FROM users WHERE name = 'Alice';
,它必须扫描整个users
表,逐行对比,这叫全表扫描,非常慢。有索引(在
name
字段上):数据库会先去name
索引这个“目录”里快速找到Alice
这个值,索引直接告诉你这条记录存储在磁盘的哪个扇区,然后数据库直接去那个位置读取即可,速度极快。
代价:
占用空间:索引本身也需要占用磁盘和内存空间。
降低写操作速度:当你对表进行增、删、改操作时,数据库不仅需要修改表数据,还需要更新对应的索引,这会带来额外的开销。
关键点:索引是用空间换时间的经典策略。正确的索引对查询性能提升巨大,但不宜滥用,通常只为经常用于查询条件的字段创建索引。
添加Redis缓存
先从redis里面获取店铺 ,要先获取redis对象,然后再根据key取值,我们这里学习所以用string类型,然后判断是否为空,然后反序列化把字符串转换成对象。
不存在则查询数据库,数据库也没有就直接返回错误,存在就要存储到Redis里面。
缓存更新策略
有缓存和数据库的数据不一致的问题
就上面两种情况,因为数据库的操作速度是远远低于缓存的,所以对于第二种情况,出现线程安全的概率远远低于第一种情况。但是第二种出现问题的情况仍然可能出现,所以我们后期会使用超时剔除。
mybatis-plus回顾
环境搭建
pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><!-- 项目基本信息 --><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.5.0</version><relativePath/></parent><groupId>com.itheima</groupId><artifactId>mybatisplus_02_dql</artifactId><version>0.0.1-SNAPSHOT</version><name>MyBatisPlus DQL Project</name><description>MyBatisPlus Dynamic Query Language Example Project</description><!-- 属性配置 --><properties><java.version>1.8</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><mybatisplus.version>3.4.1</mybatisplus.version><druid.version>1.1.16</druid.version></properties><!-- 依赖管理 --><dependencies><!-- Spring Boot Starter --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><!-- MyBatis Plus --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>${mybatisplus.version}</version></dependency><!-- Druid 数据库连接池 --><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>${druid.version}</version></dependency><!-- MySQL 驱动 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><!-- Lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!-- 测试依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><!-- 构建配置 --><build><plugins><!-- Spring Boot Maven 插件 --><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin><!-- 编译插件 --><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><source>${java.version}</source><target>${java.version}</target><encoding>${project.build.sourceEncoding}</encoding></configuration></plugin></plugins></build>
</project>
编写接口
@Mapperpublic interface UserDao extends BaseMapper<User> {}
编写模型类
@Datapublic class User {private Long id;private String name;private String password;private Integer age;private String tel;}
编写引导类
@SpringBootApplicationpublic class Mybatisplus02DqlApplication {public static void main(String[] args) {SpringApplication.run(Mybatisplus02DqlApplication.class, args);}}
编写配置文件
# 数据源配置
spring:datasource:# 连接池类型type: com.alibaba.druid.pool.DruidDataSource# 驱动类名driver-class-name: com.mysql.cj.jdbc.Driver# 数据库连接URLurl: jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=false# 数据库用户名username: root# 数据库密码password: root# Druid连接池特定配置druid:# 初始化连接数initial-size: 5# 最小空闲连接数min-idle: 5# 最大活跃连接数max-active: 20# 获取连接等待超时时间(毫秒)max-wait: 60000# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接(毫秒)time-between-eviction-runs-millis: 60000# 连接保持空闲而不被驱逐的最小时间(毫秒)min-evictable-idle-time-millis: 300000# 测试连接的SQLvalidation-query: SELECT 1# 申请连接时执行validationQuery检测连接是否有效test-while-idle: true# 获取连接时执行validationQuery检测连接是否有效test-on-borrow: false# 归还连接时执行validationQuery检测连接是否有效test-on-return: false# MyBatis Plus配置
mybatis-plus:# 全局配置global-config:db-config:# 主键类型id-type: auto# 表名下划线转驼峰table-underline: true# 列名下划线转驼峰column-underline: true# 配置configuration:# 日志实现log-impl: org.apache.ibatis.logging.stdout.StdOutImpl# 开启驼峰命名映射map-underscore-to-camel-case: true# 缓存启用cache-enabled: true# 延迟加载启用lazy-loading-enabled: true# 延迟加载触发方法lazy-load-trigger-methods: ""# 默认执行器default-executor-type: simple# 查询超时时间(秒)default-statement-timeout: 30# 日志配置
logging:level:# MyBatis Plus日志级别com.itheima.mapper: debug# Spring框架日志级别org.springframework: info# 日志文件输出路径file:name: logs/application.log# 日志模式pattern:console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
编写测试类
package com.itheima;import com.itheima.dao.UserDao;
import com.itheima.entity.User;
import org.junit.jupiter.api.DisplayName;
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.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertNotNull;/*** MyBatisPlus应用测试类* 用于测试用户数据访问功能* * @author itheima* @version 1.0* @since 2023*/
@SpringBootTest
@DisplayName("用户数据访问层测试")
class Mybatisplus02DqlApplicationTests {@Autowiredprivate UserDao userDao;/*** 测试获取所有用户数据* 验证用户数据访问层的基本查询功能*/@Test@DisplayName("测试获取所有用户")void testGetAllUsers() {// 执行查询List<User> userList = userDao.selectList(null);// 验证结果assertNotNull(userList, "用户列表不应为空");assertThat(userList).isNotEmpty().hasSizeGreaterThan(0);// 输出结果(仅用于开发调试)System.out.println("查询到的用户数量: " + userList.size());userList.forEach(user -> System.out.println("用户信息: " + user.toString()));// 验证每个用户的必要字段userList.forEach(user -> {assertThat(user.getId()).isNotNull();assertThat(user.getName()).isNotBlank();});}/*** 测试上下文加载* 验证Spring应用上下文是否正确配置*/@Test@DisplayName("测试应用上下文加载")void testContextLoads() {assertNotNull(userDao, "UserDao应该被正确注入");}/*** 测试数据库连接* 验证数据库连接是否正常*/@Test@DisplayName("测试数据库连接")void testDatabaseConnection() {long count = userDao.selectCount(null);assertThat(count).isGreaterThanOrEqualTo(0);System.out.println("数据库中的用户总数: " + count);}
}
spring整合mybatis使用方法
导入依赖
<dependencies><!-- Spring Context (包含核心功能) --><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.23</version> <!-- 请使用最新版本 --></dependency><!-- Spring JDBC (用于数据源和事务管理) --><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.3.23</version></dependency><!-- MyBatis --><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.10</version> <!-- 请使用最新版本 --></dependency><!-- MyBatis-Spring 适配器 (最关键) --><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>2.0.7</version> <!-- 请使用最新版本 --></dependency><!-- 数据库连接池 (以 HikariCP 为例) --><dependency><groupId>com.zaxxer</groupId><artifactId>HikariCP</artifactId><version>4.0.3</version></dependency><!-- MySQL 驱动 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.32</version> <!-- 注意与你的MySQL服务器版本匹配 --></dependency>
</dependencies>
编写模型类
// User.java
public class User {private Long id;private String name;private Integer age;private String email;// 省略 getter, setter, toString 方法
}
创建mapper接口
// UserMapper.java
public interface UserMapper {User selectUserById(Long id);List<User> selectAllUsers();int insertUser(User user);int updateUser(User user);int deleteUserById(Long id);
}
sql注入
文件注入
<?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.yourpackage.mapper.UserMapper"> <!-- 必须是Mapper接口的全限定名 --><sql id="Base_Column_List">id, name, age, email</sql><select id="selectUserById" resultType="com.yourpackage.entity.User">SELECT<include refid="Base_Column_List"/>FROM userWHERE id = #{id}</select><select id="selectAllUsers" resultType="com.yourpackage.entity.User">SELECT<include refid="Base_Column_List"/>FROM user</select><insert id="insertUser" parameterType="com.yourpackage.entity.User" useGeneratedKeys="true" keyProperty="id">INSERT INTO user (name, age, email)VALUES (#{name}, #{age}, #{email})</insert><!-- 其他 update, delete 语句 -->
</mapper>
注解注入
// UserMapper.java
public interface UserMapper {// 1. 最简单的查询@Select("SELECT * FROM user WHERE id = #{id}")User selectUserById(Long id);// 2. 查询所有用户@Select("SELECT * FROM user")List<User> selectAllUsers();// 3. 插入用户,并返回自增主键@Insert("INSERT INTO user (name, age, email) VALUES (#{name}, #{age}, #{email})")@Options(useGeneratedKeys = true, keyProperty = "id") // keyProperty 指实体类中的主键属性名int insertUser(User user);// 4. 更新用户@Update("UPDATE user SET name=#{name}, age=#{age}, email=#{email} WHERE id=#{id}")int updateUser(User user);// 5. 删除用户@Delete("DELETE FROM user WHERE id = #{id}")int deleteUserById(Long id);// 6. 使用 @Param 注解处理多个参数@Select("SELECT * FROM user WHERE name = #{name} AND age > #{age}")List<User> selectUsersByNameAndAge(@Param("name") String name, @Param("age") Integer age);// 7. 复杂结果映射 - 使用 @Results 和 @Result// 假设数据库字段是 user_name,而实体类属性是 name,需要手动映射@Results(id = "userResultMap", value = {@Result(property = "id", column = "id", id = true), // id=true 表示这是主键@Result(property = "name", column = "user_name"), // 映射数据库字段 user_name 到属性 name@Result(property = "email", column = "email_address") // 映射数据库字段 email_address 到属性 email})@Select("SELECT id, user_name, email_address FROM user WHERE id = #{id}")User selectUserWithComplexMapping(Long id);// 8. 引用上面定义的结果映射@ResultMap("userResultMap") // 引用上面 @Results 的 id@Select("SELECT id, user_name, email_address FROM user")List<User> selectAllUsersWithComplexMapping();
}
核心配置类
数据源 (DataSource)
SqlSessionFactoryBean:Spring 会用它来创建 MyBatis 的
SqlSessionFactory
。MapperScannerConfigurer:自动扫描 Mapper 接口并注册为 Spring Bean。
// AppConfig.java
@Configuration
@ComponentScan("com.yourpackage") // 扫描你的@Service, @Component等
@EnableTransactionManagement // 启用注解式事务管理
public class AppConfig {// 1. 配置数据源 (以HikariCP为例)@Beanpublic DataSource dataSource() {HikariDataSource dataSource = new HikariDataSource();dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/your_database?useSSL=false&serverTimezone=UTC");dataSource.setUsername("your_username");dataSource.setPassword("your_password");dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");return dataSource;}// 2. 配置SqlSessionFactoryBean@Beanpublic SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) throws Exception {SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();sessionFactory.setDataSource(dataSource); // 注入数据源// 设置Mapper XML文件的位置 (非常重要!)sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));// 可选:配置MyBatis的其他设置,如别名、驼峰命名映射等org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();configuration.setMapUnderscoreToCamelCase(true); // 开启驼峰命名自动映射sessionFactory.setConfiguration(configuration);return sessionFactory;}// 3. 配置Mapper扫描器,自动注册Mapper接口的代理Bean@Beanpublic MapperScannerConfigurer mapperScannerConfigurer() {MapperScannerConfigurer scanner = new MapperScannerConfigurer();scanner.setBasePackage("com.yourpackage.mapper"); // 指定Mapper接口所在的包// 注意:如果使用了多个SqlSessionFactory,这里需要指定,但通常一个就够了// scanner.setSqlSessionFactoryBeanName("sqlSessionFactory");return scanner;}// 4. 配置事务管理器@Beanpublic PlatformTransactionManager transactionManager(DataSource dataSource) {return new DataSourceTransactionManager(dataSource);}
}
DataSource
:一切的起点,管理数据库连接池。SqlSessionFactoryBean
:它是一个 FactoryBean,Spring 在初始化它时,内部会调用它的
getObject()
方法。这个方法会读取你的所有配置(数据源、XML 位置、设置等),最终构建出一个 MyBatis 的核心对象——
SqlSessionFactory
。SqlSessionFactory
是生产SqlSession
的工厂,而SqlSession
是执行 SQL 的会话。
MapperScannerConfigurer
:它是一个 BeanDefinitionRegistryPostProcessor,会在 Spring 容器初始化早期运行。
它的任务是扫描你指定的包,找到所有 Mapper 接口。
对于每一个 Mapper 接口,它会向 Spring 容器注册一个
MapperFactoryBean
。MapperFactoryBean
也是一个 FactoryBean,它会利用第 2 步创建的SqlSessionFactory
,为 Mapper 接口生成一个动态代理的实现类对象。最终,这个代理对象会被注册为 Spring Bean(Bean 的名称通常是接口名首字母小写,如
userMapper
)。
PlatformTransactionManager
:为 Spring 的声明式事务(
@Transactional
)提供支持,确保数据库操作在事务内进行。