Java高级全套教程(十四)------ SpringData超详细实战详解
本章学习目标
本章为SpringData全套实战核心章节,聚焦SpringData各大主流模块的原理、配置、实战开发,摒弃繁琐理论堆砌,以原理吃透+代码实战+场景落地为核心,各知识点掌握要求如下:
| 知识点模块 | 掌握层级 | 学习要求说明 |
|---|---|---|
| Spring Data 核心概念 | 了解 | 知晓SpringData定位、核心优势、模块分类及适用场景 |
| Spring Data JPA | 精通掌握 | 掌握项目搭建、多种查询方式、底层原理、DDD领域驱动设计实战 |
| Spring Data JDBC | 精通掌握 | 掌握轻量ORM开发、JdbcTemplate工具使用、MyBatis集成实战 |
| Spring Data Redis | 精通掌握 | 掌握序列化配置、五大数据类型实操、缓存业务场景开发 |
| Spring Data ElasticSearch | 精通掌握 | 掌握ES核心概念、索引操作、全文检索、分页排序、复杂条件查询 |
| Spring Data MongoDB | 精通掌握 | 掌握文档型数据库配置、CRUD、分页、条件查询实战 |
| Spring Data Solr | 基础了解 | 了解核心概念、现状、基础使用,无需深度开发 |
| SpringData 综合实战案例 | 精通掌握 | 整合多模块实现企业级电商数据分层存储架构 |
一、Spring Data 核心概述
1.1 框架定位与核心优势
Spring Data是Spring生态专为数据持久化打造的开源子项目,核心目标是统一Java程序对接各类存储中间件的开发规范,解决传统开发中不同数据库、缓存、搜索引擎API不统一、代码冗余、重复造轮子的问题。
传统开发中,对接MySQL需写JDBC/MyBatis代码、对接Redis需整合Jedis、对接ES需手写原生API,技术栈杂乱;而SpringData提供统一的Repository编程模型,一套编码风格适配所有主流存储组件,极大提升开发效率。
其核心优势可总结为三点:
-
统一API规范:所有模块均基于Repository接口实现,CRUD操作语法一致,降低学习和维护成本
-
零冗余代码:无需手动编写SQL/模板代码,通过接口命名、注解即可实现绝大多数数据操作
-
生态兼容性强:完美适配SpringBoot,自动配置、开箱即用,支持自定义拓展和原生API混用
1.2 核心模块适配场景
SpringData通过不同子模块适配差异化存储场景,企业常用模块及适用场景如下:
-
Spring Data JPA:适配关系型数据库(MySQL、Oracle),主打ORM对象映射,适合传统业务数据表开发
-
Spring Data JDBC:轻量级关系型数据库操作框架,无缓存、无延迟加载,适合简单、高性能的数据库操作场景
-
Spring Data Redis:适配Redis缓存中间件,实现内存数据读写,用于热点数据缓存、分布式锁、限流等场景
-
Spring Data MongoDB:适配MongoDB文档数据库,适合存储非结构化、海量低价值数据(评论、日志、用户行为数据)
-
Spring Data ElasticSearch:适配ES搜索引擎,用于全文检索、模糊查询、海量数据快速检索场景
-
Spring Data Solr:老旧搜索引擎适配模块,官方已停止维护,仅做技术了解,新项目优先使用ES
前置环境要求:本教程所有实战案例基于SpringBoot 2.7.x版本开发,JDK版本1.8及以上,需掌握SpringBoot基础注解、依赖引入、配置文件编写基础。
二、Spring Data JPA 实战详解(核心重点)
2.1 核心概念与技术定位
Spring Data JPA是对JPA官方规范 的高阶封装,底层基于Hibernate实现,彻底简化了传统JPA繁琐的API操作。相较于MyBatis的SQL手动编写模式,JPA以对象驱动为核心,通过操作实体类对象即可完成数据库CRUD,无需手写基础SQL,适合快速迭代的业务项目。
2.2 JPA与主流ORM框架对比
JPA是Java官方ORM规范,仅定义接口标准,无具体实现;主流实现方案为Hibernate、EclipseLink。与MyBatis的核心差异如下:
-
编程模式:MyBatis面向SQL,手动编写语句;JPA面向对象,通过注解映射表关系
-
多表操作:MyBatis手动关联多表查询;JPA通过@OneToMany、@ManyToOne注解配置对象关系,自动关联查询
-
开发效率:JPA基础CRUD零代码,效率更高;MyBatis灵活度更高,适配复杂SQL场景
2.3 项目从零搭建实战
2.3.1 引入核心依赖
新建SpringBoot项目,在pom.xml中引入JPA核心依赖、MySQL驱动,JDK9及以上需补充jaxb依赖,完整依赖如下:
xml
<dependencies>
<!-- SpringData JPA 核心起步依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- MySQL8.0驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
<scope>runtime</scope>
</dependency>
<!-- JDK9+ 必备jaxb依赖 -->
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
</dependencies>
2.3.2 全局配置文件配置
在application.yml中配置数据源、JPA参数,实现自动建表、SQL打印、字段映射适配,完整配置如下:
yaml
spring:
# 数据源配置
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/jpa_demo?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true
username: root
password: root
# JPA核心配置
jpa:
database: mysql
# 控制台打印执行的SQL语句
show-sql: true
# 开启自动建表
generate-ddl: true
hibernate:
# 自动更新表结构,新增字段不删除原有数据
ddl-auto: update
# 解决驼峰命名与数据库下划线字段不匹配问题
naming:
physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
2.3.3 创建业务实体类
以用户业务为例,创建User实体类,通过JPA注解完成表和字段映射:
java
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import java.util.Date;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "sys_user")
public class User {
// 主键自增
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// 用户名
@Column(name = "user_name", nullable = false, length = 32)
private String userName;
// 用户手机号
@Column(name = "phone", unique = true, length = 11)
private String phone;
// 用户年龄
@Column(name = "age")
private Integer age;
// 创建时间
@Column(name = "create_time")
private Date createTime;
// 用户状态 0-禁用 1-正常
@Column(name = "status")
private Integer status;
}
2.3.4 创建持久层Repository接口
自定义Repository接口,继承两大核心父接口,自动获得CRUD、复杂查询、分页排序能力:
java
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;
/**
* JPA持久层接口
* JpaRepository:提供基础CRUD、分页、排序
* JpaSpecificationExecutor:提供动态条件、复杂查询
*/
@Repository
public interface UserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> {
// 后续自定义查询方法
}
2.3.5 基础CRUD测试
编写单元测试类,测试新增、修改、删除、查询等基础操作:
java
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Date;
import java.util.Optional;
@SpringBootTest
public class UserJpaTest {
@Autowired
private UserRepository userRepository;
// 新增用户
@Test
void testSaveUser(){
User user = new User();
user.setUserName("张三");
user.setPhone("13800138000");
user.setAge(25);
user.setCreateTime(new Date());
user.setStatus(1);
// save方法:主键不存在则新增,存在则修改
userRepository.save(user);
}
// 根据ID查询用户
@Test
void testGetUserById(){
Optional<User> userOptional = userRepository.findById(1L);
// 优雅获取数据,避免空指针
userOptional.ifPresent(user -> System.out.println("查询用户:" + user));
}
// 查询所有用户
@Test
void testListUser(){
userRepository.findAll().forEach(System.out::println);
}
// 删除用户
@Test
void testDeleteUser(){
userRepository.deleteById(1L);
}
}
2.4 Spring Data JPA 底层执行原理
SpringData JPA无需编写接口实现类,核心依托AOP动态代理机制实现,完整执行链路如下:
-
程序启动时,Spring扫描所有继承JpaRepository的自定义接口
-
通过JdkDynamicAopProxy动态生成接口的代理对象,底层实现类为SimpleJpaRepository
-
SimpleJpaRepository封装了所有CRUD方法,内部调用JPA核心API------EntityManager
-
EntityManager的操作由Hibernate实现,Hibernate最终调用底层JDBC完成数据库交互
核心核心逻辑:自定义Repository接口 → 动态代理 → SimpleJpaRepository实现类 → EntityManager → Hibernate → JDBC → 数据库
2.5 五大查询方式实战
2.5.1 自带API查询
JpaRepository内置大量通用查询方法,无需自定义,直接调用即可:
java
@Test
void testBaseQuery(){
// 统计总记录数
long count = userRepository.count();
System.out.println("用户总数:" + count);
// 判断ID是否存在
boolean exists = userRepository.existsById(2L);
System.out.println("用户是否存在:" + exists);
// 延迟加载查询(需事务支持)
@Transactional
User user = userRepository.getOne(2L);
System.out.println(user.getUserName());
}
2.5.2 JPQL查询(面向对象查询)
JPQL是JPA专属查询语句,语法类似SQL,但是基于实体类和属性查询,非数据库表和字段,通过@Query注解实现:
在UserRepository中自定义方法:
java
// 根据用户名和状态查询用户
@Query("select u from User u where u.userName = ?1 and u.status = ?2")
List<User> findUserByUserNameAndStatus(String userName, Integer status);
// 修改用户状态(增删改需加@Modifying)
@Modifying
@Query("update User u set u.status = ?2 where u.id = ?1")
int updateUserStatus(Long id, Integer status);
测试方法:
java
@Test
@Transactional
@Rollback(false) // 关闭自动回滚,保证数据生效
void testJpql(){
// 查询测试
List<User> userList = userRepository.findUserByUserNameAndStatus("张三",1);
userList.forEach(System.out::println);
// 修改测试
int rows = userRepository.updateUserStatus(2L,0);
System.out.println("修改行数:" + rows);
}
2.5.3 原生SQL查询
复杂场景可直接使用原生SQL,设置nativeQuery=true即可:
java
// 原生SQL根据手机号查询用户
@Query(value = "select * from sys_user where phone = ?1",nativeQuery = true)
User findUserByPhone(String phone);
2.5.4 方法命名规则查询
SpringData JPA支持通过方法名自动生成查询SQL,无需注解,遵循固定命名规则(findBy+属性+条件关键字),常用规则实战:
java
// 根据年龄区间查询
List<User> findByAgeBetween(Integer minAge, Integer maxAge);
// 根据用户名模糊查询
List<User> findByUserNameLike(String userName);
// 查询状态正常且年龄大于20的用户
List<User> findByStatusAndAgeGreaterThan(Integer status, Integer age);
// 根据手机号为空查询
List<User> findByPhoneIsNull();
2.5.5 动态条件查询(JpaSpecificationExecutor)
适用于多条件动态组合查询(条件不确定、动态拼接),通过Specification构建查询条件,支持分页、排序:
java
@Test
void testSpecQuery(){
// 1.构建动态条件
Specification<User> spec = (root, query, cb) -> {
List<Predicate> predicateList = new ArrayList<>();
// 拼接状态条件:状态为正常
predicateList.add(cb.equal(root.get("status"),1));
// 拼接年龄条件:年龄大于20
predicateList.add(cb.greaterThan(root.get("age"),20));
return cb.and(predicateList.toArray(new Predicate[0]));
};
// 2.构建分页参数:第0页,每页5条
Pageable pageable = PageRequest.of(0,5);
// 3.执行分页查询
Page<User> page = userRepository.findAll(spec,pageable);
System.out.println("总条数:" + page.getTotalElements());
System.out.println("总页数:" + page.getTotalPages());
page.getContent().forEach(System.out::println);
}
2.6 DDD领域驱动设计实战
传统数据驱动开发需单独操作每张表,代码冗余、业务耦合度高;SpringData JPA完美适配DDD领域驱动设计,通过聚合根统一管理关联实体,一次性完成多表操作。
以「订单-订单详情」一对多业务为例实战:
2.6.1 聚合实体类创建
Order为聚合根,统一管理OrderDetail订单详情:
java
// 订单聚合根实体
@Data
@Entity
@Table(name = "biz_order")
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String orderNo; // 订单编号
private String receiverPhone; // 收件手机号
private Date createTime;
// 一对多关联订单详情,级联新增
@OneToMany(cascade = CascadeType.PERSIST)
@JoinColumn(name = "order_id")
private List<OrderDetail> detailList = new ArrayList<>();
}
// 订单详情实体
@Data
@Entity
@Table(name = "biz_order_detail")
public class OrderDetail {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long productId; // 商品ID
private Integer productNum; // 购买数量
private Double productPrice; // 商品单价
}
2.6.2 聚合根持久层接口
java
@Repository
public interface OrderRepository extends JpaRepository<Order,Long> {
}
2.6.3 聚合业务实操
仅操作聚合根,即可一次性保存订单和订单详情,无需单独操作详情表:
java
@Test
void testDDDSave(){
// 1.创建订单主表数据
Order order = new Order();
order.setOrderNo("ORD20260604001");
order.setReceiverPhone("13900139000");
order.setCreateTime(new Date());
// 2.创建订单详情数据
OrderDetail detail1 = new OrderDetail();
detail1.setProductId(1001L);
detail1.setProductNum(2);
detail1.setProductPrice(99.9);
OrderDetail detail2 = new OrderDetail();
detail2.setProductId(1002L);
detail2.setProductNum(1);
detail2.setProductPrice(199.9);
// 3.关联聚合数据
order.getDetailList().add(detail1);
order.getDetailList().add(detail2);
// 4.仅保存聚合根,自动级联保存详情
orderRepository.save(order);
}
三、Spring Data JDBC 实战详解
3.1 核心概念与定位
Spring Data JDBC是SpringData推出的轻量级ORM框架,相较于JPA更简洁、轻量化,舍弃了JPA的缓存、延迟加载、复杂关联等重型功能,专注于简单、高性能的数据库操作,同时支持集成MyBatis,兼顾简洁性与灵活性。
核心特点:无缓存、无延迟加载、上手简单、执行效率高、支持原生SQL、可集成MyBatis拓展功能。
3.2 项目搭建实战
3.2.1 核心依赖引入
xml
<dependencies>
<!-- SpringData JDBC核心依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
<scope>runtime</scope>
</dependency>
</dependencies>
3.2.2 数据源配置
配置方式与JPA一致,无需额外JPA相关配置:
yaml
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/jdbc_demo?serverTimezone=GMT%2B8&useUnicode=true
username: root
password: root
3.2.3 实体类创建
Spring Data JDBC实体类不支持@Entity、@GeneratedValue等JPA注解,仅需普通实体映射字段:
java
import lombok.Data;
@Data
public class Student {
private Integer id;
private String studentName;
private String studentNo;
private Integer gender;
private String className;
}
3.2.4 持久层接口创建
继承CrudRepository(基础CRUD)、PagingAndSortingRepository(分页排序):
java
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface StudentRepository extends CrudRepository<Student,Integer>, PagingAndSortingRepository<Student,Integer> {
// 命名规则查询:根据班级查询学生
List<Student> findByClassName(String className);
}
3.3 双模式查询实战
3.3.1 Repository 接口查询
依托继承的接口实现零代码CRUD、分页、命名查询:
java
@Test
void testRepositoryQuery(){
// 新增
Student student = new Student();
student.setStudentName("李四");
student.setStudentNo("2026060401");
student.setGender(1);
student.setClassName("高三一班");
studentRepository.save(student);
// 分页查询
Pageable pageable = PageRequest.of(0,3);
Page<Student> page = studentRepository.findAll(pageable);
page.getContent().forEach(System.out::println);
// 自定义命名查询
List<Student> studentList = studentRepository.findByClassName("高三一班");
studentList.forEach(System.out::println);
}
3.3.2 JdbcTemplate 原生查询
Spring自动注入JdbcTemplate工具类,用于复杂SQL场景,灵活可控:
java
@Autowired
private JdbcTemplate jdbcTemplate;
// 查询单行数据
@Test
void testQuerySingle(){
String sql = "select * from student where id = ?";
Student student = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Student.class),1);
System.out.println(student);
}
// 查询多行数据
@Test
void testQueryList(){
String sql = "select * from student where class_name = ?";
List<Student> studentList = jdbcTemplate.query(sql,new BeanPropertyRowMapper<>(Student.class),"高三一班");
studentList.forEach(System.out::println);
}
// 执行更新
@Test
void testUpdate(){
String sql = "update student set student_name = ? where id = ?";
int rows = jdbcTemplate.update(sql,"李小明",1);
System.out.println("修改行数:" + rows);
}
3.4 Spring Data JDBC 集成 MyBatis 实战
JDBC模块原生功能简单,可集成MyBatis拓展缓存、复杂SQL能力,完整集成步骤如下:
3.4.1 引入MyBatis依赖
xml
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
3.4.2 定义拓展查询接口
java
public interface StudentExtRepository {
// 自定义复杂查询:查询所有男生
List<Student> listMaleStudent();
}
3.4.3 编写MyBatis映射文件
在resources/mapper下创建StudentMapper.xml:
xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://www.mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.demo.repository.StudentExtRepository">
<select id="listMaleStudent" resultType="com.demo.entity.Student">
select * from student where gender = 1
</select>
</mapper>
3.4.4 实现拓展接口
java
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public class StudentExtRepositoryImpl implements StudentExtRepository {
@Autowired
private SqlSessionTemplate sqlSessionTemplate;
@Override
public List<Student> listMaleStudent() {
return sqlSessionTemplate.selectList(StudentExtRepository.class.getName() + ".listMaleStudent");
}
}
3.4.5 主接口继承拓展接口
java
@Repository
public interface StudentRepository extends CrudRepository<Student,Integer>,
PagingAndSortingRepository<Student,Integer>, StudentExtRepository {
}
3.4.6 配置Mapper扫描
yaml
mybatis:
mapper-locations: classpath:mapper/*.xml
configuration:
map-underscore-to-camel-case: true
3.5 JDBC与JPA核心对比
| 对比维度 | Spring Data JPA | Spring Data JDBC |
|---|---|---|
| 框架重量 | 重型框架,依赖Hibernate | 轻量框架,无额外依赖 |
| 核心功能 | 支持缓存、延迟加载、JPQL、级联操作 | 无缓存、无延迟加载、仅基础CRUD |
| SQL灵活性 | 复杂SQL支持较弱 | 支持JdbcTemplate,可手写任意SQL |
| 拓展能力 | 原生功能齐全,无需拓展 | 可集成MyBatis拓展功能 |
| 适用场景 | 业务复杂、多表关联、快速开发 | 简单单表操作、高性能查询场景 |
四、Spring Data Redis 实战详解
4.1 核心概念
Redis是高性能内存键值数据库,支持String、Hash、List、Set、ZSet五大核心数据类型,常用于热点数据缓存、分布式锁、限流、计数器等场景。Spring Data Redis是Spring对Redis原生客户端(Jedis、Lettuce)的高度封装,提供统一、简洁的API操作Redis。
4.2 项目搭建与基础配置
4.2.1 引入依赖
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
4.2.2 全局配置
yaml
spring:
redis:
# Redis服务地址
host: localhost
# Redis端口
port: 6379
# 连接池配置
jedis:
pool:
max-active: 10 # 最大活跃连接
max-idle: 8 # 最大空闲连接
min-idle: 2 # 最小空闲连接
max-wait: 1000 # 最大等待时间
# 超时时间
timeout: 3000ms
4.3 序列化器配置(核心优化)
SpringData Redis默认使用JDK序列化,存储数据为二进制乱码,可读性极差。实战中统一配置JSON序列化器,实现数据可视化存储:
java
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory factory){
RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
// String类型key序列化
StringRedisSerializer stringSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringSerializer);
redisTemplate.setHashKeySerializer(stringSerializer);
// JSON类型value序列化
Jackson2JsonRedisSerializer<Object> jsonSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
jsonSerializer.setObjectMapper(mapper);
redisTemplate.setValueSerializer(jsonSerializer);
redisTemplate.setHashValueSerializer(jsonSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
4.4 五大数据类型完整实战
4.4.1 String 字符串操作
java
@Autowired
private RedisTemplate<String,Object> redisTemplate;
@Test
void testStringOperate(){
ValueOperations<String,Object> valueOps = redisTemplate.opsForValue();
// 普通存储
valueOps.set("project_name","SpringData实战教程");
// 过期存储(10秒过期)
valueOps.set("temp_code","123456",10, TimeUnit.SECONDS);
// 自增计数器
valueOps.increment("view_count",1);
// 批量存储
Map<String,Object> map = new HashMap<>();
map.put("author","java高级");
map.put("level","高级教程");
valueOps.multiSet(map);
// 数据获取
System.out.println(valueOps.get("project_name"));
System.out.println(valueOps.get("view_count"));
}
4.4.2 Hash 哈希操作
适合存储对象数据,无需序列化整存整取,支持单独修改字段:
java
@Test
void testHashOperate(){
HashOperations<String,String,Object> hashOps = redisTemplate.opsForHash();
// 存储用户信息
hashOps.put("user:info:1001","name","王五");
hashOps.put("user:info:1001","age",28);
hashOps.put("user:info:1001","phone","13700137000");
// 获取单个字段
System.out.println(hashOps.get("user:info:1001","name"));
// 获取所有字段和值
System.out.println(hashOps.entries("user:info:1001"));
// 删除单个字段
hashOps.delete("user:info:1001","phone");
}
4.4.3 List 列表操作
有序可重复列表,适合消息队列、时间线数据存储:
java
@Test
void testListOperate(){
ListOperations<String,Object> listOps = redisTemplate.opsForList();
// 左侧插入
listOps.leftPush("news:list","头条新闻1");
listOps.leftPushAll("news:list","头条新闻2","头条新闻3");
// 右侧插入
listOps.rightPush("news:list","最新资讯");
// 范围查询
List<Object> newsList = listOps.range("news:list",0,-1);
System.out.println(newsList);
// 左侧弹出
System.out.println(listOps.leftPop("news:list"));
}
4.4.4 Set 集合操作
无序不可重复集合,适合去重、交集、并集场景(好友、点赞用户):
java
@Test
void testSetOperate(){
SetOperations<String,Object> setOps = redisTemplate.opsForSet();
// 添加数据
setOps.add("user:like:1001","1002","1003","1004");
setOps.add("user:like:1002","1003","1005","1006");
// 交集(共同点赞用户)
Set<Object> intersect = setOps.intersect("user:like:1001", "user:like:1002");
System.out.println("共同好友:" + intersect);
// 随机获取
System.out.println(setOps.randomMember("user:like:1001"));
}
4.4.5 ZSet 有序集合操作
带权重有序集合,适合排行榜、积分排序场景:
java
@Test
void testZSetOperate(){
ZSetOperations<String,Object> zSetOps = redisTemplate.opsForZSet();
// 添加排行榜数据(value、score积分)
zSetOps.add("rank:list","张三",98);
zSetOps.add("rank:list","李四",95);
zSetOps.add("rank:list","王五",100);
// 正序查询(分数从低到高)
Set<Object> rankList = zSetOps.range("rank:list",0,-1);
System.out.println("排行榜:" + rankList);
// 加分
zSetOps.incrementScore("rank:list","李四",3);
}
五、Spring Data ElasticSearch 实战详解
5.1 ES核心概念与对比
ElasticSearch是分布式全文检索引擎,底层基于Lucene实现,支持海量数据快速检索、模糊查询、分词检索,是企业主流搜索引擎方案。Spring Data ElasticSearch简化ES原生繁琐的REST API操作,提供Repository简化开发。
ES7.X核心概念与MySQL映射关系:
-
索引(Index) ≈ MySQL数据表(Table)
-
文档(Document) ≈ MySQL数据行(Row)
-
字段(Field) ≈ MySQL字段(Column)
5.2 环境准备与项目搭建
本地启动ES、Kibana服务,默认端口9200、5601,开启跨域配置保证访问正常。
5.2.1 引入依赖
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
5.2.2 服务配置
yaml
spring:
elasticsearch:
rest:
uris: http://localhost:9200
5.2.3 创建ES文档实体
配置索引、字段类型、分词器,实现文档映射:
java
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import lombok.Data;
@Data
@Document(indexName = "goods_index",shards = 3,replicas = 1)
public class GoodsDoc {
// 文档主键
@Field(type = FieldType.Long)
private Long id;
// 商品名称(分词检索)
@Field(type = FieldType.Text,analyzer = "ik_max_word",searchAnalyzer = "ik_smart")
private String goodsName;
// 商品描述
@Field(type = FieldType.Text,analyzer = "ik_max_word")
private String goodsDesc;
// 商品价格(不分词)
@Field(type = FieldType.Double,index = false)
private Double price;
// 商品分类
@Field(type = FieldType.Keyword)
private String category;
}
5.2.4 持久层接口
java
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface GoodsEsRepository extends ElasticsearchRepository<GoodsDoc,Long> {
// 模糊分词查询
List<GoodsDoc> findByGoodsNameLike(String goodsName);
}
5.3 核心查询实战
5.3.1 基础CRUD与分页查询
java
@Test
void testEsBaseOperate(){
// 新增文档
GoodsDoc goods = new GoodsDoc();
goods.setId(1001L);
goods.setGoodsName("华为Mate60智能手机");
goods.setGoodsDesc("全网通5G、鸿蒙系统、超长续航");
goods.setPrice(4999.9);
goods.setCategory("手机数码");
goodsEsRepository.save(goods);
// 分页查询
Pageable pageable = PageRequest.of(0,5);
Page<GoodsDoc> page = goodsEsRepository.findAll(pageable);
page.getContent().forEach(System.out::println);
}
5.3.2 原生复杂条件查询(Template)
java
@Autowired
private ElasticsearchRestTemplate esTemplate;
@Test
void testEsComplexQuery(){
// 构建布尔查询
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
// 精准匹配分类
boolQuery.filter(QueryBuilders.termQuery("category","手机数码"));
// 模糊匹配商品名称
boolQuery.must(QueryBuilders.fuzzyQuery("goodsName","华为").maxExpansions(2));
// 价格区间
boolQuery.filter(QueryBuilders.rangeQuery("price").gte(3000).lte(6000));
// 分页+排序
Pageable pageable = PageRequest.of(0,3, Sort.by(Sort.Direction.DESC,"price"));
NativeSearchQuery query = new NativeSearchQueryBuilder()
.withQuery(boolQuery)
.withPageable(pageable)
.build();
// 执行查询
SearchHits<GoodsDoc> searchHits = esTemplate.search(query, GoodsDoc.class);
searchHits.forEach(hit -> System.out.println(hit.getContent()));
}
六、Spring Data MongoDB 实战详解
6.1 核心概念与应用场景
MongoDB是一款开源的分布式文档型NoSQL数据库,数据存储格式为BSON(类JSON格式),无需预先定义表结构,支持动态增减字段,具备高可用、高并发、海量存储的特性。
Spring Data MongoDB是Spring生态针对MongoDB专门封装的持久化框架,延续SpringData统一的Repository编程模型,彻底屏蔽MongoDB原生复杂的API调用,实现零冗余代码开发。
核心适用场景:存储非结构化/半结构化数据、用户行为日志、商品评论、动态表单数据、海量低价值业务数据,不适合金融交易、订单对账等强事务性业务场景。
MongoDB与MySQL核心概念映射:
-
数据库(Database) ≈ MySQL数据库
-
集合(Collection) ≈ MySQL数据表
-
文档(Document) ≈ MySQL数据行
-
字段(Field) ≈ MySQL列字段
6.2 项目搭建与环境配置
6.2.1 引入核心依赖
在现有SpringBoot项目中引入MongoDB专用starter依赖,无需额外驱动包,开箱即用:
xml
<!-- Spring Data MongoDB 核心依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
6.2.2 全局配置文件
配置MongoDB服务地址、数据库名称、连接超时、连接池参数,适配本地及线上环境:
yaml
spring:
data:
mongodb:
# MongoDB连接地址与数据库名
uri: mongodb://localhost:27017/mongodb_demo
# 连接超时时间
timeout: 5000ms
# 连接池配置
pool:
max-size: 15
min-size: 3
max-wait: 2000ms
6.3 文档实体类创建
MongoDB无固定表结构,通过注解绑定集合、设置主键、字段映射,支持动态字段扩展。以用户评论业务为例创建实体类:
java
import lombok.Data;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.data.mongodb.core.mapping.MongoId;
import java.util.Date;
/**
* 商品评论文档实体
* 注解Document:绑定MongoDB集合名称
*/
@Data
@Document(collection = "goods_comment")
public class GoodsComment {
// MongoDB主键ID(自动生成)
@MongoId
private String id;
// 商品ID
@Field("goods_id")
private Long goodsId;
// 评论用户ID
@Field("user_id")
private Long userId;
// 评论内容
@Field("comment_content")
private String commentContent;
// 评论星级 1-5星
@Field("star_level")
private Integer starLevel;
// 评论时间
@Field("create_time")
private Date createTime;
// 是否置顶 0-否 1-是
@Field("is_top")
private Integer isTop;
// 动态扩展字段:评论配图地址
@Field("img_url")
private String imgUrl;
}
6.4 持久层接口定义
延续SpringData统一规范,继承MongoRepository接口,自动获取CRUD、分页、排序、基础条件查询能力:
java
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface GoodsCommentRepository extends MongoRepository<GoodsComment,String> {
// 自定义命名查询:根据商品ID查询所有置顶评论
List<GoodsComment> findByGoodsIdAndIsTop(Long goodsId, Integer isTop);
// 根据星级区间查询评论
List<GoodsComment> findByStarLevelBetween(Integer minStar, Integer maxStar);
}
6.5 全套CRUD实战开发
6.5.1 基础新增、修改、删除操作
MongoDB新增数据无需提前建表,集合不存在时框架自动创建;save方法同样适配新增/修改逻辑,根据主键ID判断执行逻辑:
java
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Date;
import java.util.Optional;
@SpringBootTest
public class MongoCommentTest {
@Autowired
private GoodsCommentRepository commentRepository;
// 新增评论数据
@Test
void testInsertComment(){
GoodsComment comment = new GoodsComment();
comment.setGoodsId(1001L);
comment.setUserId(10001L);
comment.setCommentContent("手机续航超强,拍照效果非常清晰,性价比很高");
comment.setStarLevel(5);
comment.setCreateTime(new Date());
comment.setIsTop(1);
comment.setImgUrl("https://demo-img.com/phone001.jpg");
// 保存数据
commentRepository.save(comment);
}
// 根据ID修改评论内容
@Test
void testUpdateComment(){
Optional<GoodsComment> optional = commentRepository.findById("填入生成的主键ID");
if(optional.isPresent()){
GoodsComment comment = optional.get();
comment.setCommentContent("手机续航超强,拍照清晰,系统流畅,非常推荐!");
comment.setStarLevel(5);
commentRepository.save(comment);
}
}
// 根据ID删除评论
@Test
void testDeleteComment(){
commentRepository.deleteById("填入生成的主键ID");
}
}
6.5.2 基础查询与分页排序查询
支持自带API查询、自定义命名查询、分页排序查询,适配日常业务检索场景:
java
// 基础查询
@Test
void testBaseSelect(){
// 查询所有数据
commentRepository.findAll().forEach(System.out::println);
// 根据商品ID查询置顶评论
List<GoodsComment> topCommentList = commentRepository.findByGoodsIdAndIsTop(1001L,1);
topCommentList.forEach(System.out::println);
// 查询4-5星高分评论
List<GoodsComment> starCommentList = commentRepository.findByStarLevelBetween(4,5);
starCommentList.forEach(System.out::println);
}
// 分页排序查询
@Test
void testPageSortSelect(){
// 分页参数:第0页,每页5条,按评论时间倒序排序
Pageable pageable = PageRequest.of(0,5, Sort.by(Sort.Direction.DESC,"createTime"));
Page<GoodsComment> page = commentRepository.findAll(pageable);
System.out.println("总数据量:" + page.getTotalElements());
System.out.println("总页数:" + page.getTotalPages());
System.out.println("当前页数据:");
page.getContent().forEach(System.out::println);
}
6.6 MongoTemplate 复杂动态查询实战
简单查询可通过Repository命名规则实现,复杂多条件动态拼接、模糊查询、字段筛选、分组统计场景,需使用MongoTemplate工具类实现,灵活性拉满:
java
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.beans.factory.annotation.Autowired;
import org.junit.jupiter.api.Test;
import java.util.List;
@Test
void testDynamicQuery(){
// 注入模板工具类
@Autowired
private MongoTemplate mongoTemplate;
// 构建动态条件
Query query = new Query();
Criteria criteria = new Criteria();
// 动态拼接条件:商品ID=1001、星级≥3星、评论内容包含"手机"
criteria.and("goods_id").is(1001L)
.and("star_level").gte(3)
.and("comment_content").regex("手机");
query.addCriteria(criteria);
// 分页设置
query.skip(0).limit(3);
// 执行查询
List<GoodsComment> commentList = mongoTemplate.find(query, GoodsComment.class);
commentList.forEach(System.out::println);
}
七、Spring Data Solr 基础了解
7.1 技术现状与核心定位
Apache Solr是一款开源全文检索引擎,早期广泛应用于企业搜索业务,目前官方迭代缓慢、社区活跃度极低,已逐步被ElasticSearch替代。Spring Data Solr是SpringData适配Solr的拓展模块,仅用于老旧项目维护,新项目一律推荐使用Spring Data ES。
7.2 核心特点与对比
-
Solr基于传统Lucene封装,侧重结构化数据检索,配置繁琐、集群部署复杂
-
ES原生支持分布式集群、实时检索、海量数据存储,适配现代互联网业务
-
Spring Data Solr用法与ES、JPA高度一致,同样基于Repository接口开发,学习成本极低
企业开发规范:新项目禁止使用Solr,仅老旧历史项目维护时使用,本章不做深度实战讲解。
八、SpringData 企业级综合实战案例
8.1 业务场景架构设计
结合本章所有知识点,搭建电商分层数据存储架构,整合多SpringData模块实现企业级业务落地,架构分工如下:
-
Spring Data JPA:操作MySQL,存储核心交易数据(订单、用户、商品基础数据),保证事务一致性
-
Spring Data Redis:缓存热点数据(用户信息、商品热度、访问计数器、排行榜),提升接口响应速度
-
Spring Data MongoDB:存储非结构化数据(商品评论、用户浏览日志)
-
Spring Data ElasticSearch:实现商品全文检索、模糊搜索、分类筛选功能
8.2 核心业务接口实战
实现「商品查询+缓存加速+全文检索+评论加载」完整业务链路,模拟企业真实开发逻辑:
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.concurrent.TimeUnit;
@Service
public class GoodsBusinessService {
// JPA持久层(MySQL商品数据)
@Autowired
private GoodsRepository goodsRepository;
// ES持久层(商品检索)
@Autowired
private GoodsEsRepository goodsEsRepository;
// MongoDB持久层(商品评论)
@Autowired
private GoodsCommentRepository commentRepository;
// Redis模板(缓存操作)
@Autowired
private RedisTemplate<String,Object> redisTemplate;
/**
* 分页查询商品(缓存优先+数据库兜底)
*/
public Page<Goods> getGoodsPage(int pageNum, int pageSize){
String cacheKey = "goods:page:" + pageNum;
// 1.先查询缓存
Object cacheData = redisTemplate.opsForValue().get(cacheKey);
if(cacheData != null){
return (Page<Goods>) cacheData;
}
// 2.缓存不存在,查询MySQL数据库
PageRequest pageable = PageRequest.of(pageNum, pageSize);
Page<Goods> goodsPage = goodsRepository.findAll(pageable);
// 3.数据写入缓存,设置10分钟过期
redisTemplate.opsForValue().set(cacheKey,goodsPage,10, TimeUnit.MINUTES);
return goodsPage;
}
/**
* 商品全文检索(ES高性能检索)
*/
public List<GoodsDoc> searchGoods(String keyword){
// ES模糊分词检索
return goodsEsRepository.findByGoodsNameLike(keyword);
}
/**
* 获取商品高分评论
*/
public List<GoodsComment> getGoodsHighComment(Long goodsId){
// 查询4-5星高分评论
return commentRepository.findByStarLevelBetween(4,5);
}
}
九、SpringData 核心总结与选型规范
9.1 各模块核心选型标准
企业开发中需根据业务场景精准选择SpringData模块,避免技术滥用,核心选型规范如下:
-
核心业务、事务性数据 (订单、用户、支付):优先使用 Spring Data JPA,开发效率高、支持事务、适配多表关联
-
简单单表、高性能查询 :选择 Spring Data JDBC,轻量无冗余、执行速度快
-
热点缓存、计数器、排行榜 :固定使用 Spring Data Redis
-
非结构化、海量低价值数据 (日志、评论、动态):使用 Spring Data MongoDB
-
全文检索、模糊匹配、海量数据搜索 :使用 Spring Data ElasticSearch
-
老旧项目维护:仅存量项目使用Spring Data Solr,新项目禁用
9.2 通用开发规范
-
所有SpringData项目统一使用Repository接口编程,杜绝原生API硬编码,保证代码统一性
-
关系型数据库优先使用方法命名查询、注解查询,复杂SQL再使用原生语句
-
缓存、检索、文档数据库必须配置序列化、分词器等基础优化参数,适配生产环境
-
生产环境禁止使用DDL自动更新,仅开发测试环境启用,避免数据丢失风险