深入Spring Boot生态中最核心部分 数据库交互spring-boot-starter-data-jpa和Hibernate (指南五)

我们继续深入Spring Boot生态中最核心、也是最能体现其工程化思想的部分:数据库交互。对于PHP开发者而言,你可能熟悉PDO、查询构造器(Query Builder),或者更常用的是ORM(对象关系映射)工具,如Laravel的Eloquent或Symfony的Doctrine。

Spring Boot通过spring-boot-starter-data-jpa提供了一套极其强大和优雅的持久化解决方案。让我们深入学习它,并与你熟悉的PHP ORM进行对比,你将会发现两者在哲学思想上的异同。


核心概念:分层的持久化世界

在PHP世界,Eloquent是一个典型的Active Record 实现。你的User模型类继承自Eloquent\Model,这个模型对象本身就"知道"如何与数据库交互(User::find(1), $user->save())。模型、业务逻辑和持久化逻辑紧密地耦合在一起,这非常便捷,开发效率极高。

Java的持久化世界则更加分层和标准化,主要涉及以下几个概念:

  1. JDBC (Java Database Connectivity): 这是最底层的Java数据库API,提供了一套标准的接口来连接和执行SQL。

    • PHP类比 : 类似PHP的PDO扩展,提供了基础的、与具体数据库无关的数据库操作能力。
  2. JPA (Jakarta Persistence API) : JPA不是一个工具,而是一套官方规范和标准 。它定义了Java对象如何映射到数据库表(ORM的规则),以及如何对这些对象进行增删改查。它只提供接口(如EntityManager),不提供实现。

    • PHP类比 : 想象一下PHP社区共同制定了一个"ORM标准接口"(PSR-ORM),规定了模型该如何定义、数据该如何存取。JPA就是这样的一个角色。
  3. Hibernate : Hibernate是JPA规范最著名、最强大的实现者。它是一个具体的ORM框架,负责解析你的映射配置,生成SQL,管理事务和缓存等脏活累活。

    • PHP类比 : Hibernate的角色类似于Doctrine,是Symfony的默认ORM,也是一个功能完备、实现了数据映射器模式(Data Mapper)的ORM引擎。
  4. Spring Data JPA : 这是Spring生态对JPA的终极封装 。它本身不做ORM,而是像一个"智能粘合剂",将Hibernate和JPA的使用体验提升到了一个全新的、不可思议的便捷高度。它提供了Repository(仓库)模式的抽象,让你几乎不用写任何实现代码就能完成绝大部分数据库操作。

    • PHP类比: 想象一个Laravel包,它能让你只定义一个接口,就自动获得了所有Eloquent的查询功能,并且还增加了更多魔法。Spring Data JPA就是这样的存在。

总结一下:你使用Spring Data JPA的接口进行编程,Spring Data JPA在底层调用JPA标准接口,而Hibernate则在幕后作为JPA的具体实现来完成真正的工作。


第一章:项目设置与数据库连接

1.1 添加依赖

首先,我们需要在pom.xml中添加两个核心依赖:

  1. spring-boot-starter-data-jpa: 这个"启动器"会自动引入所有需要的库,包括JPA API、Hibernate以及Spring Data JPA本身。
  2. 数据库驱动: 你需要为你选择的数据库添加具体的JDBC驱动。
xml 复制代码
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>

    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <scope>runtime</scope>
    </dependency>

    </dependencies>

PHP类比 : 这相当于在composer.jsonrequirelaravel/framework(它自带Eloquent),并确保php.ini中启用了pdo_mysql扩展。

1.2 配置数据库连接

接下来,在application.yml中配置数据库连接信息。

yaml 复制代码
# application.yml
spring:
  # --- Datasource Configuration ---
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/my_database?useSSL=false&serverTimezone=UTC
    username: root
    password: your_password
    driver-class-name: com.mysql.cj.jdbc.Driver # 通常可以省略,Spring Boot能自动检测

  # --- JPA & Hibernate Configuration ---
  jpa:
    hibernate:
      # ddl-auto: VERY important for development!
      # none: (Default for MySQL) Do nothing. Recommended for production.
      # validate: Check if the schema matches the entities.
      # update: Automatically add columns, tables etc. DANGEROUS in production.
      # create-drop: Create schema on startup, drop it on shutdown. Good for tests.
      ddl-auto: update
    show-sql: true # Log generated SQL to the console, great for debugging
    properties:
      hibernate:
        # Format the logged SQL to be more readable
        format_sql: true

