杰瑞科技汇

Java如何连接MySQL数据库?

目录

  1. 准备工作
    • 安装 MySQL 数据库
    • 创建数据库和测试表
    • 准备 Java 开发环境
  2. 核心步骤:获取 JDBC 驱动
  3. 编写 Java 连接代码
    • 基础版(不推荐用于生产)
    • 推荐版(使用 try-with-resources
    • 使用配置文件(推荐)
  4. 执行 SQL 查询
    • 查询数据 (SELECT)
    • 更新数据 (INSERT, UPDATE, DELETE)
  5. 最佳实践
    • 使用连接池
    • 防止 SQL 注入
    • 使用 PreparedStatement
  6. 完整示例代码
  7. 常见问题与解决

准备工作

a. 安装 MySQL 数据库

确保您的计算机上已经安装并运行了 MySQL 数据服务器,您可以从 MySQL 官网 下载并安装。

Java如何连接MySQL数据库?-图1
(图片来源网络,侵删)

b. 创建数据库和测试表

打开 MySQL 命令行客户端或任何 MySQL GUI 工具(如 MySQL Workbench, Navicat, DBeaver),执行以下 SQL 语句来创建一个示例数据库和表。

-- 创建一个名为 'testdb' 的数据库
CREATE DATABASE IF NOT EXISTS testdb;
-- 使用 'testdb' 数据库
USE testdb;
-- 创建一个 'users' 表
CREATE TABLE IF NOT EXISTS users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50) NOT NULL,
    email VARCHAR(100) UNIQUE NOT NULL,
    age INT
);
-- 插入一些测试数据
INSERT INTO users (name, email, age) VALUES
('Alice', 'alice@example.com', 30),
('Bob', 'bob@example.com', 25),
('Charlie', 'charlie@example.com', 35);

c. 准备 Java 开发环境

确保您已经安装了 JDK (Java Development Kit) 和一个 IDE(如 IntelliJ IDEA, Eclipse 或 VS Code)。


核心步骤:获取 JDBC 驱动

Java 通过 JDBC (Java Database Connectivity) API 来连接数据库,要连接 MySQL,您需要 MySQL 提供的 JDBC 驱动程序。

驱动程序类型:

Java如何连接MySQL数据库?-图2
(图片来源网络,侵删)
  • JDBC Type 4 (纯 Java 驱动): 这是最现代和推荐的类型,它不需要客户端库,直接使用网络套接字连接。

如何获取驱动程序? 您有两种主要方式:

使用 Maven(推荐) 如果您使用 Maven 管理项目,只需在 pom.xml 文件中添加以下依赖即可,Maven 会自动下载驱动。

<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <version>8.0.33</version> <!-- 建议使用最新版本 -->
</dependency>

手动下载 JAR 文件

  1. 访问 Maven Central Repository
  2. 找到最新版本的 mysql-connector-j-8.0.33.jar (或您需要的版本)。
  3. 下载 JAR 文件。
  4. 将此 JAR 文件添加到您的 Java 项目的类路径 中,在 IDE 中,通常右键项目 -> Build Path / Project Structure -> Libraries -> Add External JARs。

编写 Java 连接代码

连接数据库需要以下信息:

Java如何连接MySQL数据库?-图3
(图片来源网络,侵删)
  • URL: 格式为 jdbc:mysql://[host][:port]/[database_name]
    • host: 数据库服务器地址(本地为 localhost0.0.1
    • port: MySQL 端口(默认为 3306
    • database_name: 要连接的数据库名(如 testdb
  • User: 数据库用户名(如 root
  • Password: 数据库密码

a. 基础版(不推荐用于生产)

这是最简单的写法,但存在严重问题:如果发生异常,Connection 对象不会被关闭,会导致数据库连接泄漏。

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class BasicConnection {
    public static void main(String[] args) {
        // 数据库连接信息
        String url = "jdbc:mysql://localhost:3306/testdb";
        String user = "root";
        String password = "your_password"; // 请替换为您的密码
        Connection conn = null;
        try {
            // 1. 加载 JDBC 驱动 (对于较新版本的驱动,这一步通常可以省略)
            Class.forName("com.mysql.cj.jdbc.Driver");
            // 2. 建立连接
            System.out.println("正在连接数据库...");
            conn = DriverManager.getConnection(url, user, password);
            System.out.println("数据库连接成功!");
        } catch (ClassNotFoundException e) {
            System.err.println("找不到 MySQL JDBC 驱动。");
            e.printStackTrace();
        } catch (SQLException e) {
            System.err.println("数据库连接失败!");
            e.printStackTrace();
        } finally {
            // 3. 关闭连接 (非常重要!)
            if (conn != null) {
                try {
                    conn.close();
                    System.out.println("数据库连接已关闭。");
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

b. 推荐版(使用 try-with-resources

Java 7 引入了 try-with-resources 语句,它可以自动实现 AutoCloseable 接口的对象(如 Connection, Statement, ResultSet)在 try 块结束时自动关闭,极大地简化了资源管理,是当前的最佳实践。

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class ModernConnection {
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/testdb";
        String user = "root";
        String password = "your_password"; // 请替换为您的密码
        // try-with-resources 会自动关闭 conn
        try (Connection conn = DriverManager.getConnection(url, user, password)) {
            System.out.println("数据库连接成功!");
            // 在这里执行数据库操作...
            System.out.println("操作完成。");
        } catch (SQLException e) {
            System.err.println("数据库操作出错!");
            e.printStackTrace();
        }
        // conn 在这里会被自动关闭
        System.out.println("数据库连接已自动关闭。");
    }
}

c. 使用配置文件(推荐)

将数据库连接信息硬编码在代码中是不好的做法,更好的方式是将其放在外部配置文件中。

  1. src/main/resources 目录下创建一个 db.properties 文件:

    src/main/resources/db.properties

    # 数据库连接配置
    db.url=jdbc:mysql://localhost:3306/testdb?useSSL=false&serverTimezone=UTC
    db.user=root
    db.password=your_password # 请替换为您的密码
    • ?useSSL=false: 在本地开发时禁用 SSL(生产环境应配置 SSL)。
    • &serverTimezone=UTC: 指定服务器时区,避免警告。
  2. 修改 Java 代码以读取配置文件:

import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
public class ConfigurableConnection {
    public static void main(String[] args) {
        // 加载配置文件
        Properties props = new Properties();
        InputStream input = ConfigurableConnection.class.getClassLoader().getResourceAsStream("db.properties");
        if (input == null) {
            System.err.println("抱歉,找不到 db.properties 文件");
            return;
        }
        try {
            props.load(input);
        } catch (IOException e) {
            System.err.println("加载配置文件失败!");
            e.printStackTrace();
            return;
        }
        String url = props.getProperty("db.url");
        String user = props.getProperty("db.user");
        String password = props.getProperty("db.password");
        // 使用 try-with-resources
        try (Connection conn = DriverManager.getConnection(url, user, password)) {
            System.out.println("数据库连接成功!");
            // ... 执行操作 ...
        } catch (SQLException e) {
            System.err.println("数据库操作出错!");
            e.printStackTrace();
        }
    }
}

执行 SQL 查询

a. 查询数据 (SELECT)

import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
public class SelectExample {
    public static void main(String[] args) {
        Properties props = new Properties();
        try (InputStream input = SelectExample.class.getClassLoader().getResourceAsStream("db.properties")) {
            props.load(input);
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
        String url = props.getProperty("db.url");
        String user = props.getProperty("db.user");
        String password = props.getProperty("db.password");
        String sql = "SELECT id, name, email, age FROM users";
        try (Connection conn = DriverManager.getConnection(url, user, password);
             Statement stmt = conn.createStatement();
             ResultSet rs = stmt.executeQuery(sql)) { // executeQuery 用于 SELECT 语句
            System.out.println("用户列表:");
            System.out.println("---------------------------------");
            while (rs.next()) { // 遍历结果集
                // 通过列名获取数据,更具可读性
                int id = rs.getInt("id");
                String name = rs.getString("name");
                String email = rs.getString("email");
                int age = rs.getInt("age");
                System.out.printf("ID: %d, 姓名: %s, 邮箱: %s, 年龄: %d%n", id, name, email, age);
            }
            System.out.println("---------------------------------");
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

b. 更新数据 (INSERT, UPDATE, DELETE)

对于 INSERT, UPDATE, DELETE 等不返回结果集的语句,应使用 executeUpdate()

import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
public class UpdateExample {
    public static void main(String[] args) {
        Properties props = new Properties();
        // ... (加载配置文件的代码与上面相同) ...
        String url = props.getProperty("db.url");
        String user = props.getProperty("db.user");
        String password = props.getProperty("db.password");
        // 要执行的 SQL 语句
        String sql = "UPDATE users SET age = ? WHERE name = ?";
        try (Connection conn = DriverManager.getConnection(url, user, password);
             // 使用 PreparedStatement 来防止 SQL 注入
             PreparedStatement pstmt = conn.prepareStatement(sql)) {
            // 设置参数 (索引从 1 开始)
            pstmt.setInt(1, 31); // 第一个 ? 设置为 31
            pstmt.setString(2, "Alice"); // 第二个 ? 设置为 "Alice"
            // 执行更新
            int affectedRows = pstmt.executeUpdate();
            System.out.println("更新操作完成,影响了 " + affectedRows + " 行。");
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

最佳实践

a. 使用连接池

每次创建和销毁数据库连接都非常消耗资源,连接池(如 HikariCP, Apache DBCP, C3P0)可以复用已建立的数据库连接,显著提高性能。

使用 HikariCP (目前性能最好的连接池) 的示例:

  1. Maven 依赖:

    <dependency>
        <groupId>com.zaxxer</groupId>
        <artifactId>HikariCP</artifactId>
        <version>5.0.1</version> <!-- 使用最新版本 -->
    </dependency>
  2. 代码:

    import com.zaxxer.hikari.HikariConfig;
    import com.zaxxer.hikari.HikariDataSource;
    import java.sql.Connection;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    public class ConnectionPoolExample {
        private static HikariDataSource dataSource;
        static {
            HikariConfig config = new HikariConfig();
            config.setJdbcUrl("jdbc:mysql://localhost:3306/testdb?useSSL=false&serverTimezone=UTC");
            config.setUsername("root");
            config.setPassword("your_password");
            config.setDriverClassName("com.mysql.cj.jdbc.Driver");
            // 连接池配置
            config.setMaximumPoolSize(10); // 最大连接数
            config.setMinimumIdle(5);      // 最小空闲连接数
            config.setConnectionTimeout(30000); // 连接超时时间 (毫秒)
            config.setIdleTimeout(600000);     // 空闲连接超时时间
            config.setMaxLifetime(1800000);    // 连接最大存活时间
            dataSource = new HikariDataSource(config);
        }
        public static Connection getConnection() throws SQLException {
            return dataSource.getConnection();
        }
        public static void main(String[] args) {
            try (Connection conn = getConnection();
                 Statement stmt = conn.createStatement();
                 ResultSet rs = stmt.executeQuery("SELECT name FROM users")) {
                System.out.println("从连接池获取连接成功!");
                while (rs.next()) {
                    System.out.println(rs.getString("name"));
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

b. 防止 SQL 注入

永远不要使用字符串拼接来构建 SQL 语句! 这会导致严重的 SQL 注入安全漏洞。

错误示例 (SQL 注入):

String userName = "admin' OR '1'='1"; // 恶意输入
String sql = "SELECT * FROM users WHERE name = '" + userName + "'"; // 危险!

正确做法:使用 PreparedStatement

PreparedStatement 会对输入参数进行转义,从而有效防止 SQL 注入,它也是预编译的 SQL 语句,对于多次执行相同 SQL(仅参数不同)的场景性能更好。

// 安全的做法
String sql = "SELECT * FROM users WHERE name = ?"; // 使用 ? 作为占位符
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
    pstmt.setString(1, userName); // 安全地设置参数
    ResultSet rs = pstmt.executeQuery();
    // ...
}

完整示例代码

这是一个结合了配置文件、try-with-resourcesPreparedStatement 和查询功能的完整示例。

UserDAO.java (数据访问对象)

import java.io.InputStream;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
public class UserDAO {
    private Connection getConnection() throws SQLException {
        Properties props = new Properties();
        try (InputStream input = getClass().getClassLoader().getResourceAsStream("db.properties")) {
            if (input == null) {
                throw new SQLException("无法找到 db.properties 文件");
            }
            props.load(input);
        } catch (IOException e) {
            throw new SQLException("加载配置文件失败", e);
        }
        String url = props.getProperty("db.url");
        String user = props.getProperty("db.user");
        String password = props.getProperty("db.password");
        return DriverManager.getConnection(url, user, password);
    }
    public List<String> getAllUserNames() {
        List<String> names = new ArrayList<>();
        String sql = "SELECT name FROM users";
        try (Connection conn = getConnection();
             Statement stmt = conn.createStatement();
             ResultSet rs = stmt.executeQuery(sql)) {
            while (rs.next()) {
                names.add(rs.getString("name"));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return names;
    }
    public void updateUserAge(String name, int newAge) {
        String sql = "UPDATE users SET age = ? WHERE name = ?";
        try (Connection conn = getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql)) {
            pstmt.setInt(1, newAge);
            pstmt.setString(2, name);
            int affectedRows = pstmt.executeUpdate();
            System.out.println("更新年龄操作,影响了 " + affectedRows + " 行。");
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

Main.java (主程序)

public class Main {
    public static void main(String[] args) {
        UserDAO userDAO = new UserDAO();
        // 1. 查询所有用户名
        System.out.println("所有用户名:");
        userDAO.getAllUserNames().forEach(System.out::println);
        System.out.println("--------------------");
        // 2. 更新用户年龄
        System.out.println("将 Bob 的年龄更新为 26...");
        userDAO.updateUserAge("Bob", 26);
        System.out.println("--------------------");
        // 3. 再次查询以验证更新
        System.out.println("更新后的所有用户名:");
        userDAO.getAllUserNames().forEach(System.out::println);
    }
}

常见问题与解决

  • 问题1: No suitable driver found for jdbc:mysql://...

    • 原因: JDBC 驱动没有正确加载或添加到类路径。
    • 解决: 确保您已经下载了 mysql-connector-j.jar 并将其添加到项目的类路径中,如果您使用 Maven,请检查 pom.xml 中的依赖是否正确。
  • 问题2: Access denied for user 'root'@'localhost'

    • 原因: 用户名或密码错误,或者该用户没有权限访问 testdb 数据库。
    • 解决:
      1. 检查代码中的用户名和密码是否与 MySQL 中的设置一致。
      2. 登录 MySQL,使用 GRANT ALL PRIVILEGES ON testdb.* TO 'root'@'localhost'; 命令授权(如果需要)。
  • 问题3: The server time zone value '�йʱ��' is unrecognized...

    • 原因: JDBC 驱动无法识别服务器返回的时区。
    • 解决: 在 JDBC URL 中明确指定时区,在 db.properties 中修改 db.url
      db.url=jdbc:mysql://localhost:3306/testdb?useSSL=false&serverTimezone=UTC

      或者根据您的时区设置为 Asia/Shanghai 等。

  • 问题4: Communications link failure

    • 原因: 无法连接到 MySQL 服务器。
    • 解决:
      1. 确认 MySQL 服务正在运行。
      2. 检查 hostport 是否正确(默认 localhost:3306)。
      3. 检查防火墙是否阻止了端口 3306。

希望这份详细的指南能帮助您顺利地在 Java 中连接 MySQL 数据库!

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