目录
- 准备工作
- 1 环境准备
- 2 获取 SQL Server JDBC 驱动
- 3 配置 SQL Server 服务器
- 核心代码实现
- 1 添加 JDBC 驱动到项目
- 2 基本连接代码 (JDBC 4.0+ 自动加载)
- 3 完整的增删改查 示例
- 最佳实践
- 1 使用
try-with-resources管理资源 - 2 使用连接池
- 3 使用 PreparedStatement 防止 SQL 注入
- 1 使用
- 常见问题与解决方案
- 1 "No suitable driver found" 错误
- 2 "The TCP/IP connection to the host has failed" 错误
- 3 "Login failed for user 'sa'" 错误
- 4 "The driver could not establish a secure connection to SQL Server by using Secure Sockets Layer (SSL)" 错误
准备工作
1 环境准备
- Java 开发环境: 确保你已经安装了 JDK (Java Development Kit) 和一个 IDE (如 IntelliJ IDEA, Eclipse, 或 VS Code)。
- SQL Server: 确保你的 SQL Server 实例正在运行,如果你没有安装,可以使用 Docker 快速启动一个,或者下载 SQL Server Developer Edition (免费)。
2 获取 SQL Server JDBC 驱动
微软提供了官方的 JDBC 驱动,你需要下载它并将其添加到你的 Java 项目中。

- 下载地址: Microsoft JDBC Driver for SQL Server
- 选择版本: 根据你的 Java 版本和 SQL Server 版本选择合适的驱动,最新的驱动支持更广泛的 Java 和 SQL Server 版本。
- 下载文件: 下载后会得到一个
.zip压缩包,解压后你会找到一个mssql-jdbc-<version>.jar文件,这就是你需要的 JDBC 驱动库。
3 配置 SQL Server 服务器
为了能让 Java 应用远程连接,你的 SQL Server 服务器需要进行一些基本配置。
-
启用 TCP/IP 协议:
- 打开 SQL Server Configuration Manager。
- 在左侧导航栏中,展开
SQL Server 网络配置。 - 点击
SQLEXPRESS 的协议(或你的实例名称)。 - 在右侧,右键点击
TCP/IP,选择启用。 - 重启 SQL Server 服务以使配置生效。
-
配置防火墙:
- 确保运行 SQL Server 的机器的防火墙允许 TCP 端口 1433 (默认端口) 的入站连接。
-
创建测试用户和数据库 (推荐):
(图片来源网络,侵删)- 使用 SQL Server Management Studio (SSMS) 或其他工具连接到你的 SQL Server。
- 执行以下 T-SQL 语句来创建一个测试数据库和一个有权限的用户:
-- 创建一个名为 'JavaTestDB' 的数据库 CREATE DATABASE JavaTestDB;
-- 切换到新创建的数据库 USE JavaTestDB;
-- 创建一个名为 'java_user' 的登录名,并设置密码 'YourStrongPassword123!' -- 注意:在生产环境中,请使用更复杂的密码 CREATE LOGIN java_user WITH PASSWORD = 'YourStrongPassword123!';
-- 创建一个与登录名同名且映射到当前数据库的用户 CREATE USER java_user FOR LOGIN java_user;
-- 授予该用户在当前数据库中所有对象的权限 GRANT ALL TO java_user;
(图片来源网络,侵删)
核心代码实现
1 添加 JDBC 驱动到项目
对于 Maven 项目:
在你的 pom.xml 文件中添加以下依赖,这是最推荐的方式,因为它会自动处理依赖关系。
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<version>11.2.1.jre17</version> <!-- 请根据你的 Java 版本选择合适的版本 -->
</dependency>
对于 Gradle 项目:
在你的 build.gradle 文件中添加:
implementation 'com.microsoft.sqlserver:mssql-jdbc:11.2.1.jre17' // 请根据你的 Java 版本选择合适的版本
对于手动管理的项目:
- 将下载的
mssql-jdbc-<version>.jar文件复制到你的项目的lib目录下。 - 在你的 IDE 中,右键点击该 JAR 文件,选择 "Add as Library..." 或类似选项,将其添加到项目的构建路径中。
2 基本连接代码
下面是建立连接的核心代码,URL 的格式非常重要。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class SqlServerConnector {
// 数据库连接信息
// 注意:SQL Server 和 Java 应用在同一台机器上,且使用 Windows 身份验证,则不需要用户名和密码
private static final String DB_URL = "jdbc:sqlserver://localhost:1433;databaseName=JavaTestDB;encrypt=false;trustServerCertificate=true;";
private static final String USER = "java_user";
private static final String PASS = "YourStrongPassword123!";
public static void main(String[] args) {
// JDBC 4.0 (Java 6+) 之后,DriverManager 会自动加载类路径下的 JDBC 驱动
// 所以不需要显式地 Class.forName(...)
try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS)) {
if (conn != null) {
System.out.println("连接成功!");
System.out.println("数据库版本: " + conn.getMetaData().getDatabaseProductVersion());
}
} catch (SQLException e) {
System.err.println("连接失败!");
e.printStackTrace();
}
}
}
URL 参数说明:
jdbc:sqlserver://: 固定协议头。localhost:1433: SQL Server 的主机地址和端口号,如果你的服务器不在本机,请替换为实际的 IP 地址或域名。databaseName=JavaTestDB: 要连接的数据库名称。encrypt=false: 对于本地开发或非生产环境,可以禁用加密以简化连接,在生产环境中,应启用并正确配置 SSL。trustServerCertificate=true: 在本地开发时,信任 SQL Server 提供的任何证书(即使它是自签名的)。在生产环境中,这非常不安全,应该设置为false并配置正确的信任存储。
3 完整的增删改查 示例
这个示例展示了如何执行查询、插入、更新和删除操作,并遵循了最佳实践。
import java.sql.*;
public class SqlServerCrudExample {
private static final String DB_URL = "jdbc:sqlserver://localhost:1433;databaseName=JavaTestDB;encrypt=false;trustServerCertificate=true;";
private static final String USER = "java_user";
private static final String PASS = "YourStrongPassword123!";
public static void main(String[] args) {
// 创建表 (如果不存在)
createTable();
// 插入数据
insertData("张三", 30);
insertData("李四", 25);
// 查询并显示所有数据
selectAllData();
// 更新数据
updateAge("张三", 31);
// 再次查询以验证更新
System.out.println("\n更新后的数据:");
selectAllData();
// 删除数据
deleteData("李四");
// 最后一次查询
System.out.println("\n删除后的数据:");
selectAllData();
}
// 创建表
private static void createTable() {
String sql = "IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='Employees' and xtype='U') CREATE TABLE Employees (id INT IDENTITY(1,1) PRIMARY KEY, name NVARCHAR(50), age INT)";
try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);
Statement stmt = conn.createStatement()) {
stmt.execute(sql);
System.out.println("表 'Employees' 创建成功或已存在。");
} catch (SQLException e) {
e.printStackTrace();
}
}
// 插入数据
private static void insertData(String name, int age) {
// 使用 ? 占位符,防止 SQL 注入
String sql = "INSERT INTO Employees (name, age) VALUES (?, ?)";
try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, name);
pstmt.setInt(2, age);
int affectedRows = pstmt.executeUpdate();
System.out.println("插入了 " + affectedRows + " 行数据。");
} catch (SQLException e) {
e.printStackTrace();
}
}
// 查询所有数据
private static void selectAllData() {
String sql = "SELECT id, name, age FROM Employees";
try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql)) {
System.out.println("\n--- 员工列表 ---");
while (rs.next()) {
// 通过列名获取数据,更安全且可读性更好
int id = rs.getInt("id");
String name = rs.getString("name");
int age = rs.getInt("age");
System.out.println("ID: " + id + ", 姓名: " + name + ", 年龄: " + age);
}
} catch (SQLException e) {
e.printStackTrace();
}
}
// 更新数据
private static void updateAge(String name, int newAge) {
String sql = "UPDATE Employees SET age = ? WHERE name = ?";
try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setInt(1, newAge);
pstmt.setString(2, name);
int affectedRows = pstmt.executeUpdate();
System.out.println("\n更新了 " + affectedRows + " 行数据。");
} catch (SQLException e) {
e.printStackTrace();
}
}
// 删除数据
private static void deleteData(String name) {
String sql = "DELETE FROM Employees WHERE name = ?";
try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, name);
int affectedRows = pstmt.executeUpdate();
System.out.println("\n删除了 " + affectedRows + " 行数据。");
} catch (SQLException e) {
e.printStackTrace();
}
}
}
最佳实践
1 使用 try-with-resources
从 Java 7 开始,推荐使用 try-with-resources 语句,它可以自动实现 AutoCloseable 接口的对象(如 Connection, Statement, ResultSet)在 try 块执行完毕后自动关闭,即使发生异常也是如此,这能有效防止资源泄漏。
如上面的 CRUD 示例所示,所有 JDBC 资源都使用了 try-with-resources。
2 使用连接池
在真实的应用程序中,频繁地创建和销毁数据库连接是非常消耗资源的。连接池 应运而生,它预先创建一组数据库连接,并将它们缓存起来,当需要连接时,从池中获取一个,使用完毕后归还给池,而不是关闭。
- 常用连接池: HikariCP (目前性能最好的连接池), Apache DBCP, C3P0。
- 集成方式:
- Spring Boot: 几乎是开箱即用,只需在
application.properties或application.yml中配置即可。 - 手动集成: 将连接池库添加到项目中,然后通过其 API 获取连接。
- Spring Boot: 几乎是开箱即用,只需在
HikariCP 示例 (Spring Boot application.properties):
# HikariCP 配置 spring.datasource.url=jdbc:sqlserver://localhost:1433;databaseName=JavaTestDB;encrypt=false;trustServerCertificate=true; spring.datasource.username=java_user spring.datasource.password=YourStrongPassword123! spring.datasource.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver # HikariCP 连接池特定配置 spring.datasource.hikari.connection-timeout=30000 spring.datasource.hikari.maximum-pool-size=10 spring.datasource.hikari.minimum-idle=5 spring.datasource.hikari.idle-timeout=600000 spring.datasource.hikari.max-lifetime=1800000
3 使用 PreparedStatement
PreparedStatement 是预编译的 SQL 语句,它有两个主要好处:
- 防止 SQL 注入: 它会自动将输入参数进行转义,使得恶意代码无法作为 SQL 命令执行。
- 性能提升: 如果一条 SQL 语句需要被多次执行(只是参数不同),数据库可以只编译一次,多次执行,提高效率。
在执行 INSERT, UPDATE, DELETE 或带有参数的 SELECT 时,始终优先使用 PreparedStatement。
常见问题与解决方案
1 "No suitable driver found for jdbc:sqlserver://..."
- 原因: JDBC 驱动没有被加载到类路径中。
- 解决方案:
- 确认你已将
mssql-jdbc.jar文件添加到项目的构建路径中。 - 如果你使用 Maven 或 Gradle,检查
pom.xml或build.gradle中的依赖是否正确,并执行mvn clean install或gradle build重新下载依赖。 - 注意: 对于 JDBC 4.0+,通常不需要
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");这行代码,如果加上这行代码能解决问题,说明你的类路径配置可能有问题,移除它,确保依赖正确是根本解决办法。
- 确认你已将
2 "The TCP/IP connection to the host has failed..."
- 原因: Java 应用无法通过网络连接到 SQL Server 服务器。
- 解决方案:
- 检查 SQL Server 服务: 确认 SQL Server 服务正在运行。
- 检查 TCP/IP 协议: 确认 SQL Server Configuration Manager 中的 TCP/IP 协议已启用。
- 检查防火墙: 确认运行 SQL Server 的机器的防火墙允许 1433 端口的入站连接,你可以暂时关闭防火墙进行测试。
- 检查连接字符串: 确认 IP 地址、端口和主机名是否正确。
- SQL Server Browser 服务: 如果你使用的是非默认端口或命名实例,需要确保 SQL Server Browser 服务正在运行。
3 "Login failed for user 'sa'."
- 原因: SQL Server 拒绝了
sa用户的登录请求。 - 解决方案:
- 检查密码: 确认你提供的
sa用户密码是正确的。 - 启用 'sa' 账户: 在 SSMS 中,以 Windows 身份验证登录,然后执行以下命令来启用并重置
sa密码:ALTER LOGIN sa ENABLE; GO ALTER LOGIN sa WITH PASSWORD = 'YourNewStrongPassword!'; GO
- 使用 Windows 身份验证: 如果可能,最好使用 Windows 身份验证,它更安全,连接字符串可以简化为
jdbc:sqlserver://localhost;databaseName=JavaTestDB;integratedSecurity=true;。
- 检查密码: 确认你提供的
4 "The driver could not establish a secure connection to SQL Server by using Secure Sockets Layer (SSL)..."
- 原因: JDBC 驱动尝试与 SQL Server 建立加密连接,但配置不匹配或证书有问题。
- 解决方案:
- 本地开发: 最简单的解决方法是在连接字符串中添加
encrypt=false;trustServerCertificate=true;,这会禁用加密并信任所有证书,仅适用于开发环境。 - 生产环境:
- 将
encrypt设置为true。 - 将
trustServerCertificate设置为false。 - 你需要配置 Java 的信任存储库,将 SQL Server 的 CA 证书导入其中,这比较复杂,但能保证安全性。
- 将
- 本地开发: 最简单的解决方法是在连接字符串中添加
