SpringAI 功能体验之SQL智能助手:用自然语言查询数据库

1. 引言

在上一篇文章《SpringAI 入门实战:从 ChatModel 到 FunctionCall 的完整体验》中,我们体验了 SpringAI 如何让大模型与 Java 应用无缝集成。今天,我们将更进一步,探索 SpringAI 的一个非常实用的落地场景------SQL 智能助手

想象一下,你不再需要记住复杂的 SQL 语法,只需要用自然语言提问:"统计出版书籍前三名的出版社",系统就能自动生成并执行 SQL 语句,返回查询结果。这就是我们今天要实现的"SQL 智能助手"。

2. 项目目标与基础思路

2.1 项目目标

我们将使用 SpringAI 搭建一个 RESTful API,实现以下功能:

  • 用户通过 HTTP 接口输入自然语言问题(如"查询所有作者")。
  • 系统将问题与数据库表结构(DDL)一起构建成 Prompt,发送给大模型。
  • 大模型返回对应的 SQL 查询语句。
  • 系统自动执行该 SQL 语句,并将结果返回给用户。

2.2 基础思路

整个流程的核心逻辑非常清晰,可以分为以下几步:

  1. 接收用户提问 :将用户的自然语言问题作为 UserMessage
  2. 构建 Prompt:将用户的问题和本地数据库的表结构(DDL)一起,填充到一个精心设计的提示词模板中。
  3. 调用大模型:将构建好的 Prompt 发送给大模型,让大模型理解数据库结构并生成对应的 SQL 语句。
  4. 执行 SQL:在本地数据库中执行大模型返回的 SQL 语句,获取查询结果。
  5. 返回结果:将 SQL 语句和查询结果一并返回给用户。

3. 环境准备与数据初始化

3.1 准备测试数据

首先,我们需要准备一个简单的图书管理系统数据库。包含三张表:Authors(作者)、Publishers(出版社)和 Books(书籍)。

Schema.sql(数据库表结构):

sql 复制代码
create table Authors (
    id int not null auto_increment,
    firstName varchar(255) not null,
    lastName varchar(255) not null,
    primary key (id)
);

create table Publishers (
    id int not null auto_increment,
    name varchar(255) not null,
    primary key (id)
);

create table Books (
    id int not null auto_increment,
    isbn varchar(255) not null,
    title varchar(255) not null,
    author_ref int not null,
    publisher_ref int not null,
    primary key (id),
    foreign key (author_ref) references Authors(id),
    foreign key (publisher_ref) references Publishers(id)
);

Data.sql(测试数据):

sql 复制代码
insert into Authors (firstName, lastName) values ('Craig', 'Walls');
insert into Authors (firstName, lastName) values ('Josh', 'Long');
insert into Authors (firstName, lastName) values ('Ken', 'Kousen');
insert into Authors (firstName, lastName) values ('Venkat', 'Subramaniam');

insert into Publishers (name) values ('Manning Publications');
insert into Publishers (name) values ('OReilly Media');
insert into Publishers (name) values ('Pragmatic Bookshelf');

insert into Books (isbn, title, author_ref, publisher_ref)
values ('9781617292545', 'Spring Boot in Action', 1, 1);
insert into Books (isbn, title, author_ref, publisher_ref)
values ('9781617297571', 'Spring in Action', 1, 1);
insert into Books (isbn, title, author_ref, publisher_ref)
values ('9781680507256', 'Build Talking Apps for Alexa', 1, 3);
insert into Books (isbn, title, author_ref, publisher_ref)
values ('9781934356401', 'Modular Java', 1, 3);
insert into Books (isbn, title, author_ref, publisher_ref)
values ('9781449374648', 'Cloud Native Java', 2, 2);
insert into Books (isbn, title, author_ref, publisher_ref)
values ('9781680508222', 'Help Your Boss Help You', 3, 3);
insert into Books (isbn, title, author_ref, publisher_ref)
values ('9781492046677', 'Kotlin Cookbook', 3, 2);
insert into Books (isbn, title, author_ref, publisher_ref)
values ('9781680509816', 'Cruising Along with Java', 4, 3);
insert into Books (isbn, title, author_ref, publisher_ref)
values ('9781680506358', 'Programming Kotlin', 4, 3);

3.2 引入依赖与配置

pom.xml 中添加 MySQL 驱动和 MyBatis-Plus(用于简化数据库操作,当然你也可以直接用 JdbcTemplate)的依赖。

xml 复制代码
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
    <version>3.5.7</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.32</version>
</dependency>

application.properties 中配置数据库连接信息:

properties 复制代码
spring.datasource.url=jdbc:mysql://localhost:3306/gpttest?characterEncoding=utf8&serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=your_password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

4. 核心代码实现

4.1 创建 Prompt 模板

这是整个功能的关键。我们需要一个提示词模板,告诉大模型它的角色和任务。创建一个名为 sql-prompt-template.st 的文件,放在 resources 目录下。

text 复制代码
Given the DDL in the DDL section, write an SQL query to answer the question in the QUESTION section.
Only produce select queries. If the question would result in an insert, update, or delete, or if the query would alter the DDL in any way, say that the operation isn't supported. If the question can't be answered, say that the DDL doesn't support answering that question.
Answer with the raw SQL query only; no markdown or other punctuation that isn't part of the query itself.

DDL
{ddl}

QUESTION
{question}

这个模板清晰地定义了大模型的行为:

  • 角色:一个 SQL 生成器。
  • 约束 :只生成 SELECT 查询,拒绝 INSERT/UPDATE/DELETE 操作。
  • 输出格式:只输出纯 SQL,不包含任何 Markdown 标记。

4.2 编写 Controller

创建一个 SqlExecController,注入 ChatClientJdbcTemplate

java 复制代码
package com.roy.sqlgenerate;

import org.springframework.core.io.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;
import java.nio.charset.Charset;

@RestController
public class SqlExecController {

    @Value("classpath:/schema.sql")
    private Resource ddlResource;

    @Value("classpath:/sql-prompt-template.st")
    private Resource sqlPromptTemplateResource;

    private final ChatClient aiClient;
    private final JdbcTemplate jdbcTemplate;

    public SqlExecController(ChatClient.Builder aiClientBuilder, JdbcTemplate jdbcTemplate) {
        this.aiClient = aiClientBuilder.build();
        this.jdbcTemplate = jdbcTemplate;
    }

    @GetMapping(path = "/sql")
    public Answer sql(@RequestParam("question") String question) throws IOException {
        // 1. 读取 DDL
        String schema = ddlResource.getContentAsString(Charset.defaultCharset());

        // 2. 调用大模型生成 SQL
        String query = aiClient.prompt()
                .user(userSpec -> userSpec
                        .text(sqlPromptTemplateResource)
                        .param("question", question)
                        .param("ddl", schema)
                )
                .call()
                .content();

        // 3. 清理大模型返回的 SQL(去除可能的 Markdown 标记)
        query = query.replace("```sql", "").replace("```", "").trim();
        System.out.println("Generated SQL: " + query);

        // 4. 执行 SQL 并返回结果
        if (query.toLowerCase().startsWith("select")) {
            return new Answer(query, jdbcTemplate.queryForList(query));
        }
        throw new RuntimeException("Unsupported operation: " + query);
    }

    // 用于封装返回结果的 Record
    public record Answer(String sql, List<Map<String, Object>> result) {}
}

4.3 测试运行

启动项目,使用 curl 或 Postman 进行测试。

请求

bash 复制代码
curl "http://localhost:8080/sql?question=统计出版书籍前三名的出版社"

响应(示例):

json 复制代码
{
  "sql": "SELECT p.name, COUNT(b.id) AS book_count FROM Publishers p JOIN Books b ON p.id = b.publisher_ref GROUP BY p.name ORDER BY book_count DESC LIMIT 3",
  "result": [
    {
      "name": "Manning Publications",
      "book_count": 2
    },
    {
      "name": "Pragmatic Bookshelf",
      "book_count": 4
    },
    {
      "name": "OReilly Media",
      "book_count": 2
    }
  ]
}

5. 应用扩展与思考

一个简单的 SQL 智能助手已经诞生了。但如何让大模型更稳定、更准确地生成结果,是 AI 应用落地中需要重点考虑的问题。以下是一些扩展思路:

  1. 提供更多上下文:除了 DDL,还可以提供一些示例数据(DML)或常见的查询模式,帮助大模型更好地理解数据。
  2. 结果后处理:对于复杂的聚合查询,可以对大模型返回的 SQL 进行语法校验,或者对查询结果进行格式化、图表渲染等。
  3. 多轮对话 :结合 ChatMemory,让用户可以对上一次的查询结果进行追问,例如"再查一下这些出版社的地址"。
  4. 安全审计:在执行 SQL 前,增加一个审核步骤,确保生成的 SQL 不会对数据库造成破坏(虽然我们的 Prompt 已经做了限制,但双重保险更安全)。

6. 总结

通过本文,我们体验了 SpringAI 在"Text-to-SQL"这一经典场景下的强大能力。我们只用了不到 50 行核心代码,就搭建了一个功能完整的 SQL 智能助手。这充分展示了 SpringAI 框架的简洁与高效,让开发者能够将更多精力放在业务逻辑和 AI 应用的创新上。

从 ChatModel 到 FunctionCall,再到今天的 SQL 智能助手,SpringAI 正在不断降低 AI 应用开发的门槛。希望这篇文章能给你带来启发,快去动手试试吧!

相关推荐
Hotchip_MEMS1 小时前
旧路由器拆出“功臣芯片”:AMS1117在高温下工作8年,只消耗2mA静态电流
网络·人工智能·物联网·智能路由器
Alluxio1 小时前
造父智能(哈啰robotaxi)在阿里云环境下构建极致透明的训练加速层
人工智能·机器学习·缓存·系统架构·自动驾驶·模型训练
金融Tech趋势派1 小时前
2026企业微信SCRM与服务商测评:私域获客、会话存档、AI能力全维度评估
人工智能·dubbo·企业微信
qq_411262421 小时前
esp32-s3的编译链和环境
人工智能
AC赳赳老秦1 小时前
OpenClaw+MySQL 深度应用:自动生成建表语句、索引优化建议与数据迁移脚本
开发语言·数据库·人工智能·python·mysql·算法·openclaw
❀抽抽1 小时前
[特殊字符] 证件照制作+检测一体化API:一次调用,过审无忧!
图像处理·人工智能·计算机视觉
qcx231 小时前
【AI Daily 2026-06-05】「持续迭代」已成为 2026 年 Agent 研究的核心命题
人工智能·python·agent
是有头发的程序猿1 小时前
AI Agent自动化实战!基于OpenClaw淘宝商品详情API,实现无人化商品采集与分析
大数据·人工智能·自动化
奔袭的算法工程师1 小时前
论文解读--BEV-radar:: bidirectional radar-camera fusion for 3D object detection
人工智能·算法·目标检测·计算机视觉·自动驾驶·信号处理