🙋♂️ 作者:@whisperrr. 🙋♂️
👀 专栏:JDBC 👀
💥 标题:探索JDBC:Java数据库毗连的艺术与魅力💥
❣️ 寄语:比力是偷走幸福的小偷❣️
一.JDBC概述
1. 根本先容
JDBC(Java Database Connectivity)是Java语言中用于数据库毗连和操纵的一组尺度API,它界说了一套尺度的数据库访问接口,为Java步调提供了与数据库毗连和实行SQL语句的本事。以下是JDBC的根本先容:
1.1 JDBC的目的
- 提供一种尺度的数据库访问接口,使Java应用步调可以方便地访问各种数据库。
- 实现数据库访问的跨平台性。
- 简化数据库编程。
1.2 JDBC架构
JDBC架构包罗以下四个条理:
- JDBC API:这是提供给Java步调员的接口,他们可以利用这些接口来毗连数据库,实行SQL语句,处置惩罚结果集等。
- JDBC Driver Manager:负责管理差别数据库的JDBC驱动步调,根据哀求为应用步调加载符合的驱动步调。
- JDBC Driver API:由数据库厂商提供,实现了JDBC API中界说的接口,用于与特定的数据库举行交互。
- 数据库:存储数据的体系,JDBC API通过驱动步调与之通讯。
1.3 JDBC驱动范例
根据实现方式和功能,JDBC驱动步调可以分为以下四类:
- Type 1:JDBC-ODBC Bridge Driver
- 将JDBC调用转换为ODBC调用。
- 须要当地ODBC驱动步调。
- 实用于实行和小型应用。
- Type 2:Native API Driver
- 将JDBC调用转换为特定命据库的当地API调用。
- 依靠于特定命据库的当地库。
- 性能较好,但跨平台性较差。
- Type 3:Network Protocol Driver
- 将JDBC调用转换为网络协议,通常由中央服务器转换为特定命据库的下令。
- 跨平台性好,但大概存在性能瓶颈。
- Type 4:Thin Driver(纯Java驱动)
- 直接将JDBC调用转换为数据库的网络协议。
- 不须要额外的中央件或客户端软件。
- 性能好,跨平台性好。
1.4 JDBC根本利用步调
- 注册驱动:通过Class.forName()加载驱动类。
- 创建毗连:通过DriverManager.getConnection()获取Connection对象。
- 创建语句:通过Connection对象创建Statement或PreparedStatement。
- 实行查询:通过Statement或PreparedStatement实行SQL语句。
- 处置惩罚结果:处置惩罚ResultSet对象中的查询结果。
- 关闭毗连:操纵完成后,关闭ResultSet、Statement和Connection对象。
2. 示例代码
- import java.sql.*;
- public class JDBCDemo {
- public static void main(String[] args) {
- Connection conn = null;
- Statement stmt = null;
- try {
- // 1. 加载驱动
- Class.forName("com.mysql.cj.jdbc.Driver");
- // 2. 建立连接
- conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testdb", "username", "password");
- // 3. 创建语句
- stmt = conn.createStatement();
- // 4. 执行查询
- ResultSet rs = stmt.executeQuery("SELECT * FROM example_table");
- // 5. 处理结果
- while (rs.next()) {
- System.out.println(rs.getString("column1") + ", " + rs.getString("column2"));
- }
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- // 6. 关闭连接
- try {
- if (stmt != null) stmt.close();
- if (conn != null) conn.close();
- } catch (SQLException se) {
- se.printStackTrace();
- }
- }
- }
- }
复制代码 通过以上步调,Java步调就可以利用JDBC与数据库举行交互了。在实际开发中,通常会利用更高级的数据库访问框架,如Hibernate、MyBatis等,来简化数据库操纵和进步开发服从。
3. JDBC 带来的长处
JDBC带来的长处(表示图)
阐明:JDBC是Java提供一套用于数据库操纵的接口API,Java步调员只须要面向这套接口编程即可。差别的数据库厂商,须要针对这套接口,提供差别实现。
二.获取数据库毗连 5 种方式
1.方式1
获取 Driver 实现类对象,属于静态加载,机动性不敷高,依靠性强。
- public void connect01() throws SQLException {
- Driver driver = new Driver();
- String url = "jdbc:mysql://localhost:3306/hsp_db02";
- Properties properties = new Properties();
- properties.setProperty("user","root");
- properties.setProperty("password","lrx");
- Connection connect = driver.connect(url, properties);
- System.out.println(connect);
- }
复制代码 2.方式2
利用反射机制 Class<?> aClass = Class.forName(“com.mysql.jdbc.Driver”),动态加载,可以更加机动
- public void connect02() throws Exception {
- Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
- Driver driver = (Driver) aClass.newInstance();
- String url = "jdbc:mysql://localhost:3306/hsp_db02";
- Properties properties = new Properties();
- properties.setProperty("user","root");
- properties.setProperty("password","lrx");
- Connection connect = driver.connect(url, properties);
- System.out.println(connect);
- }
复制代码 3.方式3
利用 DriverManager 更换 Driver,DriverManager 用于管理一组 JDBC 驱动步调的根本服务。
- public void connect03() throws Exception {
- Class<?> aClass = Class.forName("com.mysql.jdbc.Driver");
- Driver driver = (Driver) aClass.newInstance();
- String url = "jdbc:mysql://localhost:3306/hsp_db02";
- String user = "root";
- String password = "lrx";
- DriverManager.registerDriver(driver);
- Connection connection
- = DriverManager.getConnection(url, user, password);
- System.out.println(connection);
- }
复制代码 4.方式4
利用反射加载 Driver 类,利用 DriverManager 更换 Driver ,注册加载的工作在加载 Driver 类时,底层已经完成,相比力方式3,更加轻便
- public void connect04() throws Exception {
- Class.forName("com.mysql.jdbc.Driver");
- String url = "jdbc:mysql://localhost:3306/hsp_db02";
- String user = "root";
- String password = "lrx";
- Connection connection = DriverManager.getConnection(url, user, password);
- System.out.println(connection);
- }
复制代码 5.方式5
利用设置文件,毗连数据库
- public void connect05() throws Exception {
- Properties properties = new Properties();
- properties.load(new FileInputStream("src\\mysql.properties"));
- String url = properties.getProperty("url");
- String user = properties.getProperty("user");
- String password = properties.getProperty("password");
- String driver = properties.getProperty("driver");
- Class.forName("com.mysql.jdbc.Driver");
- Connection connection = DriverManager.getConnection(url, user, password);
- System.out.println(connection);
- String sql = "insert into news1 values (1,'111'),(2,'222')";
- Statement statement = connection.createStatement();
- int rows = statement.executeUpdate(sql);
- System.out.println(rows > 0 ? "成功" : "失败");
- statement.close();
- connection.close();
- }
复制代码 三.ResultSet
1.ResultSet 表现数据库结果集的数据表,通常通过实行查询数据库的语句天生。
2.ResultSet 对象保持一个光标指向其当前的数据行。 最初,光标位于第一行之前。
3. next 方法将光标移动到下一行,而且由于在 ResultSet 对象中没有更多行时返回 false, 因此可以在while循环中利用循环来遍历结果集。
以下是对 ResultSet 的具体先容:
3.1 ResultSet 的作用
- 存储 SQL 查询返回的数据。
- 允许应用步调通过移动光标来遍历结果会合的行。
- 提供了访问当前行中差别列的数据的方法。
3.2 ResultSet 的范例
ResultSet 有几种范例,这些范例界说告终果集的滚动本事和更新本事:
- TYPE_FORWARD_ONLY:光标只能向前移动。
- TYPE_SCROLL_INSENSITIVE:光标可以向前和向后移动,但不反映对数据库的更改。
- TYPE_SCROLL_SENSITIVE:光标可以向前和向后移动,并反映对数据库的更改。
3.3 ResultSet 的并发性
ResultSet 的并发性界说了其他线程或历程对结果集所做的更改是否可见:
- CONCUR_READ_ONLY:结果集是只读的,不能更新。
- CONCUR_UPDATABLE:结果集是可更新的。
3.4 常用的 ResultSet 方法
以下是一些常用的 ResultSet 方法:
- next():将光标从当前位置向前移动一行。
- previous():将光标从当前位置向后移动一行(仅在可滚动的结果会合可用)。
- absolute(int row):将光标移动到指定的行号。
- relative(int rows):相对于当前位置移动光标。
- getRow():返回当前行的行号。
- getString(String columnLabel):以 String 情势获取当前行指定列的值。
- getInt(int columnIndex):以 int 情势获取当前行指定列的值。
- getObject(int columnIndex):以 Object 情势获取当前行指定列的值。
- updateString(int columnIndex, String x):更新当前行指定列的值为 String。
- updateRow():更新结果会合的当前行。
- insertRow():在结果会合插入一行。
- deleteRow():从结果会合删除当前行。
- close():关闭 ResultSet 对象并开释与之关联的资源。
3.5 示例代码
以下是怎样利用 ResultSet 的一个简朴示例:
- import java.sql.*;
- public class ResultSetExample {
- public static void main(String[] args) {
- Connection conn = null;
- Statement stmt = null;
- ResultSet rs = null;
- try {
- // 加载驱动、建立连接、创建语句等步骤省略
- // 执行查询
- stmt = conn.createStatement();
- rs = stmt.executeQuery("SELECT id, name, age FROM users");
- // 遍历结果集
- while (rs.next()) {
- int id = rs.getInt("id");
- String name = rs.getString("name");
- int age = rs.getInt("age");
-
- System.out.println("ID: " + id + ", Name: " + name + ", Age: " + age);
- }
- } catch (SQLException e) {
- e.printStackTrace();
- } finally {
- // 关闭资源
- try {
- if (rs != null) rs.close();
- if (stmt != null) stmt.close();
- if (conn != null) conn.close();
- } catch (SQLException se) {
- se.printStackTrace();
- }
- }
- }
- }
复制代码 底层存储:
在处置惩罚 ResultSet 时,须要留意资源的精确关闭,以制止埋伏的资源走漏标题。在实际应用中,通常会利用 try-with-resources 语句来主动关闭实现了 AutoCloseable 接口的资源。
四.Sql注入
SQL注入(SQL Injection)是一种常见的网络攻击技能,它重要针对基于SQL语言的数据库体系。攻击者通过在Web应用的输入字段中插入恶意的SQL代码,从而诱骗服务器实行非预期的SQL下令。以下是关于SQL注入的具体先容:
4.1 SQL注入的原理
当应用步调直接将用户输入的数据拼接到SQL查询语句中,而没有举行得当的验证或转义时,就大概出现SQL注入毛病。比方:
- String username = request.getParameter("username");
- String password = request.getParameter("password");
- String sql = "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'";
复制代码 假如用户输入的数据包罗SQL关键字或特别字符,如单引号('),它们大概会改变原始SQL语句的布局,导致安全毛病。比方,假如用户输入的username是' OR '1'='1,那么拼接后的SQL语句将变成:
- SELECT * FROM users WHERE username = '' OR '1'='1' --' AND password = '...'
复制代码 这里的--是SQL中的解释符号,它将忽略背面的内容,从而使攻击者绕过身份验证。
4.2 SQL注入的范例
- 基于错误的SQL注入:攻击者故意输入错误的SQL语法,通不对误信息相识数据库布局。
- 基于布尔的SQL注入:攻击者通过真或假的布尔表达式来推断数据库信息。
- 基于时间的SQL注入:攻击者利用SQL语句的实行时间来推断数据库信息。
- 盲注:攻击者无法直接从服务器获取信息,只能通过服务器相应的差别来推断信息。
4.3 防御SQL注入的方法
- 利用预编译的SQL语句(PreparedStatement):这是防备SQL注入最有用的方法。预编译的语句会先对SQL模板举行编译,然后再将用户输入作为参数绑定到模板中,从而制止了直接拼接SQL语句。
- String sql = "SELECT * FROM users WHERE username = ? AND password = ?";
- PreparedStatement pstmt = connection.prepareStatement(sql);
- pstmt.setString(1, username);
- pstmt.setString(2, password);
- ResultSet rs = pstmt.executeQuery();
复制代码 - 输入验证:对全部用户输入举行严格的验证,只继承符合预期格式的数据。
- 利用存储过程:在数据库层面利用存储过程可以淘汰SQL注入的风险,由于存储过程可以限定输入范例和实行权限。
- 最小权限原则:数据库连策应该利用最小权限的账户,以淘汰攻击者乐成注入SQL后的埋伏陵犯。
- 错误处置惩罚:不要在应用步调中表现数据库错误信息给用户,以免泄漏数据库布局信息。
- 利用ORM框架:对象关系映射(ORM)框架通常会处置惩罚SQL语句的构造,淘汰SQL注入的风险。
通过采取上述步调,可以有用地淘汰应用步调遭受SQL注入攻击的风险。
五.Statement
在Java的JDBC API中,Statement是一个接口,它用于实行不带参数的SQL语句并返回实行结果。以下是对Statement的具体先容:
5.1 Statement 接口的重要方法
- execute(String sql): 实行给定的SQL语句,该语句大概返回多个结果集,也大概不返回结果集,比方DDL语句。
- executeQuery(String sql): 实行查询数据库的SQL语句,并返回单个ResultSet对象。
- executeUpdate(String sql): 实行更新数据库的SQL语句(INSERT、UPDATE、DELETE等),并返回一个整数,表现受影响的行数。
- addBatch(String sql): 将给定的SQL下令添加到当前的下令批次中。
- executeBatch(): 实行当前批次中的全部下令,并返回一个整数数组,表现每条下令受影响的行数。
- close(): 立刻开释此Statement对象的数据库和JDBC资源,而不是等候它们主动开释。
5.2 创建 Statement 对象
要利用Statement,起首须要通过Connection对象创建它:
- Connection conn = DriverManager.getConnection(url, username, password);
- Statement stmt = conn.createStatement();
复制代码 5.3 利用 Statement 实行SQL语句
以下是怎样利用Statement实行差别范例的SQL语句的示例:
- // 执行查询
- String query = "SELECT * FROM employees";
- ResultSet rs = stmt.executeQuery(query);
- while (rs.next()) {
- // 处理结果集
- }
- // 执行更新
- String update = "UPDATE employees SET salary = salary * 1.1 WHERE department = 'Engineering'";
- int rowsUpdated = stmt.executeUpdate(update);
- // 执行多个更新
- stmt.addBatch("INSERT INTO employees (name, department) VALUES ('Alice', 'HR')");
- stmt.addBatch("INSERT INTO employees (name, department) VALUES ('Bob', 'Finance')");
- int[] rowsAffected = stmt.executeBatch();
复制代码 5.4 安全性和性能标题
固然Statement接口利用方便,但它存在一些安全和性能标题:
- SQL注入风险:由于Statement直接将用户输入拼接到SQL语句中,因此容易受到SQL注入攻击。
- 性能标题:每次实行SQL语句时,Statement都须要重新编译SQL语句,这大概会低落性能。
为了办理这些标题,保举利用PreparedStatement,它是Statement的子接口,提供了更好的安全性和性能:
- 防止SQL注入:PreparedStatement利用参数化查询,可以有用地防止SQL注入。
- 性能提升:PreparedStatement可以预编译SQL语句,多次实行时不须要重新编译。
5.5 资源管理
在利用完Statement对象后,应该及时关闭它以开释数据库资源:
在实际应用中,通常利用try-with-resources语句来主动关闭实现了AutoCloseable接口的资源:
- try (Statement stmt = conn.createStatement()) {
- // 使用stmt执行SQL语句
- }
- // stmt会在try块结束时自动关闭
复制代码 六. PreparedStatement
PreparedStatement 是 Java JDBC API 中的一个接口,它是 Statement 的子接口,提供了比 Statement 更强大的功能,尤其是在安全性、性能和机动性方面。以下是 PreparedStatement 的具体先容:
6.1 PreparedStatement 的上风
- 参数化查询:PreparedStatement 允许你利用占位符(?)来代替直接在 SQL 语句中插入值,如允许以有用地防止 SQL 注入攻击。
- 性能提升:利用 PreparedStatement 可以预编译 SQL 语句,多次实行雷同的 SQL 语句时,不须要再次编译,从而进步性能。
- 范例安全:PreparedStatement 提供了设置参数范例的方法,确保了数据范例的同等性。
6.2 创建 PreparedStatement 对象
要创建 PreparedStatement 对象,须要通过 Connection 对象的 prepareStatement 方法:
- Connection conn = DriverManager.getConnection(url, username, password);
- String sql = "SELECT * FROM employees WHERE department = ?";
- PreparedStatement pstmt = conn.prepareStatement(sql);
复制代码 6.3 设置参数
在实行 SQL 语句之前,须要为 PreparedStatement 中的占位符设置值:
- pstmt.setString(1, "Engineering"); // 设置第一个占位符的值为 "Engineering"
复制代码 参数的索引从 1 开始。
6.4 实行 PreparedStatement
PreparedStatement 提供了与 Statement 类似的方法来实行 SQL 语句:
- execute(): 实行任何范例的 SQL 语句。
- executeQuery(): 实行查询语句并返回 ResultSet。
- executeUpdate(): 实行 INSERT、UPDATE 或 DELETE 语句,并返回受影响的行数。
- ResultSet rs = pstmt.executeQuery(); // 执行查询
- int rowsUpdated = pstmt.executeUpdate(); // 执行更新
复制代码 6.5 利用示例
以下是一个利用 PreparedStatement 的完备示例:
- try (Connection conn = DriverManager.getConnection(url, username, password);
- PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM employees WHERE department = ?")) {
-
- pstmt.setString(1, "Engineering");
- ResultSet rs = pstmt.executeQuery();
-
- while (rs.next()) {
- // 处理结果集
- }
- } catch (SQLException e) {
- e.printStackTrace();
- }
复制代码 6.6 资源管理
与 Statement 一样,利用 PreparedStatement 后,应该关闭它以开释数据库资源。利用 try-with-resources 语句可以主动关闭资源:
- try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
- // 使用 pstmt 执行 SQL 语句
- }
- // pstmt 在 try 块结束时自动关闭
复制代码 6.7 总结
PreparedStatement 是处置惩罚数据库操纵时保举利用的接口,由于它提供了更好的安全性、性能和易用性。在处置惩罚用户输入和实行多次雷同的 SQL 语句时,它尤其有用。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |