杰瑞科技汇

Java中MySQL的in查询怎么用?

IN 操作符允许你在 WHERE 子句中指定多个值,它极大地简化了查询多个条件的 SQL 语句,避免了使用多个 OR

Java中MySQL的in查询怎么用?-图1
(图片来源网络,侵删)

IN 查询的基本 SQL 语法

我们看一下 IN 查询在 SQL 中的基本形式:

SELECT column_name(s)
FROM table_name
WHERE column_name IN (value1, value2, ...);

查询 id 为 1, 3, 5 的用户:

SELECT id, name, email FROM users WHERE id IN (1, 3, 5);

这等价于:

SELECT id, name, email FROM users WHERE id = 1 OR id = 3 OR id = 5;

在 Java 中实现 IN 查询

在 Java 中,执行 SQL 查询主要有两种方式:

Java中MySQL的in查询怎么用?-图2
(图片来源网络,侵删)
  1. 使用 PreparedStatement (推荐):这是最安全、最高效的方式,可以防止 SQL 注入。
  2. 使用字符串拼接 (不推荐):有严重的 SQL 注入风险,应尽量避免。

下面我们重点介绍使用 PreparedStatement 的几种方法。

准备工作:数据库和依赖

假设我们有一个 users 表:

CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50) NOT NULL,
    email VARCHAR(100) UNIQUE
);
INSERT INTO users (name, email) VALUES
('Alice', 'alice@example.com'),
('Bob', 'bob@example.com'),
('Charlie', 'charlie@example.com'),
('David', 'david@example.com');

在 Java 项目中,你需要添加 MySQL 驱动依赖,如果你使用 Maven,pom.xml 文件如下:

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.33</version> <!-- 使用与你的MySQL版本兼容的版本 -->
</dependency>

方法一:为 IN 中的每个值设置一个占位符 (适用于少量值)

这是最直接的方法。IN 子句中有多少个值,就在 PreparedStatement 中设置多少个 占位符。

Java中MySQL的in查询怎么用?-图3
(图片来源网络,侵删)