PHP类比 : spring.datasource部分完全等同于你在.env文件中设置的DB_HOST, DB_DATABASE, DB_USERNAME, DB_PASSWORD

spring.jpa.hibernate.ddl-auto的深入理解 :

这个配置非常方便,但也极其危险。update模式会在应用启动时,检查你的Java实体类(Entity)和数据库表结构,如果发现不一致,它会尝试自动修改数据库表(如添加列)。

  • 开发时 : 设置为update可以让你快速迭代,不用每次修改实体类都手动写数据库变更语句。
  • 生产时 : 必须设置为nonevalidate 绝对不能让程序自动修改生产数据库的结构。生产环境的数据库结构变更应该通过专门的迁移工具(如Flyway或Liquibase)来严格管理。
  • 与PHP对比 : ddl-auto: update有点像一个简化的、自动运行的php artisan migrate。但它远没有Laravel Migration系统强大和安全,因为它没有版本控制,也无法处理复杂的数据迁移。

第二章:实体(Entity)- 你的模型类

在JPA中,映射到数据库表的Java对象被称为实体(Entity)

PHP Eloquent 示例 (app/Models/User.php):

php 复制代码
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use HasFactory;
    protected $table = 'users';
    protected $fillable = ['name', 'email', 'password'];
}

Java JPA 实体示例 (src/.../entity/User.java):

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

import jakarta.persistence.*;
import java.time.LocalDateTime;

@Entity // 1. 声明这是一个JPA实体类
@Table(name = "users") // 2. 映射到数据库中的'users'表
public class User {

    @Id // 3. 标记这是主键
    @GeneratedValue(strategy = GenerationType.IDENTITY) // 4. 主键生成策略(自增)
    private Long id;

    @Column(nullable = false, length = 100) // 5. 映射到列,并添加约束
    private String name;

    @Column(nullable = false, unique = true)
    private String email;

    private String password; // 简单映射,列名与字段名相同

    @Column(name = "created_at", updatable = false)
    private LocalDateTime createdAt;
    
    @PrePersist // 6. 在持久化(INSERT)之前自动调用
    protected void onCreate() {
        createdAt = LocalDateTime.now();
    }

    // --- IMPORTANT: JPA requires a no-argument constructor and getters/setters ---
    public User() {
    }
    
    // Getters and Setters for all fields...
    // (可以使用Lombok的 @Getter, @Setter, @NoArgsConstructor 来简化)
}

注解解析与对比:

  1. @Entity: 告诉JPA:"这个类是一个实体,请为我管理它"。等同于extends Model
  2. @Table(name = "users"): 指定表名。等同于protected $table = 'users' 。如果省略,默认使用类名(user)。
  3. @Id: 标记主键字段。等同于Eloquent中$primaryKey属性的约定
  4. @GeneratedValue: 配置主键的生成方式。IDENTITY对应MySQL的AUTO_INCREMENT
  5. @Column: 详细配置字段与列的映射。你可以定义nullable, unique, length等。这部分功能更像是Laravel的Migration文件中的Schema定义 ,如$table->string('name', 100)->nullable(false);。JPA将模型属性和部分表结构定义融合在了一起。
  6. @PrePersist: 这是一个生命周期回调注解。@PrePersist注解的方法会在新实体被保存到数据库(执行INSERT)之前调用。等同于Eloquent中的模型事件(Model Events) ,如boot()方法中定义的static::creating(function ($user) { ... })

第三章:仓库(Repository)- 数据访问的革命

这是Spring Data JPA与Eloquent(Active Record模式)思想上最大的分歧点,也是其魅力的核心。

Eloquent中,模型自己负责数据操作。Spring Data JPA则遵循仓库模式(Repository Pattern),将数据访问的职责从实体中分离出来,交给一个专门的接口------Repository。

操作步骤:

  1. 创建一个接口 UserRepository :
    entity包旁边创建一个repository包,然后创建一个接口(Interface) ,而不是类。

    java 复制代码
    package com.example.demoapp.repository;
    
    import com.example.demoapp.entity.User;
    import org.springframework.data.jpa.repository.JpaRepository;
    import java.util.Optional;
    
    // 1. 定义一个接口,并继承 JpaRepository
    public interface UserRepository extends JpaRepository<User, Long> {
    
        // 2. 仅需定义方法签名,无需任何实现!
        Optional<User> findByEmail(String email);
    }

发生了什么?

  1. extends JpaRepository<User, Long> : 这是魔法的核心。通过继承JpaRepository,Spring Data JPA在应用启动时,会自动为你生成这个接口的实现类,并将其注册为一个Bean。这个实现类已经包含了全套的CRUD方法!

    • User: 泛型参数,指定这个Repository是为User实体服务的。
    • Long: 泛型参数,指定User实体的主键类型是Long
  2. 你获得的"免费"方法 (部分):

    • save(User user): 创建或更新用户。等同于$user->save()
    • findById(Long id): 按ID查找用户。等同于User::find($id)
    • findAll(): 获取所有用户。等同于User::all()
    • deleteById(Long id): 按ID删除用户。等同于User::destroy($id)
    • count(): 统计用户数。等同于User::count()
    • ...还有分页查询、批量操作等大量方法。
  3. 查询衍生(Query Derivation) : 这是第二个魔法。你在接口中定义了一个findByEmail(String email)方法。你没有写任何实现代码 ,但Spring Data JPA会解析这个方法名,并自动为你生成并执行如下的JPQL(JPA查询语言,类似于SQL)查询:"select u from User u where u.email = ?1"

    • 方法命名规则 : findBy{字段名}countBy{字段名}deleteBy{字段名}... 你还可以组合And, Or, GreaterThan, Like, OrderBy等关键字。例如:List<User> findByNameContainingAndStatusOrderByCreatedAtDesc(String keyword, String status);
    • PHP类比 : 这就像是Eloquent的魔术where方法User::whereEmail($email)->first())的超级进化版。你将查询的意图直接固化在了一个个强类型的方法签名中,而不是在代码里拼接字符串。

第四章:在服务层中使用Repository

现在,我们将Repository注入到服务层(Service Layer)来执行业务逻辑。

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

import com.example.demoapp.entity.User;
import com.example.demoapp.repository.UserRepository;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;

@Service // 标记为服务层组件
public class UserService {

    private final UserRepository userRepository;

    // 通过构造函数注入UserRepository
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public User createUser(String name, String email, String password) {
        User newUser = new User();
        newUser.setName(name);
        newUser.setEmail(email);
        newUser.setPassword(password); // (实际项目中需要加密)
        
        return userRepository.save(newUser); // 调用save方法持久化
    }
    
    public Optional<User> getUserByEmail(String email) {
        // 直接调用我们刚才在接口中定义的魔法方法
        return userRepository.findByEmail(email);
    }

    public List<User> getAllUsers() {
        return userRepository.findAll(); // 获取所有用户
    }
}

与PHP对比 :

在Laravel中,你可能会在Controller或Service中直接使用Eloquent模型:User::create([...])。在Spring Boot中,你注入对应的Repository,然后调用Repository的方法:userRepository.save(user)

这种**关注点分离(Separation of Concerns)**是Java企业级应用的核心思想:

  • Entity: 只负责描述数据结构和映射关系。
  • Repository: 只负责数据的存取。
  • Service: 只负责实现业务逻辑,协调Entity和Repository。

总结对比

特性/概念 PHP (Eloquent) Java (Spring Data JPA + Hibernate) 心态转变
设计模式 Active Record (模型自带存取方法) Repository + Data Mapper (存取逻辑分离到仓库) 从模型万能论到职责分离,代码更清晰,可测试性更强。
模型/实体 class User extends Model {} @Entity class User {} 概念类似,但JPA实体通过注解承载了更多元数据。
数据访问 User::find(1), $user->save() (静态/实例方法) 注入UserRepository, 调用repo.findById(1), repo.save(user) 从静态调用模型到注入专门的数据访问对象。
查询构建 查询构造器 User::where(...) / Eloquent查询 查询衍生 repo.findByName(...) / JPQL / Criteria API 从链式调用构建查询到通过方法命名或专用查询语言定义查询。
数据库迁移 Migration系统 (php artisan migrate) 无内置迁移系统ddl-auto仅供开发。生产需用Flyway/Liquibase 必须引入专业的数据库版本控制工具,这是Java工程化的体现。
核心优势 开发效率极高,上手快,简单直观 类型安全,高度解耦,标准化,IDE支持极佳,易于测试和维护 从追求极致的开发速度到构建一个长期、稳定、可维护的系统。

