1、连接数据库
建立连接前的准备:在读取数据库之前,需要先确定数据库的类型(如 MySQL、Oracle、SQL Server 等),然后准备好相应的数据库连接信息,包括服务器地址、端口号、数据库名称、用户名以及密码等,对于 MySQL 数据库,可能使用类似“jdbc:mysql://localhost:3306/database_name”这样的连接 URL,localhost”是服务器地址(本地连接时),“3306”是 MySQL 默认的端口号,“database_name”是要连接的数据库名称。
创建连接对象:根据不同的编程语言和数据库驱动,创建与数据库的连接对象,以 Java 语言为例,如果使用 JDBC 驱动连接 MySQL 数据库,代码可能如下:
import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class DatabaseConnection { private static final String URL = "jdbc:mysql://localhost:3306/database_name"; private static final String USER = "username"; private static final String PASSWORD = "password"; public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } }
2、执行查询操作
编写 SQL 查询语句:根据要读取的数据需求,编写相应的 SQL 查询语句,要查询名为“users”表中的所有用户信息,SQL 语句可能是“SELECT FROM users”,如果要查询特定条件的用户,比如年龄大于 25 岁的用户,可以使用“SELECT FROM users WHERE age > 25”。
通过连接对象执行查询:利用创建好的连接对象,创建一个用于执行 SQL 语句的对象(如 Statement 或 PreparedStatement),以 PreparedStatement 为例,它是预编译的 SQL 语句对象,可以提高查询效率和安全性,防止 SQL 注入攻击,以下是使用 PreparedStatement 执行查询的示例代码(Java):
import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; public class ReadDataExample { public void readUsers() { String query = "SELECT FROM users WHERE age > ?"; try (Connection connection = DatabaseConnection.getConnection(); PreparedStatement preparedStatement = connection.prepareStatement(query)) { preparedStatement.setInt(1, 25); ResultSet resultSet = preparedStatement.executeQuery(); while (resultSet.next()) { int id = resultSet.getInt("id"); String name = resultSet.getString("name"); int age = resultSet.getInt("age"); // 处理获取到的数据,例如打印输出 System.out.println("ID: " + id + ", Name: " + name + ", Age: " + age); } } catch (SQLException e) { e.printStackTrace(); } } }
在上述代码中,首先设置了查询条件中的参数值(这里是年龄大于 25),然后执行查询操作,通过 ResultSet 对象来获取查询结果集,并逐行处理数据。
3、处理查询结果
遍历结果集:当执行查询后得到的结果集(ResultSet)包含多行数据时,需要使用循环来遍历每一行,在循环中,可以通过列名或列索引来获取每行各个字段的值,使用“resultSet.getString("column_name")”可以获取指定列名为“column_name”的字段值,使用“resultSet.getInt(1)”可以获取第一列的整数值(假设第一列是整数类型)。
数据类型转换与处理:从数据库中读取的数据可能需要进行适当的类型转换才能在应用程序中使用,从数据库中读取的日期类型的数据可能需要转换为应用程序中对应的日期格式或日期对象,还可能需要对数据进行一些业务逻辑上的处理,比如计算统计数据、筛选特定数据等。
1、连接数据库(同读取时的连接步骤):与从数据库读取数据一样,首先要连接到目标数据库,确保能够与数据库进行交互,连接的方式和所需的连接信息在前面读取部分已有详细介绍,这里不再赘述。
2、执行插入操作
编写 SQL 插入语句:根据要插入的数据结构和目标表的结构,编写相应的 SQL 插入语句,要将一个新的用户信息插入到“users”表中,SQL 语句可能是“INSERT INTO users (name, age, email) VALUES (?, ?, ?)”,name”、“age”和“email”是“users”表的列名,问号“?”是占位符,用于后续设置具体的值。
通过连接对象执行插入:同样可以使用 PreparedStatement 对象来执行插入操作,以下是一个简单的插入用户信息的示例代码(Java):
import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; public class InsertDataExample { public void insertUser(String name, int age, String email) { String insertSql = "INSERT INTO users (name, age, email) VALUES (?, ?, ?)"; try (Connection connection = DatabaseConnection.getConnection(); PreparedStatement preparedStatement = connection.prepareStatement(insertSql)) { preparedStatement.setString(1, name); preparedStatement.setInt(2, age); preparedStatement.setString(3, email); int rowsAffected = preparedStatement.executeUpdate(); if (rowsAffected > 0) { System.out.println("User inserted successfully!"); } else { System.out.println("Insertion failed!"); } } catch (SQLException e) { e.printStackTrace(); } } }
在上述代码中,首先设置了插入语句中的各个参数值,然后执行更新操作(对于插入、更新、删除等操作,在 JDBC 中使用 executeUpdate 方法),如果返回值大于 0,表示插入成功;否则,插入失败。
3、执行更新和删除操作
编写 SQL 更新和删除语句:更新操作的 SQL 语句一般形式为“UPDATE table_name SET column1 = value1, column2 = value2 WHERE condition”,用于将满足条件的记录的指定列更新为新的值,要将“users”表中 id 为 1 的用户的年龄更新为 30 岁,SQL 语句可以是“UPDATE users SET age = 30 WHERE id = 1”,删除操作的 SQL 语句一般形式为“DELETE FROM table_name WHERE condition”,用于删除满足条件的记录,要删除“users”表中 id 为 2 的用户,SQL 语句可以是“DELETE FROM users WHERE id = 2”。
通过连接对象执行更新和删除:与插入操作类似,使用 PreparedStatement 对象来执行更新和删除操作,设置好相应的参数值后调用 executeUpdate 方法即可。
public class UpdateDeleteExample { public void updateUserAge(int userId, int newAge) { String updateSql = "UPDATE users SET age = ? WHERE id = ?"; try (Connection connection = DatabaseConnection.getConnection(); PreparedStatement preparedStatement = connection.prepareStatement(updateSql)) { preparedStatement.setInt(1, newAge); preparedStatement.setInt(2, userId); int rowsAffected = preparedStatement.executeUpdate(); if (rowsAffected > 0) { System.out.println("User age updated successfully!"); } else { System.out.println("Update failed!"); } } catch (SQLException e) { e.printStackTrace(); } } public void deleteUser(int userId) { String deleteSql = "DELETE FROM users WHERE id = ?"; try (Connection connection = DatabaseConnection.getConnection(); PreparedStatement preparedStatement = connection.prepareStatement(deleteSql)) { preparedStatement.setInt(1, userId); int rowsAffected = preparedStatement.executeUpdate(); if (rowsAffected > 0) { System.out.println("User deleted successfully!"); } else { System.out.println("Deletion failed!"); } } catch (SQLException e) { e.printStackTrace(); } } }
在这两个方法中,分别进行了更新用户年龄和删除用户的操作,同样是先设置参数值,然后执行更新或删除操作,并根据返回值判断操作是否成功。
1、事务的概念与作用:事务是一组原子性的 SQL 操作单元,要么全部执行成功,要么全部回滚(撤销),在涉及多个数据库操作(如同时插入多条相关记录或更新多个表中的数据)时,使用事务可以保证数据的一致性和完整性,在一个银行转账系统中,从一个账户扣除金额和向另一个账户增加金额这两个操作应该作为一个事务来执行,要么两个操作都成功,要么都不执行,以避免出现数据不一致的情况(如扣款成功但加款失败)。
2、控制事务的方法:在 JDBC 中,可以通过设置连接对象的自动提交模式来控制事务,默认情况下,连接是自动提交模式,即每个 SQL 语句执行后会自动提交事务,如果想手动控制事务,可以将自动提交模式设置为 false,然后在所有需要的操作执行完成后,根据情况选择提交(commit)事务或回滚(rollback)事务,以下是一个简单的示例:
import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; public class TransactionExample { public void performTransaction() { Connection connection = null; PreparedStatement preparedStatement1 = null; PreparedStatement preparedStatement2 = null; try { connection = DatabaseConnection.getConnection(); connection.setAutoCommit(false); // 开启事务,设置手动提交模式 // 第一个操作:插入一条记录到表1 String insertSql1 = "INSERT INTO table1 (column1) VALUES (?)"; preparedStatement1 = connection.prepareStatement(insertSql1); preparedStatement1.setString(1, "value1"); preparedStatement1.executeUpdate(); // 第二个操作:插入一条记录到表2 String insertSql2 = "INSERT INTO table2 (column2) VALUES (?)"; preparedStatement2 = connection.prepareStatement(insertSql2); preparedStatement2.setString(1, "value2"); preparedStatement2.executeUpdate(); // 如果两个操作都成功,提交事务 connection.commit(); System.out.println("Transaction committed successfully!"); } catch (SQLException e) { e.printStackTrace(); if (connection != null) { try { // 如果发生异常,回滚事务 connection.rollback(); System.out.println("Transaction rolled back!"); } catch (SQLException ex) { ex.printStackTrace(); } } } finally { // 关闭资源(注意关闭顺序) try { if (preparedStatement1 != null) preparedStatement1.close(); if (preparedStatement2 != null) preparedStatement2.close(); if (connection != null) connection.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
在上述代码中,先关闭了自动提交模式,然后依次执行两个插入操作,如果两个操作都成功,则提交事务;如果任何一个操作出现异常,则回滚事务,以确保数据的一致性,最后在 finally 块中关闭相关的资源。
问题 1:为什么使用 PreparedStatement 而不是直接使用 Statement?
答:PreparedStatement 相比 Statement 具有以下优点:
性能优势:PreparedStatement 会对 SQL 语句进行预编译,当多次执行相同结构的 SQL 语句时,只需要发送参数值即可,不需要每次都编译 SQL 语句,从而提高了执行效率,而 Statement 每次执行 SQL 语句都需要进行编译,效率相对较低,在一个循环中频繁执行插入操作,使用 PreparedStatement 可以显著提高插入速度。
安全性更高:PreparedStatement 可以有效防止 SQL 注入攻击,在拼接 SQL 语句时,如果直接使用字符串拼接的方式来设置参数值,可能会被反面用户利用来注入反面的 SQL 代码,而 PreparedStatement 使用参数占位符,参数的值是通过独立的设置方法传入的,不会与 SQL 语句本身的结构混淆,从而避免了 SQL 注入的风险,在登录验证时,如果使用 Statement 拼接 SQL 语句来查询用户密码,用户输入的特殊字符可能会被误解释为 SQL 命令的一部分;而使用 PreparedStatement 则可以安全地处理用户输入。
问题 2:在执行数据库操作时,如何确保资源的正确关闭?
答:在 Java 中,为了确保数据库连接、PreparedStatement 等资源在使用完毕后正确关闭,通常会使用 try-with-resources 语句或者在 finally 块中手动关闭资源,try-with-resources 语句是在 Java 7 及以后版本中引入的一种更简洁的资源管理方式,它会自动关闭实现了 AutoCloseable 接口的资源(如 Connection、PreparedStatement、ResultSet 等)。
try (Connection connection = DatabaseConnection.getConnection(); PreparedStatement preparedStatement = connection.prepareStatement(query)) { // 执行数据库操作... } catch (SQLException e) { e.printStackTrace(); }
在这个例子中,无论是正常执行完操作还是发生异常,Connection 和 PreparedStatement 都会在 try-with-resources 语句结束时自动关闭,如果在较早版本的 Java 中或者出于某些特殊原因无法使用 try-with-resources 语句,那么可以在 finally 块中手动关闭资源:
Connection connection = null; PreparedStatement preparedStatement = null; try { connection = DatabaseConnection.getConnection(); preparedStatement = connection.prepareStatement(query); // 执行数据库操作... } catch (SQLException e) { e.printStackTrace(); } finally { if (preparedStatement != null) { try { preparedStatement.close(); } catch (SQLException e) { e.printStackTrace(); } } if (connection != null) { try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } } }
这样可以确保即使在操作过程中发生异常,资源也能够得到正确的释放,避免资源泄漏等问题。