Spring Data JPA 完全指南

Spring Data JPA 完全指南

一、概述

Spring Data JPA 是 Spring Data 项目的核心模块之一,它在 JPA(Java Persistence API)之上提供了一层抽象,极大地简化了数据访问层的开发。开发者只需定义接口,框架自动生成实现,消除了大量样板代码。

核心价值:

  • 零实现类:只写接口,不写实现
  • 方法名即查询:按命名规则自动生成 SQL
  • 统一的 CRUD 抽象
  • 内置分页、排序、审计支持

二、核心概念

2.1 JPA 与 Spring Data JPA 的关系

复制代码
Hibernate (JPA 实现) 
    ↑
JPA 规范 (javax.persistence / jakarta.persistence)
    ↑
Spring Data JPA (在 JPA 之上的 Repository 抽象)
  • JPA 是规范(接口),定义了 ORM 的标准 API
  • Hibernate 是 JPA 的实现(最常用)
  • Spring Data JPA 是在 JPA 之上的便捷封装,提供 Repository 模式

2.2 Repository 继承体系

复制代码
Repository<T, ID>                    (标记接口,无任何方法)
  └── CrudRepository<T, ID>         (基础 CRUD:save/findById/delete/count)
       └── PagingAndSortingRepository<T, ID>  (分页 + 排序)
            └── JpaRepository<T, ID>          (JPA 特有:flush/批量操作/Example 查询)
接口 提供的能力
CrudRepository save、saveAll、findById、findAll、count、delete、existsById
PagingAndSortingRepository findAll(Sort)、findAll(Pageable)
JpaRepository flush、saveAndFlush、deleteInBatch、findAll(Example)

三、实体映射

3.1 基础注解

java 复制代码
@Entity
@Table(name = "t_user")
public class UserEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    /** 用户名 */
    @Column(name = "user_name", nullable = false, length = 50)
    private String userName;

    /** 邮箱 */
    @Column(name = "email", unique = true)
    private String email;

    /** 是否激活 */
    @Column(name = "is_active")
    private Boolean active;

    /** 创建时间 */
    @Column(name = "created_at", updatable = false)
    private LocalDateTime createdAt;

    /** 更新时间 */
    @Column(name = "updated_at")
    private LocalDateTime updatedAt;
}

3.2 常用注解说明

注解 作用
@Entity 标记为 JPA 实体
@Table 指定表名
@Id 主键
@GeneratedValue 主键生成策略(IDENTITY/SEQUENCE/TABLE/AUTO)
@Column 列映射(名称、长度、是否可空等)
@Transient 不映射到数据库
@Enumerated 枚举映射(ORDINAL/STRING)
@Temporal 日期类型映射
@Lob 大字段(BLOB/CLOB)

3.3 关联关系

java 复制代码
// 一对多
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<OrderEntity> orders;

// 多对一
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private UserEntity user;

// 多对多
@ManyToMany
@JoinTable(
    name = "t_user_role",
    joinColumns = @JoinColumn(name = "user_id"),
    inverseJoinColumns = @JoinColumn(name = "role_id")
)
private Set<RoleEntity> roles;

// 一对一
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "profile_id")
private UserProfileEntity profile;

四、查询方法派生(Query Derivation)

4.1 命名规则

Spring Data JPA 解析方法名,自动生成查询:

java 复制代码
public interface UserRepository extends JpaRepository<UserEntity, Long> {

    // WHERE user_name = ?
    UserEntity findByUserName(String userName);

    // WHERE email = ? AND is_active = ?
    List<UserEntity> findByEmailAndActive(String email, Boolean active);

    // WHERE user_name LIKE ?
    List<UserEntity> findByUserNameLike(String pattern);

    // WHERE user_name LIKE '%xxx%'
    List<UserEntity> findByUserNameContaining(String keyword);

    // WHERE age BETWEEN ? AND ?
    List<UserEntity> findByAgeBetween(Integer min, Integer max);