虽然Spring Data JPA的入门曲线比Eloquent要陡峭一些,因为它引入了更多的分层和概念。但一旦你掌握了Entity、Repository和Service的协作模式,你将能构建出结构极其清晰、类型安全、易于测试和扩展的大型应用。这种由框架强制执行的"最佳实践"正是Java生态的魅力所在。

前面的教程我们聚焦于如何使用 spring-boot-starter-data-jpa,而这个使用体验主要是由Spring Data JPA塑造的。在这个过程中,Hibernate就像一台性能强劲但藏在幕后的发动机,我们并没有直接操作它,但它完成了所有最艰苦的工作。

现在,我们就把发动机盖打开,深入探究一下Hibernate本身到底是什么,它在整个体系中扮演了什么角色,以及它提供了哪些JPA规范之外的强大功能


重新理解我们的技术栈:一个精准的类比

为了清晰地理解JPA和Hibernate的关系,我们可以用一个接口和实现的类比:

  • JPA (Jakarta Persistence API) : 就像是USB接口标准。它是一份公开的、行业公认的规范。它定义了USB接口应该是什么形状、有几根针脚、每根针脚的功能是什么。但这份规范本身并不能帮你存储数据,它只是一套标准。

  • Hibernate : 就像是一个高性能的移动硬盘(例如三星或西部数据品牌) 。这个硬盘遵循并实现了USB接口标准,所以它可以插在任何有USB口的电脑上使用。但除了实现标准接口外,这个硬盘内部还有自己独家的、先进的技术,比如特殊的缓存算法、磨损均衡技术等,这些是它作为一款优秀产品的核心竞争力。

  • Spring Data JPA : 就像是操作系统中那个极其智能、好用的"文件管理器"或"驱动程序"。当你把移动硬盘插上电脑,你通常不会直接用二进制命令去操作硬盘,而是通过这个文件管理器进行"复制"、"粘贴"等简单操作。文件管理器利用了USB标准与硬盘通信,让你的使用体验变得极其简单。它能与任何实现了USB标准的硬盘工作,但它与像Hibernate这样主流、强大的硬盘配合得最好。

结论:你日常编程接触的是Spring Data JPA(文件管理器),它让你用最简单的方式下达指令。Spring Data JPA将你的指令翻译成JPA标准(USB标准)的调用。而Hibernate(移动硬盘)作为JPA标准的实现者,在底层接收这些标准调用,并用自己强大的内部引擎完成实际的数据读写、事务管理和性能优化。


Hibernate的核心职责:它在幕后做了什么?

当你调用userRepository.save(user)时,背后发生了什么?Hibernate主要承担了以下几个核心职责:

1. 终极翻译官:从对象到SQL (SQL Generation)

这是Hibernate最基本也最重要的工作。它负责在你的Java对象世界和关系型数据库的SQL世界之间进行双向翻译。

  • 对象 -> SQL : 当你调用save(user)时,Hibernate会读取User实体类的所有@注解(@Entity, @Table, @Column等),然后动态地生成 一条对应数据库方言(Dialect)的INSERT SQL语句。如果调用findById(1L),它会生成SELECT * FROM users WHERE id = ?
  • SQL -> 对象 : 当查询到数据后,Hibernate会读取结果集(ResultSet),并根据映射关系,将一行行的数据"水合"(Hydrate)成一个个的User对象实例。

关键特性:数据库方言 (Dialect)

Hibernate非常智能,它知道不同数据库的SQL语法有细微差别。例如,MySQL的分页用LIMIT,Oracle用ROWNUM;MySQL的主键自增是AUTO_INCREMENT,PostgreSQL是SERIAL。你只需要在application.yml中配置好spring.datasource.url,Hibernate就会自动选择合适的方言来生成最适配当前数据库的SQL。

PHP类比 :这部分工作类似于Eloquent或Doctrine的查询构造器(Query Builder)。当你链式调用->where()->orderBy()->get()时,ORM在底层为你拼接SQL字符串。Hibernate以一种更全面、更自动化的方式完成了这项工作。

2. 状态管理器:持久化上下文与生命周期 (Persistence Context)

这是理解ORM精髓的关键,也是Hibernate强大的地方。Hibernate内部维护了一个叫做**持久化上下文(Persistence Context)**的概念,在JPA规范中它被称为EntityManager,在Hibernate的早期API中它被称为Session

你可以把它想象成一个**"实体暂存区"或"工作单元"**,它与一个数据库事务绑定。所有在这个事务中被加载或保存的实体,都会被放入这个上下文中进行管理。

实体在Hibernate中有四种状态:

  1. 瞬时态 (Transient) : 一个普通的new User()对象,与持久化上下文无任何关联。
  2. 持久态 (Persistent) : 该对象在持久化上下文中,它的任何变更都会被Hibernate追踪。通过find()save()后的实体就处于这个状态。
  3. 游离态 (Detached): 该对象曾经被管理,但由于持久化上下文被关闭(如事务结束),它现在脱离了管理。
  4. 删除态 (Removed): 准备从数据库中删除的对象。

核心特性:自动脏检查 (Automatic Dirty Checking)

这是持久化上下文带来的最大便利。

java 复制代码
@Transactional
public void updateUserEmail(Long userId, String newEmail) {
    // 1. findById使user对象进入"持久态",被Hibernate纳入管理
    User user = userRepository.findById(userId).orElseThrow(); 
    
    // 2. 你只修改了Java对象的状态,没有调用save
    user.setEmail(newEmail); 
    
    // 3. 当这个@Transactional方法结束,事务准备提交时...
    // Hibernate会自动检查其管理的所有"持久态"对象。
    // 它发现user对象的email字段与刚从数据库加载时的快照不同(变"脏"了)。
    // 于是,Hibernate会自动生成一条UPDATE语句并执行,将变更同步到数据库。
}

不需要手动调用userRepository.save(user)来更新!这就是"脏检查"的威力。

PHP类比 :Eloquent也有类似机制。当你执行$user = User::find(1); $user->email = '...'; $user->save();时,save()方法内部会检查哪些属性被修改过(isDirty),然后只更新修改过的字段。Hibernate的强大之处在于,结合Spring的@Transactional,这个save()的调用过程在很多更新场景下都被隐式地、自动地完成了

3. 性能加速器:多级缓存机制 (Caching)

为了减少对数据库的昂贵访问,Hibernate内置了一套复杂的缓存系统。

  • 一级缓存 (L1 Cache / Session Cache):

    • 这就是我们上面提到的持久化上下文 。它的生命周期与Session或事务相同。
    • 作用: 在同一个事务内,如果你多次通过ID请求同一个对象,只有第一次会查询数据库。后续的请求,Hibernate会直接从一级缓存(一个内部Map)中返回这个对象,避免了重复的SQL查询。
    • 这是默认开启且无法关闭的
    java 复制代码
    @Transactional
    public void L1CacheExample(Long userId) {
        User user1 = userRepository.findById(userId).get(); // 发送SELECT SQL
        User user2 = userRepository.findById(userId).get(); // 不会发SQL,直接从L1缓存返回
        System.out.println(user1 == user2); // 输出 true, 两个引用指向同一个内存中的对象
    }
  • 二级缓存 (L2 Cache / Global Cache):

    • 这是一个跨事务、跨Session的全局缓存,它的生命周期和整个应用程序一样长。
    • 作用 : 用于缓存那些很少被修改但经常被读取的数据,例如国家列表、系统配置、用户角色等。当一个事务请求某个实体时,Hibernate会先检查L2缓存,如果命中,就无需访问数据库。
    • 默认关闭,需要手动配置 。你需要引入一个缓存提供商(如EhCache, Hazelcast, Infinispan),然后在application.yml中开启L2缓存,并在需要缓存的实体类上添加@Cacheable注解。

PHP类比

  • 一级缓存:类似于在一个请求的生命周期内,一些设计精良的PHP ORM可能会做的内部对象缓存。
  • 二级缓存 :完全等同于你在PHP项目中引入Redis或Memcached ,并使用Cache::remember('user:'.$id, ...)这样的模式来手动缓存模型数据。Hibernate的优势在于,它将这个缓存过程与ORM深度集成,对开发者更透明,配置好之后几乎是自动的。
