Java与SQL Server数据库连接的实践与要点
本文还有配套的精品资源,点击获取
简介:Java和SQL Server数据库交互是企业级应用开发中的重要环节。本文详细探讨了使用Java通过JDBC连接到SQL Server数据库的过程,包括加载驱动、建立连接、执行SQL语句、处理异常、资源管理、事务处理和连接池的使用。通过实例代码,介绍了如何在Java中实现数据库连接、执行查询和更新操作、以及进行事务控制等关键步骤。
1. Java数据库连接(JDBC)概述
Java数据库连接(JDBC)是一种用于执行SQL语句的Java API,它是Java SE标准中的一个部分,使得Java程序员可以编写与数据库进行交互的应用程序。JDBC为开发者提供了一套标准的数据库操作接口,通过这些接口,可以实现跨数据库平台的数据库操作代码,同时保持了代码的可移植性和数据库的独立性。
JDBC API中定义了四个主要的接口: Driver
, Connection
, Statement
, 和 ResultSet
,分别负责与数据库驱动通信、创建连接、执行SQL语句和处理查询结果。通过这些接口,开发者可以完成从连接数据库到获取数据等一系列操作。
JDBC不仅支持传统的SQL数据库,还包括对NoSQL数据库的支持。随着Java技术的发展,JDBC不断演进,以适应新的数据库技术和应用场景,例如支持连接池管理、事务控制、存储过程调用等高级特性。JDBC的灵活性和强大的功能,使其成为了Java应用中数据库交互的核心技术之一。
2. SQL Server JDBC驱动的加载与配置
2.1 JDBC驱动的作用与分类
2.1.1 驱动的工作原理
JDBC(Java Database Connectivity)驱动是Java应用程序与数据库之间通信的桥梁。其核心作用是将Java应用程序的数据库操作请求转换为数据库能够理解并执行的命令。这些命令通常是SQL语句。当一个Java应用程序请求数据库连接时,JDBC驱动会提供这种连接并允许执行SQL命令。
驱动程序通过实现Java.sql和javax.sql接口来完成其工作。驱动一般分为四类:
- JDBC-ODBC桥驱动 :通过本地的ODBC(Open Database Connectivity)驱动来访问数据库。
- 本地API部分Java驱动 :这些驱动使用本地代码(如C或C++)实现,并通过Java本地接口(JNI)调用。
- 网络协议部分Java驱动 :使用中间网络层将客户端请求转换为数据库可以理解的协议。
- 本地协议纯Java驱动 :直接与数据库通信,不使用中间协议。
2.1.2 SQL Server JDBC驱动的特点
SQL Server JDBC驱动是由微软官方提供,确保了与SQL Server数据库的紧密集成和最佳性能。特点包括:
- 性能优化 :针对SQL Server进行了优化,以提供更好的性能。
- 安全性 :支持最新的安全特性,例如加密连接和Windows身份验证。
- 兼容性 :与SQL Server版本保持更新同步,支持新功能和兼容性改进。
- 易用性 :提供了易于使用的API,便于开发者快速集成数据库功能。
2.2 驱动的加载方式
2.2.1 Class.forName()方法
传统加载JDBC驱动的方式是使用 Class.forName()
方法。当调用这个方法时,JVM会查找指定的类,并且加载它。通过这种方式,驱动程序在加载时可以执行初始化代码,例如注册驱动到JDBC驱动管理器中。
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
加载后, SQLServerDriver
类的静态代码块将被执行,如下所示:
static {try {register();} catch (SQLException var1) {throw new RuntimeException("Can not register the driver!");}
}
这段代码会调用 DriverManager.registerDriver
方法,注册驱动类到JDBC驱动管理器中,为之后的数据库连接提供支持。
2.2.2 驱动加载的替代方法
随着JDBC驱动版本的更新,微软推荐使用依赖注入的方式加载驱动。这种方式更加简洁,不需要显式调用 Class.forName()
,因为驱动类会由依赖注入容器自动加载。
在Java项目中使用Maven进行依赖管理时,可以添加以下依赖到 pom.xml
文件中:
<dependency><groupId>com.microsoft.sqlserver</groupId><artifactId>sqlserver-jdbc</artifactId><version>版本号</version>
</dependency>
添加依赖后,当创建数据库连接时,JDBC驱动会自动被加载。
2.3 驱动配置实战
2.3.1 驱动版本选择与依赖管理
选择合适的SQL Server JDBC驱动版本非常重要,因为每个版本可能对Java API的支持有所不同,同时也会影响性能和安全性。
在企业项目中,版本管理是通过依赖管理工具来实现的。以Maven为例,项目依赖配置如下:
<dependency><groupId>com.microsoft.sqlserver</groupId><artifactId>sqlserver-jdbc</artifactId><version>10.2.1.jre8</version>
</dependency>
通过指定版本号,Maven会从中央仓库中下载相应的JDBC驱动包,并解决依赖关系。
2.3.2 连接URL格式与配置项解读
连接URL是一个字符串,由多个部分组成,它告诉驱动程序如何连接到数据库。对于SQL Server,标准的连接URL格式如下:
jdbc:sqlserver://<host>:<port>;databaseName=<databaseName>;user=<username>;password=<password>
其中各部分的含义如下:
jdbc:sqlserver://
:这是JDBC驱动的标准URL前缀。<host>
:数据库服务器的IP地址或主机名。<port>
:数据库监听的端口,默认通常是1433。databaseName
:要连接的数据库的名称。user
和password
:访问数据库的用户名和密码。
完整的配置示例如下:
String connectionUrl = "jdbc:sqlserver://localhost:1433;databaseName=AdventureWorks;user=sa;password=MyPassword!";
通过正确配置这些参数,可以确保Java应用程序能够成功连接到SQL Server数据库。
3. 数据库连接的建立与关闭操作
数据库连接是JDBC应用的核心,直接关系到程序的性能和稳定性。在本章节中,我们将深入探讨如何建立和关闭数据库连接,以及连接池的使用,旨在提供高效且稳定的数据访问。
3.1 连接建立的步骤与方法
数据库连接的建立是通过获取一个 Connection
对象来实现的,这是与数据库进行交互的起点。
3.1.1 Connection对象的获取
在JDBC中,通过 DriverManager.getConnection()
方法获取到 Connection
对象。这个方法通常接收一个URL格式的字符串,用于指定数据库的位置和数据库特定的连接信息。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;public class DatabaseConnectionExample {public static void main(String[] args) {Connection conn = null;try {// 加载并注册JDBC驱动Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");// 建立数据库连接String url = "jdbc:sqlserver://localhost:1433;databaseName=SampleDB";conn = DriverManager.getConnection(url, "username", "password");// 连接成功后的逻辑处理System.out.println("Connection established");} catch(ClassNotFoundException | SQLException e) {e.printStackTrace();} finally {// 关闭连接if (conn != null) {try {conn.close();} catch (SQLException e) {e.printStackTrace();}}}}
}
在上面的代码中, Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver")
负责加载SQL Server JDBC驱动。 DriverManager.getConnection()
方法接受三个参数:URL、用户名和密码。成功获取到 Connection
对象后,就可以进行数据操作了。
3.1.2 连接字符串的构建与使用
连接字符串(URL)是连接数据库的关键,不同的数据库使用不同的格式。在构建连接字符串时,需要指定如下信息:
- 协议:通常是
jdbc
。 - 子协议:例如
sqlserver
表示要连接的数据库是SQL Server。 - 数据库地址:可能包括主机名和端口号。
- 数据库名:指定要连接到的数据库名称。
示例代码中使用的连接字符串为 jdbc:sqlserver://localhost:1433;databaseName=SampleDB
。注意,实际编码中需要根据实际环境替换主机名、端口号、数据库名和认证信息。
3.2 连接池的使用
在多用户环境下,频繁地建立和关闭数据库连接会消耗大量系统资源。连接池技术可以缓存一定数量的数据库连接,避免重复的连接和断开操作。
3.2.1 连接池的概念与优势
连接池是一种管理资源池,预先创建好一定数量的连接并维护它们的生命周期。当应用程序需要访问数据库时,可以从池中借用一个连接,用完后,再归还给池子。这样大大减少了连接的开销,提高了程序的响应速度。
3.2.2 常用连接池的配置与使用示例
常用的连接池有C3P0、HikariCP、Apache DBCP等。下面以HikariCP为例,展示如何在Java应用中配置和使用连接池。
首先需要添加HikariCP的依赖到项目中:
<!-- pom.xml 中添加依赖 -->
<dependency><groupId>com.zaxxer</groupId><artifactId>HikariCP</artifactId><version>5.0.1</version>
</dependency>
然后在Java代码中配置连接池:
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;public class HikariCPExample {public static void main(String[] args) {HikariConfig config = new HikariConfig();config.setJdbcUrl("jdbc:sqlserver://localhost:1433;databaseName=SampleDB");config.setUsername("username");config.setPassword("password");config.addDataSourceProperty("cachePrepStmts", "true");config.addDataSourceProperty("prepStmtCacheSize", "250");config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");DataSource ds = new HikariDataSource(config);Connection conn = null;try {conn = ds.getConnection();System.out.println("Connection obtained from the pool");// 数据库操作逻辑} catch (SQLException e) {e.printStackTrace();} finally {if (conn != null) {try {conn.close();} catch (SQLException e) {e.printStackTrace();}}}}
}
在这段代码中,我们创建了一个 HikariConfig
对象,并对它进行了一系列的配置,包括数据库的URL、用户名、密码以及其他连接池的参数。然后使用这个配置创建了一个 HikariDataSource
实例,这个实例就是我们的连接池。通过 ds.getConnection()
方法,我们可以从连接池中获取一个连接。
3.3 连接关闭的最佳实践
正确关闭数据库连接是资源管理的重要一环,可以防止资源泄露,并保持应用的稳定性。
3.3.1 使用try-with-resources确保关闭
为了防止忘记关闭数据库连接,可以利用Java 7引入的try-with-resources语句自动管理资源。这样可以保证即使在发生异常的情况下,资源也能被正确关闭。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;public class TryWithResourcesExample {public static void main(String[] args) {String url = "jdbc:sqlserver://localhost:1433;databaseName=SampleDB";try (Connection conn = DriverManager.getConnection(url, "username", "password")) {// 数据库操作逻辑System.out.println("Connection established and closed automatically");} catch (SQLException e) {e.printStackTrace();}}
}
在这个例子中, Connection
对象在try括号内被创建,这意味着当try块执行完毕后,无论是否发生异常, Connection
对象都会被自动关闭。
3.3.2 关闭时机与资源泄露预防
关闭数据库连接的最佳时机是在数据操作完成后,且在事务的边界上。通常,每个数据库操作都应当包含开启事务、执行操作和关闭事务三个步骤。确保在每个操作块后关闭连接可以避免资源泄露。
预防资源泄露的另一个重要措施是检查代码中是否有未关闭的资源。在IDE中可以启用资源泄露的检查,例如在IntelliJ IDEA中设置检测未关闭的资源。
try (Connection conn = ...) {try (Statement stmt = conn.createStatement()) {// 执行SQL查询} // Statement会在try-with-resources代码块结束时自动关闭
} // Connection会在try-with-resources代码块结束时自动关闭
在上述代码块中, Statement
和 Connection
分别被 try-with-resources
语句管理,确保它们的关闭操作在不再需要时自动执行。
通过本章的介绍,我们已经了解了如何建立和关闭数据库连接,以及如何使用连接池来优化资源的使用。这些操作对于保持Java应用程序的性能至关重要,能够有效避免常见的资源泄露和性能问题。接下来的章节将深入探讨SQL语句的执行与优化,进一步提升数据操作的效率和安全性。
4. SQL语句的执行与优化
SQL语句的执行是数据库交互中最为关键的部分,直接影响到程序的性能和数据的准确性。本章节将详细介绍SQL语句执行的机制、执行过程中的优化技巧以及如何安全高效地管理数据库资源。
4.1 SQL语句的执行机制
4.1.1 Statement与PreparedStatement的区别
在JDBC中,执行SQL语句主要有两种方式:使用Statement对象和使用PreparedStatement对象。这两种方式在执行机制和使用上有所不同,各有优势。
Statement对象用于执行静态SQL语句。每次调用其executeQuery()或executeUpdate()方法时,JDBC驱动都会创建一个新的SQL语句,并向数据库服务器发送以执行。这种方式适合于一次性操作,如执行一次性的SQL查询。
Statement stmt = conn.createStatement();
String sql = "SELECT * FROM users WHERE id = 1";
ResultSet rs = stmt.executeQuery(sql);
PreparedStatement对象是对Statement的增强,用于执行预编译的SQL语句。通过使用占位符(),可以有效地防止SQL注入攻击,并可重用预编译的SQL语句,提高执行效率。
PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM users WHERE id = ?");
pstmt.setInt(1, 1);
ResultSet rs = pstmt.executeQuery();
4.1.2 SQL注入的防范策略
SQL注入是一种常见的安全攻击方式,攻击者通过在输入的SQL语句中嵌入恶意代码来干扰或破坏正常的SQL命令执行。PreparedStatement通过使用参数化查询有效地防止了SQL注入,因为它在执行之前将所有输入数据视为参数。
4.2 SQL语句的编译与执行
4.2.1 Statement对象的使用
使用Statement对象执行SQL语句时,需要注意SQL语句的编写和异常处理。Statement允许执行任何类型的SQL语句,包括数据定义语言(DDL)和数据操作语言(DML)。
Statement stmt = conn.createStatement();
String createTableSQL = "CREATE TABLE IF NOT EXISTS users (" +"id INT PRIMARY KEY AUTO_INCREMENT, " +"username VARCHAR(255) NOT NULL, " +"password VARCHAR(255) NOT NULL)";
stmt.executeUpdate(createTableSQL);
在使用Statement时,如果遇到错误或需要执行批处理,需要合理处理SQL异常。
4.2.2 PreparedStatement对象的使用
PreparedStatement不仅提高了SQL语句的安全性,还优化了性能。预编译的SQL语句在执行前就已经被数据库编译过,因此在多次执行相同的SQL语句时,可以节省编译时间。
String insertSQL = "INSERT INTO users (username, password) VALUES (?, ?)";
try(PreparedStatement pstmt = conn.prepareStatement(insertSQL)) {pstmt.setString(1, "admin");pstmt.setString(2, "admin123");int affectedRows = pstmt.executeUpdate();System.out.println("插入成功!受影响的行数:" + affectedRows);
}
使用PreparedStatement时,参数的设置是关键,它决定了SQL语句的灵活性和安全性。
4.3 SQL执行优化技巧
4.3.1 SQL语句的性能调优
SQL语句的性能调优是数据库性能优化的重要环节。优化可以从多个角度进行,例如:
- 减少不必要的数据返回,使用SELECT时指定需要的列,而不是使用SELECT *。
- 使用合适的索引,包括但不限于在JOIN操作的字段上创建索引。
- 分析查询计划,确保使用了最优的查询策略。
- 避免在WHERE子句中使用函数,这可能导致无法使用索引。
4.3.2 索引与查询优化
索引是数据库优化查询性能的重要手段,合理的索引可以大大提高数据检索的速度。但是索引并不是越多越好,过多的索引会增加写操作的负担,甚至可能导致查询效率下降。
CREATE INDEX idx_username ON users(username);
在创建索引时,应考虑以下几点:
- 索引的字段选择,通常选择查询中经常用作过滤条件的字段。
- 索引的类型选择,例如是否使用复合索引、单列索引。
- 监控和分析索引的使用情况,定期维护和优化索引。
通过上述措施,SQL语句执行的性能可以得到显著提高。然而,优化是一个持续的过程,需要不断地监控、分析和调整。
5. 异常处理与资源管理
5.1 JDBC中的异常体系
JDBC中的异常处理是一个关键组成部分,它帮助开发者捕捉和响应在数据库操作过程中可能发生的各种错误情况。
5.1.1 SQL异常处理机制
在JDBC中,所有的数据库相关异常都属于 java.sql.SQLException
类,这是一个检查型异常(checked exception)。当发生错误时,JDBC驱动会抛出此类异常,开发者需要对其进行处理。异常通常分为两大类:SQL错误和非SQL错误。
- SQL错误 :例如,当数据库服务器无法理解或执行传入的SQL语句时,会抛出这种异常。
- 非SQL错误 :例如,当数据库连接因为网络问题而中断时,会抛出这种异常。
5.1.2 捕获与分类异常
开发者应当使用try-catch语句块来捕获和处理这些异常。对于JDBC异常,可以按照以下方式进行分类处理:
- 可重试的异常 :比如数据库连接失败,可能是因为暂时的网络问题,可以尝试重试操作。
- 业务逻辑错误 :这类异常通常表示业务规则被违反,比如尝试插入重复的主键值。
- 非预期的系统错误 :这类异常表示发生了不可预见的错误,比如数据库服务崩溃,通常需要记录错误日志并通知系统管理员。
5.2 资源管理的策略
在JDBC操作中,管理好数据库资源是非常重要的,尤其是在操作结束后要确保资源被正确关闭。
5.2.1 自动资源管理的实现
在Java 7及之后的版本中,引入了try-with-resources语句,它可以自动管理实现了 AutoCloseable
接口的资源。使用try-with-resources,可以确保资源在使用完毕后被正确关闭,即使在发生异常的情况下也不例外。这对于关闭数据库连接、语句和结果集等资源来说是非常有用的。
try (Connection conn = DriverManager.getConnection(dbUrl, user, password);Statement stmt = conn.createStatement();ResultSet rs = stmt.executeQuery("SELECT * FROM table")) {// 处理结果集
} catch (SQLException e) {// 处理SQL异常
}
5.2.2 手动资源管理与注意事项
尽管try-with-resources提供了便捷的自动资源管理方式,但在某些情况下,你可能仍需要手动关闭资源。这需要确保在所有代码路径上都能够正确关闭资源,包括在try块中抛出异常后的情况。
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {conn = DriverManager.getConnection(dbUrl, user, password);stmt = conn.createStatement();rs = stmt.executeQuery("SELECT * FROM table");// 处理结果集
} catch (SQLException e) {// 处理SQL异常
} finally {if (rs != null) {try {rs.close();} catch (SQLException e) {// 记录日志,但不抛出异常}}if (stmt != null) {try {stmt.close();} catch (SQLException e) {// 记录日志,但不抛出异常}}if (conn != null) {try {conn.close();} catch (SQLException e) {// 记录日志,但不抛出异常}}
}
在手动管理资源时,要特别注意异常的处理。在finally块中,即使原先的try块中发生了异常,也应该确保资源能够被关闭。
5.3 异常与资源管理实战
在实际开发中,异常处理与资源管理的结合使用是一个需要深思熟虑的环节。
5.3.1 复杂业务逻辑中的异常处理
在复杂业务逻辑中,一个操作可能会涉及多个数据库操作和外部系统调用。在这种情况下,应当采用分层处理异常的策略。
try {// 开始事务connection.setAutoCommit(false);// 执行多个数据库操作// ...// 如果所有操作都成功,则提交事务***mit();
} catch (Exception e) {// 回滚事务以防止数据不一致connection.rollback();// 根据异常类型进行分类处理if (e instanceof SQLIntegrityConstraintViolationException) {// 处理违反完整性约束的异常} else if (e instanceof SQLTimeoutException) {// 处理数据库超时异常} else {// 处理其他类型的SQL异常}// 记录异常日志logger.error("数据库操作异常", e);
} finally {// 确保连接被关闭if (connection != null) {try {connection.close();} catch (SQLException e) {logger.error("无法关闭数据库连接", e);}}
}
5.3.2 多资源操作的事务管理
在涉及多个资源(如多个数据库连接或外部服务)的操作时,事务管理变得更为复杂。在这些情况下,需要确保所有资源的操作要么全部成功,要么全部回滚。
// 假设有一个操作需要同时操作两个数据库
Connection db1Conn = null;
Connection db2Conn = null;
try {db1Conn = getDB1Connection();db2Conn = getDB2Connection();db1Conn.setAutoCommit(false);db2Conn.setAutoCommit(false);// 执行操作executeOnDB1(db1Conn);executeOnDB2(db2Conn);// 提交两个数据库的事务***mit();***mit();
} catch (Exception e) {// 如果发生异常,则回滚两个数据库的事务try {if (db1Conn != null) db1Conn.rollback();} catch (SQLException ex) {logger.error("回滚db1事务失败", ex);}try {if (db2Conn != null) db2Conn.rollback();} catch (SQLException ex) {logger.error("回滚db2事务失败", ex);}// 记录异常日志logger.error("事务操作失败", e);
} finally {// 关闭所有连接if (db1Conn != null) {try {db1Conn.close();} catch (SQLException e) {logger.error("关闭db1连接失败", e);}}if (db2Conn != null) {try {db2Conn.close();} catch (SQLException e) {logger.error("关闭db2连接失败", e);}}
}
在本章中,我们详细讨论了JDBC中异常处理与资源管理的策略和技巧。通过对异常的准确分类和处理,以及使用try-with-resources语句实现自动资源管理,开发者可以编写出更加健壮和易于维护的数据库操作代码。在后续章节中,我们将继续深入探讨PreparedStatement的高级应用以及ResultSet结果集的处理与事务处理策略。
6. PreparedStatement的高级应用
在处理数据库操作时,安全性、效率以及灵活性是我们经常需要考虑的关键因素。JDBC中PreparedStatement对象的引入正是为了解决这些问题。本章将深入探讨PreparedStatement的高级应用,包括它的优势与使用场景、参数的设置、批量处理以及如何调用存储过程和构建动态SQL。
6.1 PreparedStatement的优势与使用场景
6.1.1 预编译语句与性能提升
PreparedStatement是JDBC提供的一个接口,它允许执行预编译的SQL语句。这意味着在第一次使用PreparedStatement时,SQL语句会先被数据库编译,随后反复执行时就可以直接使用已经编译好的执行计划。这一特性在执行多次相似查询时,可以大大提升性能。
在Java代码中,创建PreparedStatement的一个基本示例如下:
Connection conn = null;
PreparedStatement pstmt = null;
try {conn = dataSource.getConnection();String sql = "SELECT * FROM users WHERE id = ?";pstmt = conn.prepareStatement(sql);pstmt.setInt(1, 1);ResultSet rs = pstmt.executeQuery();// 处理结果集...
} catch (SQLException e) {e.printStackTrace();
} finally {// 关闭资源...
}
在上面的代码中, ?
是一个参数标记,它允许我们在后续的执行中设置不同的参数值,而不是每次都重新编译整个SQL语句。
6.1.2 防范SQL注入攻击
使用PreparedStatement另一个显著的优势是防范SQL注入攻击。SQL注入是一种常见的网络攻击手段,攻击者通过在SQL语句中注入恶意SQL代码,试图对数据库进行未授权的操作。由于PreparedStatement使用参数化查询,任何传入的参数值都会被数据库视为数据而不会被执行为SQL代码的一部分。
例如,如果试图通过以下代码来执行注入:
String user = "admin' OR '1'='1";
String sql = "SELECT * FROM users WHERE username = '" + user + "'";
PreparedStatement pstmt = conn.prepareStatement(sql);
由于PreparedStatement的参数化,上述注入尝试将不会成功,因为’1’='1’将不会被解释为代码的一部分,而是被数据库视为普通字符串值。
6.2 参数的设置与高级特性
6.2.1 参数的类型与设置方法
PreparedStatement支持多种类型的参数设置。根据不同的数据类型,如整数、浮点数、字符串、日期等,需要使用不同的 setXXX
方法来设置相应的值。以下是一些常见类型的设置方法:
pstmt.setInt(1, value); // 设置第一个参数为整数
pstmt.setString(2, value); // 设置第二个参数为字符串
pstmt.setDouble(3, value); // 设置第三个参数为双精度浮点数
pstmt.setDate(4, new java.sql.Date(date.getTime())); // 设置日期
// 其他类型如setFloat, setLong等
当涉及到日期时间类型参数时,推荐使用Java 8引入的 java.time
包中的类,因为它们提供了更好的时间日期API。
6.2.2 批量处理与效率优化
PreparedStatement还支持批量处理,这允许我们一次性发送多个SQL语句到数据库执行,从而显著提升性能,特别是用于插入、更新或删除大量数据时。以下是一个批量处理的示例:
pstmt.setInt(1, value1);
pstmt.addBatch();
pstmt.setInt(1, value2);
pstmt.addBatch();
// ...
pstmt.executeBatch(); // 执行批量处理
6.3 PreparedStatement的高级操作
6.3.1 存储过程的调用
存储过程是存储在数据库中的一组预编译的SQL语句。它们可以被当作数据库中的一个函数调用,通常用于实现复杂的业务逻辑。使用PreparedStatement调用存储过程非常简单:
String callStoredProcedure = "{ call myStoredProcedure(?, ?) }";
pstmt = conn.prepareStatement(callStoredProcedure);
pstmt.setInt(1, value1);
pstmt.setString(2, value2);
ResultSet rs = pstmt.executeQuery();
6.3.2 动态SQL的构建
有时我们需要构建动态的SQL语句,可能因为不同的业务逻辑或条件。尽管拼接字符串的方式可以实现,但使用PreparedStatement的动态SQL构建功能更为安全。这可以通过在SQL语句中嵌入条件和控制逻辑来实现:
String sql = new StringBuilder("SELECT * FROM users WHERE id = ?").append(" AND name = ?").append(" AND age > ?").toString();
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, 1);
pstmt.setString(2, "John");
pstmt.setInt(3, 30);
ResultSet rs = pstmt.executeQuery();
以上代码展示了如何创建一个动态SQL语句,并通过PreparedStatement来安全地执行它。
通过本章节的介绍,我们可以看到PreparedStatement不仅能够提供性能上的优化,而且在安全性、灵活性方面都有显著的提升。了解并熟练使用PreparedStatement的高级特性,对于构建健壮的数据库交互应用至关重要。
7. ResultSet结果集的处理与事务处理
7.1 ResultSet的遍历与操作
7.1.1 结果集的类型与游标移动
在使用JDBC处理数据库查询操作时, ResultSet
对象用来存储SQL查询返回的数据集。 ResultSet
通过游标的方式允许我们遍历结果集中的每一行数据。在Java中, ResultSet
对象有以下几种类型:
TYPE_FORWARD_ONLY
(默认):游标只能向前移动;TYPE_SCROLL_INSENSITIVE
:游标可以前后移动,且对底层数据的修改不敏感;TYPE_SCROLL_SENSITIVE
:游标可以前后移动,且对底层数据的修改敏感。
选择合适的 ResultSet
类型取决于你对结果集的处理需求。例如,如果你需要遍历结果集多次或者需要随机访问结果集中的记录,则需要 TYPE_SCROLL_INSENSITIVE
或 TYPE_SCROLL_SENSITIVE
。请注意,后两种类型可能会引起性能下降,因为它们需要更多的资源来维护结果集状态。
遍历 ResultSet
的一个基本例子:
try (ResultSet rs = stmt.executeQuery("SELECT * FROM users")) {while (rs.next()) {String username = rs.getString("username");int age = rs.getInt("age");// 处理每一行的数据System.out.println("User: " + username + ", Age: " + age);}
}
在上面的代码中, rs.next()
方法会将游标从当前行向前移动到下一行。如果游标可以后退,我们也可以使用 rs.previous()
。
7.1.2 更新与删除操作的处理
ResultSet
提供了更新和删除数据行的能力。这需要结果集支持 CONCUR_UPDATABLE
,通常与可滚动的游标类型一起使用。如果要更新或删除数据,需要通过 Connection
对象创建一个可更新的 ResultSet
:
try (Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE)) {ResultSet rs = stmt.executeQuery("SELECT * FROM users");while (rs.next()) {if (rs.getInt("age") > 30) {// 更新操作rs.updateInt("age", rs.getInt("age") - 1);rs.updateRow(); // 提交更新// 删除操作// rs.deleteRow(); // 调用删除行的方法}}
}
在此代码段中,如果用户年龄大于30岁,则将年龄减一,并使用 updateRow()
方法更新行。 deleteRow()
方法可以用来删除当前游标位置上的行。
7.2 事务的概念与管理
7.2.1 事务的基本原理
事务是一组操作的集合,这些操作要么全部成功,要么全部失败回滚。事务具有ACID属性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。在JDBC中,事务通过 Connection
对象控制,这包括开始事务、提交事务以及回滚事务。
事务的默认行为是自动提交,这意味着每条语句执行完毕后都会立即被提交。要实现事务控制,首先需要关闭自动提交:
Connection conn = DriverManager.getConnection(url, user, password);
conn.setAutoCommit(false); // 关闭自动提交
一旦关闭自动提交,就需要手动管理事务的提交和回滚:
try {// 执行业务逻辑***mit(); // 提交事务
} catch (Exception e) {conn.rollback(); // 出现异常时回滚事务
}
在上述代码中,业务逻辑完成后需要调用 commit()
方法来提交事务。如果捕获到异常,则调用 rollback()
来回滚事务。
7.3 高级事务处理策略
7.3.1 事务隔离级别与并发控制
在数据库系统中,不同的事务隔离级别可以提供不同程度的隔离以防止事务间相互干扰。隔离级别包括:
READ_UNCOMMITTED
:读未提交数据;READ_COMMITTED
:读已提交数据;REPEATABLE_READ
:可重复读;SERIALIZABLE
:可串行化。
设置事务隔离级别通常使用 setTransactionIsolation
方法:
conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
选择合适的隔离级别是至关重要的,因为它会直接影响到并发操作的性能和数据的一致性。较低的隔离级别可能导致更多的并发,但增加了数据不一致的风险。较高的隔离级别则可以确保数据一致性,但可能会减少并发操作。
7.3.2 分布式事务处理与XA事务
在分布式系统中,一个事务可能需要跨多个资源进行操作,比如多个数据库或应用服务器。这时候就需要使用分布式事务,而JDBC提供了对分布式事务的支持,通过XA事务协议实现。XA事务通过两阶段提交保证多个资源上的操作要么全部提交,要么全部回滚。
使用分布式事务需要支持XA的资源管理器(如数据库),以及一个事务协调器(Transaction Manager)。Java通过 javax.transaction.xa.XAResource
接口管理这些资源:
UserTransaction ut = (UserTransaction) new InitialContext().lookup("java:comp/UserTransaction");
ut.begin();
try {// 跨资源的操作***mit();
} catch (Exception e) {ut.rollback();
}
在此代码段中,我们使用了 UserTransaction
接口来开始、提交和回滚事务。需要注意的是,要确保应用程序服务器和数据库支持分布式事务。
通过本章的介绍,我们了解了如何处理JDBC的 ResultSet
结果集以及如何管理和控制事务。理解这些高级概念对于开发健壮和高效的数据库应用至关重要。接下来我们将转向 PreparedStatement
的高级应用。
本文还有配套的精品资源,点击获取
简介:Java和SQL Server数据库交互是企业级应用开发中的重要环节。本文详细探讨了使用Java通过JDBC连接到SQL Server数据库的过程,包括加载驱动、建立连接、执行SQL语句、处理异常、资源管理、事务处理和连接池的使用。通过实例代码,介绍了如何在Java中实现数据库连接、执行查询和更新操作、以及进行事务控制等关键步骤。
本文还有配套的精品资源,点击获取
]
相关文章:

Java与SQL Server数据库连接的实践与要点
本文还有配套的精品资源,点击获取 简介:Java和SQL Server数据库交互是企业级应用开发中的重要环节。本文详细探讨了使用Java通过JDBC连接到SQL Server数据库的过程,包括加载驱动、建立连接、执行SQL语句、处理异常、资源管理、事务处理和连…...

客户案例:基于慧集通的致远OA与海康威视智能会议设备集成方案
一、引言 本案例原型公司是我国生产纺织原料的大型上市企业,主导产品为再生纤维素长丝、氨纶等系列产品。公司产品不仅得到国内客户认可,还远销海外,合作伙伴遍布德国、意大利、日本、韩国、土耳其、印度等30多个国家和地区。 二、简介 &am…...

嵌入式驱动开发详解7(并发、竞争、中断)
文章目录 前言并发和竞争原子操作自旋锁信号量互斥体 中断中断简介中断API上半部和下半部设备树分析中断号获取源码 后续参考文献 前言 中断会引起线程的切换,并发和竞争也是对线程切换的一种灵活保护和处理,因此这里将中断和并发与竞争放在一块讲解说明…...
@repository注解
Repository 是 Spring 框架中的一个注解,主要用于标记类作为数据访问层(Data Access Layer, DAL)的组件。在分层架构中,Repository 注解标识的类负责与数据库或其他持久化存储进行交互,如执行查询、保存或更新数据等操…...
Linux top指令
top指令概述 top 是 Linux 系统中用于实时监控系统性能和进程信息的命令,功能强大且灵活。它提供了系统资源的动态视图,包括 CPU、内存、运行中的进程等。 这个指令可以说是Linux中最基本的工具了,用来监视系统的实时运行状态,类…...

Anaconda 安装与虚拟环境创建完整指南
Anaconda 安装与虚拟环境创建完整指南 Anaconda 是目前最流行的 Python 和数据科学工具集之一,它不仅可以轻松管理 Python 包,还能提供强大的虚拟环境功能,避免项目之间的依赖冲突。如果你是机器学习、数据科学或计算机视觉的开发者…...
基于自然语言处理(NLP)的智能客服系统
基于自然语言处理(NLP)的智能客服系统是现代客户服务领域的一项重要技术,它通过模拟人类对话的方式,为用户提供及时、准确和个性化的服务。以下是关于基于NLP的智能客服系统的一些关键要素和功能: 1. 自然语言理解&am…...
C语言期末复习题(PTA)
去字符串中的重复字符与空格 dele函数的功能:对给定的字符串,去掉重复的字符与字符串中的空格后,按照字符ASCII码顺序从小到大排序后输出。 输入样例: abcd efg abc fH 3 2 1 输出样例: 123Habcdefg 测试程序样例&am…...

Ubuntu 中安装 RabbitMQ 教程
简介 RabbitMq作为一款消息队列产品,它由Erlang语言开发,实现AMQP(高级消息队列协议)的开源消息中间件。 应用场景 异步处理 场景说明:用户注册后,注册信息写入数据库,再发邮件、短信通知。 …...
如何测试模型推理精度:Python初学者指南
如何测试模型推理精度:Python初学者指南 什么是模型推理精度?使用工具包:lm-evaluation-harness安装工具包 测试模型推理精度的步骤1. 加载模型和分词器2. 使用 lm-evaluation-harness 进行测试3. 运行脚本4. 查看结果 总结 在机器学习和深度…...

人工智能与区块链的碰撞:双剑合璧的创新前景
引言 人工智能(AI)与区块链技术,这两项曾经各自独立发展的前沿科技,如今正逐步走向融合。人工智能通过强大的数据处理能力和智能决策能力,在各个领域掀起了革命性的变革;而区块链凭借其去中心化、不可篡改的…...
安装Visual studio2022后,没法新建.net 4.5.2的项目
在Visual Studio 2022中无法新建.NET Framework 4.5.2的项目,通常是因为Visual Studio 2022默认不再支持较旧的.NET Framework版本,如4.5或4.5.2。不过,你可以通过一些手动步骤来使Visual Studio 2022支持.NET Framework 4.5.2项目。以下是一…...

小程序配置文件 —— 12 全局配置 - pages配置
全局配置 - pages配置 在根目录下的 app.json 文件中有一个 pages 字段,这里我们介绍一下 pages 字段的具体用法; pages 字段:用来指定小程序由哪些页面组成,用来让小程序知道由哪些页面组成以及页面定义在哪个目录,…...
Flink Data Source详解
注意在高版本中SourceFunction以及其子类RichSourceFunction、ParallelSourceFunction等已经被标记为废弃,所以我们要看数据源的实现只需要关注Source接口(org.apache.flink.api.connector.source.Source)。了解Source背后的架构和运行原理有助于我们更好的使用Source,或者…...
Spring Boot 介绍与应用
什么是 Spring Boot? Spring Boot 是一个用于简化 Spring 应用程序开发和部署的框架,它建立在 Spring 框架的基础之上,但去除了繁琐的配置。Spring Boot 采用“约定优于配置”的原则,默认启用了大量自动配置,使得开发…...
并行编程实战——TBB框架的应用之五Supra中IGTL的应用
一、OpenIGTLink OpenIGTLink,可以简称为IGTL。它是一个专供医疗应用的网络通信库。IGTL可以应用各种传感器、手术机器人和成像仪等的数据传输。OpenIGTLink是有一系列的协议在底层支持的,它是跨平台的,应用也非常简单。 OpenIGTLink迭代还是…...
【Golang 面试题】每日 3 题(八)
✍个人博客:Pandaconda-CSDN博客 📣专栏地址:http://t.csdnimg.cn/UWz06 📚专栏简介:在这个专栏中,我将会分享 Golang 面试中常见的面试题给大家~ ❤️如果有收获的话,欢迎点赞👍收藏…...

11. 日常算法
1. 141. 环形链表 题目来源 给你一个链表的头节点 head ,判断链表中是否有环。 如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接…...

FPGA三模冗余TMR工具(二)
学术和商业领域有许多自动化的三模冗余TMR工具,本文介绍当前主流的基于寄存器传输级的三模冗余工具(Register-Transfer Level,RTL),基于重要软核资源的三模冗余工具,以及新兴的基于高层次综合的三模冗余工具…...

springboot499基于javaweb的城乡居民基本医疗信息管理系统(论文+源码)_kaic
摘 要 信息数据从传统到当代,是一直在变革当中,突如其来的互联网让传统的信息管理看到了革命性的曙光,因为传统信息管理从时效性,还是安全性,还是可操作性等各个方面来讲,遇到了互联网时代才发现能补上自古…...

垂起固定翼无人机应用及技术分析
一、主要应用行业 1. 能源基础设施巡检 电力巡检:适用于超高压输电线路通道的快速巡查,实时回传数据提升智能运检效率。 油田管道监测:利用长航时特性(1.5-2小时)对大范围管道进行隐患排查,减少人力巡…...

C++课设:实现简易文件加密工具(凯撒密码、异或加密、Base64编码)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、初识文件加密:为什么需要…...

全流程开源!高德3D贴图生成系统,白模一键生成真实感纹理贴图
导读 MVPainter 随着3D生成从几何建模迈向真实感还原,贴图质量正逐渐成为决定3D资产视觉表现的核心因素。我们团队自研的MVPainter系统,作为业内首个全流程开源的3D贴图生成方案,仅需一张参考图与任意白模,即可自动生成对齐精确…...

【软件工具】批量OCR指定区域图片自动识别内容重命名软件使用教程及注意事项
批量OCR指定区域图片自动识别内容重命名软件使用教程及注意事项 1、操作步骤1-5: 安装与启动:安装成功后,在桌面或开始菜单找到软件图标,双击启动。 导入图片:进入软件主界面,点击 “导入图片” 按钮&a…...

日志收集工具-Filebeat
提示:windows 环境下 Filebeat 的安装与使用 文章目录 前言一、安装二、配置部署三、启动测试 前言 Filebeat 一般用于日志采集,由两部分组成 :Harvesters 和 prospector Harvesters采集器:逐行读取单个文件的内容,并…...

AI生成的基于html+marked.js实现的Markdown转html工具,离线使用,可实时预览 [
有一个markdown格式的文档,手头只有notepad的MarkdownPanel插件可以预览,但是只能预览,不能直接转换为html文件下载,直接复制预览的内效果又不太好,度娘也能找到很多工具,但是都需要在线使用。所以考虑用AI…...

一个完整的日志收集方案:Elasticsearch + Logstash + Kibana+Filebeat (二)
📄 本地 Windows 部署 Logstash 连接本地 Elasticsearch 指南 ✅ 目标 在本地 Windows 上安装并运行 Logstash配置 Logstash 将数据发送至本地 Elasticsearch测试数据采集与 ES 存储流程 🧰 前提条件 软件版本要求安装说明Java17Oracle JDK 下载 或 O…...

【WPF】WPF 项目实战:用ObservableCollection构建一个可增删、排序的管理界面(含源码)
💡WPF 项目实战:构建一个可增删、排序的光源类型管理界面(含源码) 在实际的图像处理项目中,我们经常需要对“光源类型”进行筛选或管理。今天我们来一步步构建一个实用的 WPF 界面,实现以下功能࿱…...

Reactor和Proactor
reactor的重要组件包括:Event事件、Reactor反应堆、Demultiplex事件分发器、Eventhandler事件处理器。...

论文解读:Locating and Editing Factual Associations in GPT(ROME)
论文发表于人工智能顶会NeurIPS(原文链接),研究了GPT(Generative Pre-trained Transformer)中事实关联的存储和回忆,发现这些关联与局部化、可直接编辑的计算相对应。因此: 1、开发了一种因果干预方法,用于识别对模型的事实预测起…...