    // WHERE age > ? ORDER BY created_at DESC
    List<UserEntity> findByAgeGreaterThanOrderByCreatedAtDesc(Integer age);

    // WHERE is_active = true (取前3条)
    List<UserEntity> findTop3ByActiveTrue();

    // 统计
    Long countByActive(Boolean active);

    // 是否存在
    Boolean existsByEmail(String email);

    // 删除
    void deleteByUserName(String userName);
}

4.2 完整关键字对照表

关键字 方法名示例 JPQL 等价
And findByNameAndAge WHERE name=? AND age=?
Or findByNameOrAge WHERE name=? OR age=?
Is/Equals findByName / findByNameIs WHERE name=?
Between findByAgeBetween WHERE age BETWEEN ? AND ?
LessThan findByAgeLessThan WHERE age < ?
LessThanEqual findByAgeLessThanEqual WHERE age <= ?
GreaterThan findByAgeGreaterThan WHERE age > ?
GreaterThanEqual findByAgeGreaterThanEqual WHERE age >= ?
After findByCreatedAtAfter WHERE created_at > ?
Before findByCreatedAtBefore WHERE created_at < ?
IsNull findByEmailIsNull WHERE email IS NULL
IsNotNull/NotNull findByEmailIsNotNull WHERE email IS NOT NULL
Like findByNameLike WHERE name LIKE ?
NotLike findByNameNotLike WHERE name NOT LIKE ?
StartingWith findByNameStartingWith WHERE name LIKE 'xxx%'
EndingWith findByNameEndingWith WHERE name LIKE '%xxx'
Containing findByNameContaining WHERE name LIKE '%xxx%'
OrderBy findByAgeOrderByNameDesc ... ORDER BY name DESC
Not findByNameNot WHERE name <> ?
In findByAgeIn(Collection) WHERE age IN (...)
NotIn findByAgeNotIn(Collection) WHERE age NOT IN (...)
True findByActiveTrue WHERE active = true
False findByActiveFalse WHERE active = false
IgnoreCase findByNameIgnoreCase WHERE UPPER(name)=UPPER(?)
Top/First findTop10By... LIMIT 10
Distinct findDistinctByName SELECT DISTINCT ...

五、自定义查询

5.1 @Query 注解(JPQL)

java 复制代码
public interface UserRepository extends JpaRepository<UserEntity, Long> {

    // JPQL 查询
    @Query("SELECT u FROM UserEntity u WHERE u.email = :email")
    UserEntity findByEmailAddress(@Param("email") String email);

    // JPQL 带分页
    @Query("SELECT u FROM UserEntity u WHERE u.active = true")
    Page<UserEntity> findActiveUsers(Pageable pageable);

    // 原生 SQL
    @Query(value = "SELECT * FROM t_user WHERE user_name = ?1", nativeQuery = true)
    UserEntity findByUserNameNative(String userName);

    // 更新操作(需要 @Modifying)
    @Modifying
    @Query("UPDATE UserEntity u SET u.active = :active WHERE u.id = :id")
    int updateActiveStatus(@Param("id") Long id, @Param("active") Boolean active);

    // 删除操作
    @Modifying
    @Query("DELETE FROM UserEntity u WHERE u.active = false")
    int deleteInactiveUsers();
}

5.2 @Modifying 注意事项

  • 必须配合 @Transactional 使用
  • 默认不会清除持久化上下文,可加 @Modifying(clearAutomatically = true) 自动清除
  • 返回值为 int(受影响行数)或 void

六、分页与排序

6.1 分页查询

java 复制代码
// Repository 方法
Page<UserEntity> findByActive(Boolean active, Pageable pageable);

// 调用方式
Pageable pageable = PageRequest.of(0, 10, Sort.by("createdAt").descending());
Page<UserEntity> page = userRepository.findByActive(true, pageable);

// Page 对象包含的信息
page.getContent();        // 当前页数据列表
page.getTotalElements();  // 总记录数
page.getTotalPages();     // 总页数
page.getNumber();         // 当前页码(从0开始)
page.getSize();           // 每页大小
page.hasNext();           // 是否有下一页
page.hasPrevious();       // 是否有上一页

6.2 Slice(轻量分页,不查总数)

java 复制代码
Slice<UserEntity> findByActive(Boolean active, Pageable pageable);

Slice 不执行 COUNT 查询,性能更好,适合"加载更多"场景。

6.3 排序

java 复制代码
// 单字段排序
Sort sort = Sort.by("createdAt").descending();

// 多字段排序
Sort sort = Sort.by(
    Sort.Order.desc("createdAt"),
    Sort.Order.asc("userName")
);

List<UserEntity> users = userRepository.findAll(sort);

七、Specification 动态查询

适合复杂的动态条件组合查询,Repository 需继承 JpaSpecificationExecutor

java 复制代码
public interface UserRepository extends JpaRepository<UserEntity, Long>,
                                        JpaSpecificationExecutor<UserEntity> {
}

7.1 构建 Specification

java 复制代码
public class UserSpecification {

    /** 根据用户名模糊查询 */
    public static Specification<UserEntity> userNameLike(String userName) {
        return (root, query, cb) -> {
            if (userName == null || userName.isEmpty()) {
                return cb.conjunction();
            }
            return cb.like(root.get("userName"), "%" + userName + "%");
        };
    }

    /** 根据激活状态查询 */
    public static Specification<UserEntity> isActive(Boolean active) {
        return (root, query, cb) -> {
            if (active == null) {
                return cb.conjunction();
            }
            return cb.equal(root.get("active"), active);
        };
    }

    /** 根据创建时间范围查询 */
    public static Specification<UserEntity> createdBetween(LocalDateTime start, LocalDateTime end) {
        return (root, query, cb) -> cb.between(root.get("createdAt"), start, end);
    }
}

7.2 组合使用

java 复制代码
// 动态组合条件
Specification<UserEntity> spec = Specification
    .where(UserSpecification.userNameLike("张"))
    .and(UserSpecification.isActive(true))
    .and(UserSpecification.createdBetween(startTime, endTime));

Page<UserEntity> result = userRepository.findAll(spec, pageable);

八、审计功能(Auditing)

自动填充创建时间、修改时间、创建人、修改人。

8.1 启用审计

java 复制代码
@Configuration
@EnableJpaAuditing
public class JpaAuditConfig {

    @Bean
    public AuditorAware<String> auditorProvider() {
        // 从安全上下文获取当前用户
        return () -> Optional.ofNullable(SecurityContextHolder.getContext())
                .map(SecurityContext::getAuthentication)
                .map(Authentication::getName);
    }
}

8.2 审计基类

java 复制代码
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseEntity {

    @CreatedDate
    @Column(name = "created_at", updatable = false)
    private LocalDateTime createdAt;

    @LastModifiedDate
    @Column(name = "updated_at")
    private LocalDateTime updatedAt;

    @CreatedBy
    @Column(name = "created_by", updatable = false)
    private String createdBy;

    @LastModifiedBy
    @Column(name = "updated_by")
    private String updatedBy;
}

九、投影(Projection)

只查询部分字段,避免加载整个实体。

9.1 接口投影(Interface Projection)

java 复制代码
// 定义投影接口
public interface UserSummaryVo {
    String getUserName();
    String getEmail();
}

// Repository 方法
List<UserSummaryVo> findByActive(Boolean active);

9.2 类投影(Class Projection / DTO Projection)

java 复制代码
public class UserDto {
    private String userName;
    private String email;

    // 构造函数必须与查询字段对应
    public UserDto(String userName, String email) {
        this.userName = userName;
        this.email = email;
    }
}

// 使用 JPQL 构造
@Query("SELECT new com.example.dto.UserDto(u.userName, u.email) FROM UserEntity u WHERE u.active = true")
List<UserDto> findActiveUserSummaries();

十、乐观锁与软删除

10.1 乐观锁

java 复制代码
@Entity
public class ProductEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String productName;

    private Integer stock;

    /** 版本号,用于乐观锁 */
    @Version
    private Integer version;
}

更新时 Hibernate 自动在 WHERE 条件中加入 version = ?,并发冲突时抛出 OptimisticLockException

10.2 软删除

java 复制代码
@Entity
@Where(clause = "is_deleted = 0")
@SQLDelete(sql = "UPDATE t_user SET is_deleted = 1 WHERE id = ?")
public class UserEntity {

    @Column(name = "is_deleted")
    private Boolean deleted = false;
}

十一、自定义 Repository 实现

当方法名派生和 @Query 都无法满足需求时,可以自定义实现。

java 复制代码
// 1. 定义自定义接口
public interface UserRepositoryCustom {
    List<UserEntity> findByComplexCondition(String keyword, Integer minAge);
}

// 2. 实现类(命名必须为 Repository名 + Impl)
public class UserRepositoryImpl implements UserRepositoryCustom {

    @PersistenceContext
    private EntityManager entityManager;

    @Override
    public List<UserEntity> findByComplexCondition(String keyword, Integer minAge) {
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<UserEntity> query = cb.createQuery(UserEntity.class);
        Root<UserEntity> root = query.from(UserEntity.class);

        List<Predicate> predicates = new ArrayList<>();
        if (keyword != null) {
            predicates.add(cb.like(root.get("userName"), "%" + keyword + "%"));
        }
        if (minAge != null) {
            predicates.add(cb.greaterThanOrEqualTo(root.get("age"), minAge));
        }

        query.where(predicates.toArray(new Predicate[0]));
        return entityManager.createQuery(query).getResultList();
    }
}

// 3. 主 Repository 继承自定义接口
public interface UserRepository extends JpaRepository<UserEntity, Long>, UserRepositoryCustom {
}

十二、事务管理

java 复制代码
@Service
public class UserServiceImpl implements UserService {

    private final UserRepository userRepository;

    public UserServiceImpl(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    // 默认:运行时异常回滚
    @Transactional(rollbackFor = Exception.class)
    @Override
    public UserEntity createUser(UserEntity user) {
        return userRepository.save(user);
    }

    // 只读事务(优化性能,不加写锁)
    @Transactional(readOnly = true)
    @Override
    public UserEntity getUserById(Long id) {
        return userRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("用户不存在"));
    }
}

事务传播行为:

传播类型 说明
REQUIRED(默认) 有事务则加入,没有则新建
REQUIRES_NEW 总是新建事务,挂起当前事务
NESTED 嵌套事务(savepoint)
SUPPORTS 有事务则加入,没有则非事务执行
NOT_SUPPORTED 非事务执行,挂起当前事务
MANDATORY 必须在事务中调用,否则抛异常
NEVER 必须非事务调用,否则抛异常

十三、性能优化

13.1 N+1 问题解决

java 复制代码
// 使用 @EntityGraph 一次性加载关联
@EntityGraph(attributePaths = {"orders", "roles"})
List<UserEntity> findByActive(Boolean active);

// 或在 JPQL 中使用 JOIN FETCH
@Query("SELECT u FROM UserEntity u LEFT JOIN FETCH u.orders WHERE u.id = :id")
UserEntity findWithOrdersById(@Param("id") Long id);

13.2 批量操作

java 复制代码
// 批量插入(需配置 hibernate.jdbc.batch_size)
@Transactional(rollbackFor = Exception.class)
public void batchSave(List<UserEntity> users) {
    int batchSize = 500;
    for (int i = 0; i < users.size(); i++) {
        userRepository.save(users.get(i));
        if (i % batchSize == 0) {
            entityManager.flush();
            entityManager.clear();
        }
    }
}

13.3 配置建议

yaml 复制代码
spring:
  jpa:
    properties:
      hibernate:
        jdbc:
          batch_size: 500
        order_inserts: true
        order_updates: true
        default_batch_fetch_size: 100
    open-in-view: false  # 生产环境建议关闭

注:

博客:

https://blog.csdn.net/badao_liumang_qizhi

十四、完整示例代码

以下是一个完整的用户管理模块示例:

14.1 实体类

java 复制代码
package com.example.entity;

import javax.persistence.*;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import java.time.LocalDateTime;

/**
 * 用户实体
 */
@Entity
@Table(name = "t_user")
@EntityListeners(AuditingEntityListener.class)
public class UserEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    /** 用户名 */
    @Column(name = "user_name", nullable = false, length = 50)
    private String userName;

    /** 邮箱 */
    @Column(name = "email", unique = true, length = 100)
    private String email;

    /** 年龄 */
    @Column(name = "age")
    private Integer age;

    /** 是否激活 */
    @Column(name = "is_active")
    private Boolean active = true;

    /** 创建时间 */
    @CreatedDate
    @Column(name = "created_at", updatable = false)
    private LocalDateTime createdAt;

    /** 更新时间 */
    @LastModifiedDate
    @Column(name = "updated_at")
    private LocalDateTime updatedAt;

    // getter / setter 省略
}

14.2 Repository

java 复制代码
package com.example.dao;

import com.example.entity.UserEntity;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
import java.util.Optional;

/**
 * 用户数据访问层
 */
public interface UserRepository extends JpaRepository<UserEntity, Long>,
                                        JpaSpecificationExecutor<UserEntity> {

    /** 根据用户名查询 */
    Optional<UserEntity> findByUserName(String userName);

    /** 根据邮箱查询 */
    Optional<UserEntity> findByEmail(String email);

    /** 根据激活状态分页查询 */
    Page<UserEntity> findByActive(Boolean active, Pageable pageable);

    /** 根据用户名模糊查询 */
    List<UserEntity> findByUserNameContaining(String keyword);

    /** 根据年龄范围查询 */
    List<UserEntity> findByAgeBetween(Integer minAge, Integer maxAge);

    /** 是否存在该邮箱 */
    Boolean existsByEmail(String email);

    /** 自定义更新 */
    @Modifying
    @Query("UPDATE UserEntity u SET u.email = :email WHERE u.id = :id")
    int updateEmail(@Param("id") Long id, @Param("email") String email);

    /** 自定义统计 */
    @Query("SELECT COUNT(u) FROM UserEntity u WHERE u.active = :active")
    Long countByActiveStatus(@Param("active") Boolean active);
}

14.3 Service 接口

java 复制代码
package com.example.service;

import com.example.entity.UserEntity;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

/**
 * 用户服务接口
 */
public interface UserService {

    /** 创建用户 */
    UserEntity createUser(UserEntity user);

    /** 根据ID查询用户 */
    UserEntity getUserById(Long id);

    /** 分页查询激活用户 */
    Page<UserEntity> listActiveUsers(Pageable pageable);

    /** 更新邮箱 */
    void updateEmail(Long id, String email);

    /** 删除用户 */
    void deleteUser(Long id);
}

14.4 Service 实现

java 复制代码
package com.example.service.impl;

import com.example.dao.UserRepository;
import com.example.entity.UserEntity;
import com.example.service.UserService;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * 用户服务实现
 */
@Service
public class UserServiceImpl implements UserService {

    private final UserRepository userRepository;

    public UserServiceImpl(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public UserEntity createUser(UserEntity user) {
        // 检查邮箱是否已存在
        if (userRepository.existsByEmail(user.getEmail())) {
            throw new RuntimeException("邮箱已被注册");
        }
        return userRepository.save(user);
    }

    @Transactional(readOnly = true)
    @Override
    public UserEntity getUserById(Long id) {
        return userRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("用户不存在,ID: " + id));
    }

    @Transactional(readOnly = true)
    @Override
    public Page<UserEntity> listActiveUsers(Pageable pageable) {
        return userRepository.findByActive(true, pageable);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateEmail(Long id, String email) {
        // 检查用户是否存在
        if (!userRepository.existsById(id)) {
            throw new RuntimeException("用户不存在,ID: " + id);
        }
        userRepository.updateEmail(id, email);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteUser(Long id) {
        userRepository.deleteById(id);
    }
}

14.5 Controller

java 复制代码
package com.example.controller;

import com.example.entity.UserEntity;
import com.example.service.UserService;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.web.bind.annotation.*;

/**
 * 用户管理控制器
 */
@RestController
@RequestMapping("/api/users")
public class UserController {

    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    /** 创建用户 */
    @PostMapping
    public UserEntity createUser(@RequestBody UserEntity user) {
        return userService.createUser(user);
    }

    /** 根据ID查询 */
    @GetMapping("/{id}")
    public UserEntity getUserById(@PathVariable Long id) {
        return userService.getUserById(id);
    }

    /** 分页查询激活用户 */
    @GetMapping
    public Page<UserEntity> listActiveUsers(
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "10") int size) {
        PageRequest pageRequest = PageRequest.of(page, size, Sort.by("createdAt").descending());
        return userService.listActiveUsers(pageRequest);
    }

    /** 更新邮箱 */
    @PutMapping("/{id}/email")
    public void updateEmail(@PathVariable Long id, @RequestParam String email) {
        userService.updateEmail(id, email);
    }

    /** 删除用户 */
    @DeleteMapping("/{id}")
    public void deleteUser(@PathVariable Long id) {
        userService.deleteUser(id);
    }
}

14.6 配置文件

yaml 复制代码
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
    properties:
      hibernate:
        format_sql: true
        dialect: org.hibernate.dialect.MySQL8Dialect
        jdbc:
          batch_size: 500
    open-in-view: false

14.7 Maven 依赖

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

十五、Spring Data JPA vs MyBatis 选型建议

维度 Spring Data JPA MyBatis
开发效率 高(单表 CRUD 零 SQL) 中(需写 XML/注解 SQL)
复杂查询 Specification / JPQL(较繁琐) 原生 SQL + 动态 SQL(灵活)
学习曲线 较陡(需理解 JPA 生命周期) 较平(SQL 即所得)
性能调优 需理解懒加载、缓存、N+1 直接优化 SQL
数据库无关性 好(JPQL 抽象) 差(SQL 方言绑定)
适用场景 领域模型丰富、单
相关推荐
Xin_ye100862 小时前
C# 零基础到精通教程 - 第十六章:ASP.NET Core Web API——构建现代 Web 服务
开发语言·c#
basketball6162 小时前
Go语言介绍
开发语言·go
Demon1_Coder2 小时前
Day4-LangChain4j-向量数据库-检索增强RAG
数据库
phltxy2 小时前
RabbitMQ 应用问题
数据库·分布式·rabbitmq
星晨雪海2 小时前
基于 SpringBoot + Redis (Lettuce) + RabbitMQ 实现「Redis 预扣库存 + 异步同步数据库」
数据库·spring boot·java-rabbitmq
mosaic_born2 小时前
centos 7.9 离线部署Zabbix 6.0.46 监控详细方案(解决数据库字符集问题)
数据库·centos·zabbix
weelinking2 小时前
【产品】10_搭建前端框架——把你的原型变成真实页面
java·大数据·前端·数据库·人工智能·python·前端框架
dualven_in_csdn2 小时前
cmd切换到powershell (一)
服务器·开发语言·php
会编程的土豆2 小时前
Go 里的 init() 到底是什么(彻底理解)
开发语言·后端·golang