Java使用JDBC连接MySQL数据库
Java使用JDBC连接MySQL数据库
JDBC基础概念
JDBC(Java Database Connectivity)是Java提供的一套用于执行SQL语句的Java API。它为Java程序员提供了一种标准的、通用的方式来与各种不同类型的数据库进行交互,而不需要针对每种数据库编写特定的代码。通过JDBC,Java应用程序可以连接到数据库,执行SQL语句,获取结果集并对数据库进行各种操作,如插入、更新、删除数据等。
JDBC的核心是一组接口,这些接口定义了Java程序与数据库交互的方法。主要的接口包括Driver
、Connection
、Statement
、ResultSet
等。不同的数据库厂商会根据这些接口提供具体的实现类,这些实现类构成了数据库的JDBC驱动程序。例如,MySQL数据库有自己的JDBC驱动,Oracle数据库也有相应的JDBC驱动。
MySQL数据库简介
MySQL是一个开源的关系型数据库管理系统,因其高性能、可靠性和易用性而被广泛使用。它支持多种操作系统,并且与Java的结合非常紧密,是Web应用开发中常用的数据库之一。
在使用JDBC连接MySQL数据库之前,需要确保已经安装并正确配置了MySQL数据库。可以从MySQL官方网站下载并安装MySQL Server,安装过程中会提示设置root用户的密码等信息。安装完成后,可以通过MySQL命令行客户端或者一些图形化工具(如Navicat、MySQL Workbench等)来管理数据库。
准备工作
- 下载MySQL JDBC驱动:要在Java程序中连接MySQL数据库,需要下载MySQL官方提供的JDBC驱动。可以从MySQL官方网站的下载页面获取最新版本的JDBC驱动。下载得到的是一个
.jar
文件,例如mysql - connector - java - 8.0.26.jar
。 - 添加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数据库,主要步骤如下:
- 加载JDBC驱动:在Java 6及以上版本,不再需要显式地使用
Class.forName()
方法来加载JDBC驱动,因为驱动程序在被调用时会自动加载。但在旧版本中,加载MySQL JDBC驱动的代码如下:
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
- 建立连接:使用
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中,主要通过Statement
、PreparedStatement
和CallableStatement
接口来执行SQL语句。
- 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
中。最后,通过ResultSet
的next()
方法遍历结果集,并获取每一行的数据。
- 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插入语句。通过PreparedStatement
的setString()
方法为占位符赋值,然后通过executeUpdate()
方法执行插入操作。executeUpdate()
方法返回受影响的行数,如果返回值大于0,则表示插入成功。
- 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(?)}
,其中?
是存储过程的参数。通过CallableStatement
的setInt()
方法为参数赋值,然后执行存储过程并处理结果集。
处理结果集
当执行查询语句(如SELECT
语句)时,会返回一个ResultSet
对象,该对象包含了查询结果。ResultSet
对象提供了一系列方法来获取结果集中的数据。
- 遍历结果集:通过
ResultSet
的next()
方法可以将游标移动到下一行,当游标指向有效行时,next()
方法返回true
,否则返回false
。例如:
ResultSet resultSet = statement.executeQuery(sql);
while (resultSet.next()) {
// 获取数据
}
- 获取列数据:
ResultSet
提供了多种方法来获取不同类型的列数据,如getInt()
、getString()
、getDouble()
等。获取列数据时,可以通过列名或列索引来指定列。例如:
int id = resultSet.getInt("id");
String name = resultSet.getString(2);
上述代码中,getInt("id")
通过列名获取id
列的值,getString(2)
通过列索引(从1开始)获取第二列的值。
- 结果集的类型和并发模式:
ResultSet
有不同的类型和并发模式。默认情况下,ResultSet
是TYPE_FORWARD_ONLY
类型,即只能向前遍历结果集,并且是CONCUR_READ_ONLY
模式,即只能读取数据,不能更新数据。如果需要更灵活的操作,可以在创建Statement
或PreparedStatement
时指定ResultSet
的类型和并发模式。例如:
Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);
上述代码创建了一个可滚动且可更新的ResultSet
。TYPE_SCROLL_INSENSITIVE
表示结果集可以前后滚动,并且对数据库的更改不敏感;CONCUR_UPDATABLE
表示可以通过ResultSet
更新数据库中的数据。
事务处理
在数据库操作中,事务是一组逻辑上相关的操作,这些操作要么全部成功执行,要么全部失败回滚。JDBC提供了对事务处理的支持。
- 开启事务:默认情况下,JDBC的连接是自动提交模式,即每执行一条SQL语句就会立即提交到数据库。要开启事务,需要将连接的自动提交模式设置为
false
。例如:
connection.setAutoCommit(false);
- 提交事务:当所有需要在事务中执行的SQL语句都成功执行后,调用
connection.commit()
方法来提交事务,将所有操作持久化到数据库。例如:
try {
// 执行SQL语句
connection.commit();
} catch (SQLException e) {
e.printStackTrace();
}
- 回滚事务:如果在事务执行过程中出现异常,调用
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连接池有C3P0
、HikariCP
、Druid
等。以HikariCP
为例,使用步骤如下:
- 添加依赖:如果使用Maven,在
pom.xml
文件中添加HikariCP
的依赖:
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>4.0.3</version>
</dependency>
- 配置连接池:在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
对象,通过该对象获取数据库连接。
- 使用连接池获取连接:在需要连接数据库的地方,调用
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();
}
}
}
}
}
通过使用连接池,可以大大提高应用程序的性能和资源利用率。
常见问题及解决方法
- 驱动加载问题:如果在加载JDBC驱动时出现
ClassNotFoundException
异常,可能是JDBC驱动没有正确添加到项目的类路径中。检查.jar
文件是否存在,以及是否在IDE中正确配置了类路径。如果使用Maven,检查pom.xml
文件中依赖是否正确添加。 - 连接失败问题:连接数据库失败可能有多种原因,如数据库服务未启动、用户名或密码错误、数据库URL配置错误等。可以检查数据库服务状态,确保用户名和密码正确,以及数据库URL的格式是否正确。例如,MySQL 8.0及以上版本的JDBC URL可能需要添加时区等参数,如
jdbc:mysql://localhost:3306/test?serverTimezone=UTC
。 - 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);
如果用户输入的username
为admin' 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();
- 资源未释放问题:在使用JDBC时,如果没有正确关闭
Connection
、Statement
、ResultSet
等资源,可能会导致资源泄漏,影响应用程序的性能。应在代码中使用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语句执行、事务处理、连接池技术以及常见问题解决等方面,都进行了详细的介绍和示例展示。在实际应用开发中,根据具体需求合理运用这些知识,能够开发出高效、稳定的数据库相关应用程序。