示例代码:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.List;
public class InQueryExample {
    // 数据库连接信息
    private static final String DB_URL = "jdbc:mysql://localhost:3306/your_database?useSSL=false&serverTimezone=UTC";
    private static final String USER = "your_username";
    private static final String PASS = "your_password";
    public static void main(String[] args) {
        // 要查询的ID列表
        List<Integer> ids = Arrays.asList(1, 3, 5);
        // SQL语句,为每个id准备一个占位符
        String sql = "SELECT id, name, email FROM users WHERE id IN (?, ?, ?)";
        try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);
             PreparedStatement pstmt = conn.prepareStatement(sql)) {
            // 设置占位符的值
            for (int i = 0; i < ids.size(); i++) {
                pstmt.setInt(i + 1, ids.get(i)); // setXXX的索引从1开始
            }
            System.out.println("Executing SQL: " + pstmt.toString());
            try (ResultSet rs = pstmt.executeQuery()) {
                System.out.println("查询结果:");
                while (rs.next()) {
                    // 通过列名获取数据,更具可读性
                    int id = rs.getInt("id");
                    String name = rs.getString("name");
                    String email = rs.getString("email");
                    System.out.printf("ID: %d, Name: %s, Email: %s%n", id, name, email);
                }
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

优点

  • 简单直观。

缺点

  • 不灵活ids 列表的长度是动态变化的,你就需要动态构建 SQL 字符串,这很麻烦且容易出错。

方法二:使用 FIND_IN_SET (适用于MySQL,但效率较低)

FIND_IN_SET(str, strlist) 函数返回在逗号分隔的字符串列表 strlist 中找到 str 的位置,如果找到,返回 1,否则返回 0

SQL 语法:

SELECT id, name, email FROM users WHERE FIND_IN_SET(id, '1,3,5');

在 Java 中,我们可以将 List 拼接成一个逗号分隔的字符串。

示例代码:

import java.sql.*;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class FindInSetExample {
    private static final String DB_URL = "jdbc:mysql://localhost:3306/your_database?useSSL=false&serverTimezone=UTC";
    private static final String USER = "your_username";
    private static final String PASS = "your_password";
    public static void main(String[] args) {
        List<Integer> ids = Arrays.asList(1, 3, 5);
        // 将List<Integer>转换为逗号分隔的字符串 "1,3,5"
        String idString = ids.stream()
                             .map(String::valueOf)
                             .collect(Collectors.joining(","));
        // SQL语句
        String sql = "SELECT id, name, email FROM users WHERE FIND_IN_SET(id, ?)";
        try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);
             PreparedStatement pstmt = conn.prepareStatement(sql)) {
            // 设置占位符
            pstmt.setString(1, idString);
            System.out.println("Executing SQL: " + pstmt.toString());
            try (ResultSet rs = pstmt.executeQuery()) {
                System.out.println("查询结果:");
                while (rs.next()) {
                    int id = rs.getInt("id");
                    String name = rs.getString("name");
                    String email = rs.getString("email");
                    System.out.printf("ID: %d, Name: %s, Email: %s%n", id, name, email);
                }
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

优点

  • 只需要一个占位符,代码更简洁,适合动态长度的列表。

缺点

  • 性能较差FIND_IN_SET 会进行全表扫描,无法使用 id 列上的索引,当数据量大时,查询速度会非常慢。不推荐用于大数据量的查询。

方法三:使用 JOININ (最推荐的高性能方法)

这是处理动态 IN 列表最推荐的方法,尤其是在使用像 MyBatis 或 JPA 这样的 ORM 框架时,它将 IN 查询转换为一个 JOIN 操作,可以高效地利用索引。

核心思想

  1. 创建一个临时表(或内存表),将要查询的值(如 1, 3, 5)存入其中。
  2. 将主表与这个临时表进行 JOIN 关联。

示例代码:

import java.sql.*;
import java.util.Arrays;
import java.util.List;
public class JoinInSetExample {
    private static final String DB_URL = "jdbc:mysql://localhost:3306/your_database?useSSL=false&serverTimezone=UTC";
    private static final String USER = "your_username";
    private static final String PASS = "your_password";
    public static void main(String[] args) {
        List<Integer> ids = Arrays.asList(1, 3, 5);
        // 1. 创建一个临时表来存放ID
        String createTempTableSql = "CREATE TEMPORARY TABLE temp_ids (id INT PRIMARY KEY)";
        // 2. 向临时表中插入数据
        String insertTempSql = "INSERT INTO temp_ids (id) VALUES (?)";
        // 3. 使用JOIN查询
        String selectSql = "SELECT u.id, u.name, u.email FROM users u JOIN temp_ids t ON u.id = t.id";
        try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS)) {
            // 关闭自动提交,以便在一个事务中执行多个语句
            conn.setAutoCommit(false);
            // 步骤1: 创建临时表
            try (Statement stmt = conn.createStatement()) {
                stmt.execute(createTempTableSql);
            }
            // 步骤2: 批量插入数据到临时表
            try (PreparedStatement pstmt = conn.prepareStatement(insertTempSql)) {
                for (Integer id : ids) {
                    pstmt.setInt(1, id);
                    pstmt.addBatch(); // 添加到批处理
                }
                pstmt.executeBatch(); // 执行批处理
            }
            // 步骤3: 执行JOIN查询
            try (Statement stmt = conn.createStatement();
                 ResultSet rs = stmt.executeQuery(selectSql)) {
                System.out.println("查询结果:");
                while (rs.next()) {
                    int id = rs.getInt("id");
                    String name = rs.getString("name");
                    String email = rs.getString("email");
                    System.out.printf("ID: %d, Name: %s, Email: %s%n", id, name, email);
                }
            }
            // 提交事务
            conn.commit();
        } catch (SQLException e) {
            e.printStackTrace();
            // 如果出错,回滚事务
            try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS)) {
                conn.rollback();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
        }
    }
}

优点

  • 性能极高JOIN 操作可以利用 id 列上的索引,查询速度非常快,不受 IN 列表长度的影响。
  • 代码结构清晰:将数据准备和查询逻辑分离。

缺点

  • 需要执行多个 SQL 语句,代码相对复杂一些。

总结与最佳实践

方法 优点 缺点 适用场景
多个占位符 简单,安全,性能好 不灵活,无法处理动态长度的列表 IN 列表长度固定且很少变化的情况。
FIND_IN_SET 代码简洁,一个占位符 性能差,无法使用索引,全表扫描 列表很短,或者对性能要求不高的场景。尽量避免。
JOIN 临时表 性能最高,可处理任意长度的列表 代码复杂,需要多个SQL语句 强烈推荐,特别是当 IN 列表是动态生成且可能很长时。

最终建议:

  1. IN 列表是固定的、已知的,使用方法一(多个占位符),它最简单直接。
  2. IN 列表是动态变化的,强烈推荐使用方法三(JOIN 临时表),以获得最佳性能。
  3. 永远不要在 Java 代码中通过字符串拼接来构建 IN 子句,以防止 SQL 注入。

在现代 Java 开发中,通常会使用 MyBatis、JPA (Hibernate) 等 ORM 框架,它们内部已经实现了高效处理动态 IN 查询的机制(通常就是基于 JOIN 的思想),开发者只需调用相应的方法即可,无需关心底层的 SQL 实现。

分享:
扫描分享到社交APP
上一篇
下一篇