4. 高级查询武器:HQL/JPQL 和 Criteria API

虽然Spring Data JPA的查询衍生(findByEmail)能解决80%的问题,但对于复杂的查询(例如多表JOIN、子查询、聚合函数等),我们需要更强大的工具。Hibernate提供了两种方式:

  • HQL (Hibernate Query Language) / JPQL (Jakarta Persistence Query Language):

    • 这是一种面向对象的查询语言 。你查询的不是数据库表和列,而是Java实体和属性
    • 语法和SQL非常相似,但更具可移植性,因为最终由Hibernate把它翻译成特定数据库方言的SQL。
    • 在Spring Data JPA中,通常通过@Query注解在Repository方法上使用。
    java 复制代码
    // UserRepository.java
    @Query("SELECT u FROM User u WHERE u.status = :status AND u.createdAt > :date")
    List<User> findActiveUsersSince(@Param("status") String status, @Param("date") LocalDateTime date);

    注意,我们写的是FROM User u(类名和别名),而不是FROM users(表名)。

  • Criteria API:

    • 这是一种纯Java的、类型安全的、程序化的方式来构建动态查询。你可以通过调用一系列方法来构建一个查询对象,完全避免了拼接字符串。
    • 代码更冗长,但类型安全,可以在编译期发现错误。

PHP类比:

  • HQL/JPQL :非常类似于Doctrine的DQL。对于Laravel开发者来说,它像是查询构造器的一种更结构化、更安全的替代品,特别是当你需要构建的查询逻辑非常复杂时。
  • Criteria API:类似于你在PHP中以纯程序化方式构建查询对象(一些高级查询构造器库提供了这种能力),优点是所有东西都是代码,易于重构和进行条件判断。

总结:JPA, Hibernate, Spring Data JPA的角色分工

组件 角色 PHP类比 核心价值
JPA 标准/规范 (一套接口) 类似PSR (PHP Standard Recommendation) 可移植性:理论上可以随时将Hibernate替换为其他JPA实现(如EclipseLink)而无需修改业务代码。
Hibernate JPA的实现 (一个强大的引擎) Doctrine (一个具体的、强大的ORM实现) 功能强大:负责SQL生成、事务管理、生命周期、多级缓存、HQL等所有底层重活。
Spring Data JPA JPA的简化封装 (一个便捷的工具) 像是Eloquent的便捷性 + 仓库模式的优雅 开发效率:通过Repository接口和查询衍生,极大简化了数据访问层的代码,让你几乎不用写实现。

作为一名从PHP转过来的开发者,你可以这样理解:

你失去了Eloquent那种模型无所不能(Active Record)的极致便捷,但你得到的是一个更加分层、解耦、健壮和高性能的持久化架构 。你主要与Spring Data JPA的Repository打交道,享受它带来的便利,同时心中要清楚,是强大的Hibernate引擎在底层为你保驾护航,而JPA标准则保证了这套架构的开放性和通用性。

相关推荐
_Minato_2 小时前
数据库知识整理——SQL数据定义
数据库·sql·mysql·oracle·database·数据库开发·数据库架构
程序员卷卷狗2 小时前
MySQL 四种隔离级别:从脏读到幻读的全过程
数据库·mysql
l1t3 小时前
改写ITPUB newkid的求解数独DuckDB SQL为Clickhouse格式
数据库·sql·clickhouse·duckdb
国服第二切图仔4 小时前
鸿蒙应用开发之实现键值型数据库跨设备数据同步
数据库·wpf·harmonyos
東雪木4 小时前
Spring Boot 2.x 集成 Knife4j (OpenAPI 3) 完整操作指南
java·spring boot·后端·swagger·knife4j·java异常处理
盒马coding5 小时前
PostgreSQL18新功能COPY命令变得更加用户友好
数据库·postgresql
陈果然DeepVersion5 小时前
Java大厂面试真题:从Spring Boot到AI微服务的三轮技术拷问(二)
spring boot·redis·spring cloud·微服务·ai·java面试·rag
️️(^~^)5 小时前
触发器,存储过程
数据库
罗光记5 小时前
Quantinuum 发布新型量子计算机“Helios“
数据库·经验分享·其他·百度·twitter
友友马5 小时前
『 数据库 』MySQL索引深度解析:从数据结构到B+树的完整指南
数据库·mysql