MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Java使用JDBC连接MySQL数据库

2021-01-194.9k 阅读

Java使用JDBC连接MySQL数据库

JDBC基础概念

JDBC(Java Database Connectivity)是Java提供的一套用于执行SQL语句的Java API。它为Java程序员提供了一种标准的、通用的方式来与各种不同类型的数据库进行交互,而不需要针对每种数据库编写特定的代码。通过JDBC,Java应用程序可以连接到数据库,执行SQL语句,获取结果集并对数据库进行各种操作,如插入、更新、删除数据等。

JDBC的核心是一组接口,这些接口定义了Java程序与数据库交互的方法。主要的接口包括DriverConnectionStatementResultSet等。不同的数据库厂商会根据这些接口提供具体的实现类,这些实现类构成了数据库的JDBC驱动程序。例如,MySQL数据库有自己的JDBC驱动,Oracle数据库也有相应的JDBC驱动。

MySQL数据库简介

MySQL是一个开源的关系型数据库管理系统,因其高性能、可靠性和易用性而被广泛使用。它支持多种操作系统,并且与Java的结合非常紧密,是Web应用开发中常用的数据库之一。

在使用JDBC连接MySQL数据库之前,需要确保已经安装并正确配置了MySQL数据库。可以从MySQL官方网站下载并安装MySQL Server,安装过程中会提示设置root用户的密码等信息。安装完成后,可以通过MySQL命令行客户端或者一些图形化工具(如Navicat、MySQL Workbench等)来管理数据库。

准备工作

  1. 下载MySQL JDBC驱动:要在Java程序中连接MySQL数据库,需要下载MySQL官方提供的JDBC驱动。可以从MySQL官方网站的下载页面获取最新版本的JDBC驱动。下载得到的是一个.jar文件,例如mysql - connector - java - 8.0.26.jar
  2. 添加JDBC驱动到项目:在Java项目中使用JDBC驱动,需要将下载的.jar文件添加到项目的类路径中。如果使用Maven进行项目管理,可以在pom.xml文件中添加以下依赖:
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql - connector - java</artifactId>
    <version>8.0.26</version>
</dependency>

如果不使用Maven,手动将.jar文件复制到项目的lib目录下,并在IDE(如Eclipse、IntelliJ IDEA等)中配置该.jar文件为项目的库。

建立数据库连接

在Java中使用JDBC连接MySQL数据库,主要步骤如下:

  1. 加载JDBC驱动:在Java 6及以上版本,不再需要显式地使用Class.forName()方法来加载JDBC驱动,因为驱动程序在被调用时会自动加载。但在旧版本中,加载MySQL JDBC驱动的代码如下:
try {
    Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}
  1. 建立连接:使用DriverManager类的getConnection()方法来建立与MySQL数据库的连接。getConnection()方法需要三个参数:数据库的URL、用户名和密码。数据库URL的格式为jdbc:mysql://主机名:端口号/数据库名。例如,连接本地的MySQL数据库,数据库名为test,用户名root,密码123456,代码如下:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class DatabaseConnection {
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/test";
        String username = "root";
        String password = "123456";
        Connection connection = null;
        try {
            connection = DriverManager.getConnection(url, username, password);
            if (connection != null) {
                System.out.println("成功连接到数据库!");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

在上述代码中,首先定义了数据库的URL、用户名和密码。然后通过DriverManager.getConnection()方法尝试建立连接。如果连接成功,会输出“成功连接到数据库!”。最后,在finally块中关闭连接,以确保资源被正确释放。

执行SQL语句

连接到数据库后,就可以执行SQL语句了。在JDBC中,主要通过StatementPreparedStatementCallableStatement接口来执行SQL语句。

  1. Statement接口Statement接口用于执行静态SQL语句。例如,查询数据库中的所有用户信息,代码如下:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class ExecuteStatement {
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/test";
        String username = "root";
        String password = "123456";
        Connection connection = null;
        Statement statement = null;
        ResultSet resultSet = null;
        try {
            connection = DriverManager.getConnection(url, username, password);
            statement = connection.createStatement();
            String sql = "SELECT * FROM users";
            resultSet = statement.executeQuery(sql);
            while (resultSet.next()) {
                int id = resultSet.getInt("id");
                String name = resultSet.getString("name");
                String email = resultSet.getString("email");
                System.out.println("ID: " + id + ", Name: " + name + ", Email: " + email);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            if (resultSet != null) {
                try {
                    resultSet.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (statement != null) {
                try {
                    statement.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

在上述代码中,首先创建了Statement对象,然后定义了SQL查询语句SELECT * FROM users。通过statement.executeQuery()方法执行查询,并将结果存储在ResultSet中。最后,通过ResultSetnext()方法遍历结果集,并获取每一行的数据。

  1. PreparedStatement接口PreparedStatement接口用于执行预编译的SQL语句,它比Statement更安全,性能也更好。例如,向users表中插入一条新记录,代码如下:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class ExecutePreparedStatement {
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/test";
        String username = "root";
        String password = "123456";
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        try {
            connection = DriverManager.getConnection(url, username, password);
            String sql = "INSERT INTO users (name, email) VALUES (?,?)";
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setString(1, "John Doe");
            preparedStatement.setString(2, "johndoe@example.com");
            int rowsInserted = preparedStatement.executeUpdate();
            if (rowsInserted > 0) {
                System.out.println("一条记录插入成功!");
            }
        } 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();
                }
            }
        }
    }
}

在上述代码中,定义了一条带有占位符?的SQL插入语句。通过PreparedStatementsetString()方法为占位符赋值,然后通过executeUpdate()方法执行插入操作。executeUpdate()方法返回受影响的行数,如果返回值大于0,则表示插入成功。

  1. CallableStatement接口CallableStatement接口用于执行数据库存储过程。例如,假设数据库中有一个名为get_user_by_id的存储过程,用于根据用户ID获取用户信息,代码如下:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.CallableStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class ExecuteCallableStatement {
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/test";
        String username = "root";
        String password = "123456";
        Connection connection = null;
        CallableStatement callableStatement = null;
        ResultSet resultSet = null;
        try {
            connection = DriverManager.getConnection(url, username, password);
            String sql = "{call get_user_by_id(?)}";
            callableStatement = connection.prepareCall(sql);
            callableStatement.setInt(1, 1);
            resultSet = callableStatement.executeQuery();
            while (resultSet.next()) {
                int id = resultSet.getInt("id");
                String name = resultSet.getString("name");
                String email = resultSet.getString("email");
                System.out.println("ID: " + id + ", Name: " + name + ", Email: " + email);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            if (resultSet != null) {
                try {
                    resultSet.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (callableStatement != null) {
                try {
                    callableStatement.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

在上述代码中,定义了调用存储过程的SQL语句{call get_user_by_id(?)},其中?是存储过程的参数。通过CallableStatementsetInt()方法为参数赋值,然后执行存储过程并处理结果集。

处理结果集

当执行查询语句(如SELECT语句)时,会返回一个ResultSet对象,该对象包含了查询结果。ResultSet对象提供了一系列方法来获取结果集中的数据。

  1. 遍历结果集:通过ResultSetnext()方法可以将游标移动到下一行,当游标指向有效行时,next()方法返回true,否则返回false。例如:
ResultSet resultSet = statement.executeQuery(sql);
while (resultSet.next()) {
    // 获取数据
}
  1. 获取列数据ResultSet提供了多种方法来获取不同类型的列数据,如getInt()getString()getDouble()等。获取列数据时,可以通过列名或列索引来指定列。例如:
int id = resultSet.getInt("id");
String name = resultSet.getString(2);

上述代码中,getInt("id")通过列名获取id列的值,getString(2)通过列索引(从1开始)获取第二列的值。

  1. 结果集的类型和并发模式ResultSet有不同的类型和并发模式。默认情况下,ResultSetTYPE_FORWARD_ONLY类型,即只能向前遍历结果集,并且是CONCUR_READ_ONLY模式,即只能读取数据,不能更新数据。如果需要更灵活的操作,可以在创建StatementPreparedStatement时指定ResultSet的类型和并发模式。例如:
Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);

上述代码创建了一个可滚动且可更新的ResultSetTYPE_SCROLL_INSENSITIVE表示结果集可以前后滚动,并且对数据库的更改不敏感;CONCUR_UPDATABLE表示可以通过ResultSet更新数据库中的数据。

事务处理

在数据库操作中,事务是一组逻辑上相关的操作,这些操作要么全部成功执行,要么全部失败回滚。JDBC提供了对事务处理的支持。

  1. 开启事务:默认情况下,JDBC的连接是自动提交模式,即每执行一条SQL语句就会立即提交到数据库。要开启事务,需要将连接的自动提交模式设置为false。例如:
connection.setAutoCommit(false);
  1. 提交事务:当所有需要在事务中执行的SQL语句都成功执行后,调用connection.commit()方法来提交事务,将所有操作持久化到数据库。例如:
try {
    // 执行SQL语句
    connection.commit();
} catch (SQLException e) {
    e.printStackTrace();
}
  1. 回滚事务:如果在事务执行过程中出现异常,调用connection.rollback()方法来回滚事务,撤销之前执行的所有操作。例如:
try {
    // 执行SQL语句
    connection.commit();
} catch (SQLException e) {
    try {
        connection.rollback();
    } catch (SQLException ex) {
        ex.printStackTrace();
    }
    e.printStackTrace();
}

以下是一个完整的事务处理示例,向users表中插入两条记录,并且在出现异常时回滚事务:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class TransactionExample {
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/test";
        String username = "root";
        String password = "123456";
        Connection connection = null;
        PreparedStatement preparedStatement1 = null;
        PreparedStatement preparedStatement2 = null;
        try {
            connection = DriverManager.getConnection(url, username, password);
            connection.setAutoCommit(false);
            String sql1 = "INSERT INTO users (name, email) VALUES (?,?)";
            preparedStatement1 = connection.prepareStatement(sql1);
            preparedStatement1.setString(1, "Alice");
            preparedStatement1.setString(2, "alice@example.com");
            preparedStatement1.executeUpdate();
            String sql2 = "INSERT INTO users (name, email) VALUES (?,?)";
            preparedStatement2 = connection.prepareStatement(sql2);
            preparedStatement2.setString(1, "Bob");
            preparedStatement2.setString(2, "bob@example.com");
            preparedStatement2.executeUpdate();
            connection.commit();
            System.out.println("两条记录插入成功!");
        } catch (SQLException e) {
            try {
                if (connection != null) {
                    connection.rollback();
                    System.out.println("事务回滚!");
                }
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
            e.printStackTrace();
        } finally {
            if (preparedStatement2 != null) {
                try {
                    preparedStatement2.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (preparedStatement1 != null) {
                try {
                    preparedStatement1.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

在上述代码中,首先将连接的自动提交模式设置为false,然后执行两条插入语句。如果两条语句都执行成功,提交事务;如果出现异常,回滚事务。

连接池技术

在实际应用中,频繁地创建和销毁数据库连接会消耗大量的系统资源,影响应用程序的性能。连接池技术可以解决这个问题。连接池是一个存放数据库连接的缓存池,应用程序需要连接时,从连接池中获取一个连接,使用完毕后再将连接归还到连接池中。

常见的Java连接池有C3P0HikariCPDruid等。以HikariCP为例,使用步骤如下:

  1. 添加依赖:如果使用Maven,在pom.xml文件中添加HikariCP的依赖:
<dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
    <version>4.0.3</version>
</dependency>
  1. 配置连接池:在Java代码中配置HikariCP连接池,示例代码如下:
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

import java.sql.Connection;
import java.sql.SQLException;

public class HikariCPExample {
    private static HikariDataSource dataSource;

    static {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl("jdbc:mysql://localhost:3306/test");
        config.setUsername("root");
        config.setPassword("123456");
        config.setMaximumPoolSize(10);
        dataSource = new HikariDataSource(config);
    }

    public static Connection getConnection() throws SQLException {
        return dataSource.getConnection();
    }
}

在上述代码中,通过HikariConfig类配置了连接池的各项参数,如数据库URL、用户名、密码、最大连接数等。然后创建了HikariDataSource对象,通过该对象获取数据库连接。

  1. 使用连接池获取连接:在需要连接数据库的地方,调用HikariCPExample.getConnection()方法获取连接,示例代码如下:
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class UseHikariCP {
    public static void main(String[] args) {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        try {
            connection = HikariCPExample.getConnection();
            String sql = "INSERT INTO users (name, email) VALUES (?,?)";
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setString(1, "Charlie");
            preparedStatement.setString(2, "charlie@example.com");
            int rowsInserted = preparedStatement.executeUpdate();
            if (rowsInserted > 0) {
                System.out.println("一条记录插入成功!");
            }
        } 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();
                }
            }
        }
    }
}

通过使用连接池,可以大大提高应用程序的性能和资源利用率。

常见问题及解决方法

  1. 驱动加载问题:如果在加载JDBC驱动时出现ClassNotFoundException异常,可能是JDBC驱动没有正确添加到项目的类路径中。检查.jar文件是否存在,以及是否在IDE中正确配置了类路径。如果使用Maven,检查pom.xml文件中依赖是否正确添加。
  2. 连接失败问题:连接数据库失败可能有多种原因,如数据库服务未启动、用户名或密码错误、数据库URL配置错误等。可以检查数据库服务状态,确保用户名和密码正确,以及数据库URL的格式是否正确。例如,MySQL 8.0及以上版本的JDBC URL可能需要添加时区等参数,如jdbc:mysql://localhost:3306/test?serverTimezone=UTC
  3. SQL注入问题:使用Statement执行SQL语句时,如果直接拼接用户输入的数据,可能会导致SQL注入攻击。例如:
String username = request.getParameter("username");
String password = request.getParameter("password");
String sql = "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'";
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(sql);

如果用户输入的usernameadmin' OR '1'='1,则拼接后的SQL语句为SELECT * FROM users WHERE username = 'admin' OR '1'='1' AND password = '...',这样即使密码错误,也能成功查询到用户信息。为了避免SQL注入,应使用PreparedStatement,如:

String sql = "SELECT * FROM users WHERE username =? AND password =?";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1, username);
preparedStatement.setString(2, password);
ResultSet resultSet = preparedStatement.executeQuery();
  1. 资源未释放问题:在使用JDBC时,如果没有正确关闭ConnectionStatementResultSet等资源,可能会导致资源泄漏,影响应用程序的性能。应在代码中使用try - catch - finally块,确保在使用完资源后及时关闭。例如:
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
try {
    // 执行数据库操作
} catch (SQLException e) {
    e.printStackTrace();
} finally {
    if (resultSet != null) {
        try {
            resultSet.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    if (statement != null) {
        try {
            statement.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    if (connection != null) {
        try {
            connection.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

通过以上内容,相信你对Java使用JDBC连接MySQL数据库有了较为深入的了解。从基础概念到实际操作,包括连接建立、SQL语句执行、事务处理、连接池技术以及常见问题解决等方面,都进行了详细的介绍和示例展示。在实际应用开发中,根据具体需求合理运用这些知识,能够开发出高效、稳定的数据库相关应用程序。