虽然MyBatis等ORM(Object-Relational Mapping)框架在Java开发中变得非常流行,并且简化了数据库操作的复杂性,但学习JDBC仍然具有一定的重要性:
-
基础理解:学习JDBC可以帮助你深入理解数据库连接和操作的底层原理。了解JDBC可以让你更好地理解MyBatis等ORM框架是如何工作的,以及它们背后的实现机制。
-
灵活性:在一些特定的场景下,可能需要直接使用JDBC来完成一些特定的数据库操作。比如,在一些性能要求极高的场景下,直接使用JDBC可能比使用ORM框架更加高效。
-
问题排查:在开发过程中,可能会遇到一些与数据库操作相关的问题,此时对JDBC的理解会帮助你更好地进行问题排查和调试。
-
与其他技术的集成:JDBC作为Java的标准数据库连接API,很多其他的数据库相关技术都是基于JDBC来实现的。比如,连接池技术、分布式事务处理等都与JDBC紧密相关,因此对JDBC的了解可以帮助你更好地与这些技术进行集成。
-
面试准备:在一些面试中,面试官可能会问及JDBC相关的问题,对JDBC的了解可以帮助你更好地准备面试
JDBC,即Java Database Connectivity,java数据库连接。在开发中我们可能会使用到各种的数据库,每一种数据库都有着自己的一套“方法”。难道我们需要每使用到一种数据库就要学习掌握一种新的方法吗?这样的话学习成本也太高了。这时候我们java由于用户多,多到能够让java来制定“标准”,其他数据库厂商都来根据java的标准来对接自己的数据库,好比java来定义接口,各个数据库厂商来写接口一样。这样我们的java用户就可以学习java的标准来连接不用的数据库了。
1. 获取对应版本的jar包
那么想要使用JDBC就必须先安装对应数据库的驱动包,下面依旧是以MySQL为例。
我使用的MySQL版本是5.7
C:\Users\86134\Desktop>mysql --version
mysql Ver 14.14 Distrib 5.7.27, for Win64 (x86_64)
所以我就可以去中央仓库:https://mvnrepository.com/,来搜索mysql:
点进去之后,下拉选取对应的版本,我这里选取的是5.1.x,和自己的服务器版本对应。点击版本号之后,会进去对应版本的页面,然年后点击jar标识就会自动下载:
2. 将jar包导入到项目中
创建好一个java项目后,新建一个文件夹LIB,将下载的jar包复制到LIB文件夹下,然后右键选择
随便给它取一个名字,不妨就叫mysql-jar,然后点击ok,此时idea就已经导入了jar包,可以解析出jar包中包含的内容了。
3. JDBC使用步骤
3.1 创建数据库连接Connection
Connection接口实现类由数据库提供,获取Connection对象通常有两种方式:
1. 通过DriverManager(驱动管理类)的静态方法获取:
// 加载JDBC驱动程序
Class.forName("com.mysql.jdbc.Driver");// 创建数据库连接
Connection connection = DriverManager.getConnection(url);
2. 是通过DataSource(数据源)对象获取。实际应用中会使用DataSource对象。
DataSource ds = new MysqlDataSource();((MysqlDataSource) ds).setUrl("jdbc:mysql://localhost:3306/test");((MysqlDataSource) ds).setUser("root");((MysqlDataSource) ds).setPassword("root");Connection connection = ds.getConnection();
setUrl()方法中,传入的就是mysql的url,MySQL数据连接的URL参数格式如下:
jdbc:mysql://服务器地址:端口/数据库名?参数名=参数值
既然出现了两种不同的连接方式,那么就需要对比一下,他们的区别是什么?
1. DriverManager类来获取的Connection连接,是无法重复利用的,每次使用完以后释放资源 时,通过connection.close()都是关闭物理连接。
2. DataSource提供连接池的支持。连接池在初始化时将创建一定数量的数据库连接,这些连接 是可以复用的,每次使用完数据库连接,释放资源调用connection.close()都是将 Conncetion连接对象回收。
3.2 创建操作命令Statement
Statement对象主要是将SQL语句发送到数据库中。JDBC API中主要提供了三种Statement对象。
实际开发中最常用的是PreparedStatement对象。
PreparedStatement相对于Statement的优势:
-
预编译:
Statement
对象在执行SQL语句之前不进行编译,每次执行SQL语句时都会将SQL语句发送给数据库进行解析和编译。而PreparedStatement
对象在创建时就会预编译SQL语句,数据库会将编译后的执行计划缓存起来,以便重复使用。因此,使用PreparedStatement
可以提高性能,特别是在多次执行相同的SQL语句时。
-
SQL注入:
- 使用
Statement
执行SQL语句时,如果SQL语句中包含用户输入的数据,存在SQL注入的风险。因为SQL语句是在执行时动态拼接的,恶意用户可以通过在输入中插入恶意代码来执行额外的SQL操作。而PreparedStatement
通过参数化查询的方式,将SQL语句与用户输入的数据分开,避免了SQL注入的问题。
- 使用
-
可读性和维护性:
- 使用
PreparedStatement
可以使代码更具可读性和维护性。由于SQL语句与参数分开,易于理解和修改。
- 使用
-
性能:
- 由于
PreparedStatement
在创建时进行了预编译,并且执行计划被缓存,因此在某些情况下可能比Statement
执行速度更快。尤其是当需要多次执行相同的SQL语句时,PreparedStatement
通常会更高效
- 由于
使用案例:
Statement statement = connection.createStatement();
// 或者
PreparedStatement preparedStatement = connection.prepareStatement("SELECT * FROM mytable WHERE id = ?");
3.3 使用操作命令执行SQL
主要掌握两种执行SQL的方法:
- executeQuery() 方法执行后返回单个结果集的,通常用于select语句
- executeUpdate()方法返回值是一个整数,指示受影响的行数,通常用于update、insert、delete 语句
使用案例:
ResultSet resultSet = statement.executeQuery("SELECT * FROM mytable");
// 或者
preparedStatement.setInt(1,2);
ResultSet resultSet = preparedStatement.executeQuery();
3.4 处理结果集ResultSet
ResultSet对象它被称为结果集,它代表符合SQL语句条件的所有行,并且它通过一套getXXX方法提供了对这些行中数据的访问。 ResultSet里的数据一行一行排列,每行有多个字段,并且有一个记录指针,指针所指的数据行叫做当前数据行,我们只能来操作当前的数据行。我们如果想要取得某一条记录,就要使用ResultSet的next() 方法 ,如果我们想要得到ResultSet里的所有记录,就应该使用while循环。
案例:
while (resultSet.next()) {int id = resultSet.getInt("id");String sn = resultSet.getString("sn");String name = resultSet.getString("name");int classesId = resultSet.getInt("classes_id");System.out.println(String.format("Student: id=%d, sn=%s, name=%s, classesId=%s", id, sn, name, classesId));}
3.5 释放资源
释放资源需要关闭结果集、命令、连接(关闭顺序和创建顺序相反,这里就没有go语言中的defer还用了哈哈哈哈)
try {// 关闭结果集if (resultSet != null) {resultSet.close();}
} catch (SQLException e) {// 处理关闭结果集时可能发生的异常e.printStackTrace();
}try {// 关闭命令if (statement != null) {statement.close();}// 如果使用了 PreparedStatement,也要关闭它if (preparedStatement != null) {preparedStatement.close();}
} catch (SQLException e) {// 处理关闭命令时可能发生的异常e.printStackTrace();
}try {// 关闭连接if (connection != null) {connection.close();}
} catch (SQLException e) {// 处理关闭连接时可能发生的异常e.printStackTrace();
}
4. 完整使用案例代码
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;import javax.sql.DataSource;
import java.sql.*;public class JDBCDemo1 {public static void main(String[] args) {Connection connection = null;Statement statement = null;ResultSet resultSet = null;try {// 1. 创建数据库连接Connection//connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/csdn", "root", "yourPassWord");// 也可以通过DataSource获取连接DataSource ds = new MysqlDataSource();((MysqlDataSource) ds).setUrl("jdbc:mysql://localhost:3306/csdn?characterEncoding=UTF-8&useSSL=false");((MysqlDataSource) ds).setUser("root");((MysqlDataSource) ds).setPassword("yourPassWord");connection = ds.getConnection();// 2. 创建操作命令Statement 获取全部学生的id和namestatement = connection.createStatement();// 3. 使用操作命令来执行SQLString sqlQuery = "SELECT * FROM student";resultSet = statement.executeQuery(sqlQuery);// 4. 处理结果集ResultSetwhile (resultSet.next()) {// Process each row of the result set hereint id = resultSet.getInt("id");String name = resultSet.getString("name");// Do something with the retrieved dataSystem.out.println("ID: " + id + ", Name: " + name);}//2. 创建操作命令PreparedStatement 查询id == 1 的学生id和namePreparedStatement preparedStatement = connection.prepareStatement("SELECT * FROM student WHERE id = ?");// 3. 使用操作命令来执行SQL preparedStatement.setInt(参数index,参数值)preparedStatement.setInt(1,1);ResultSet resultSet1 = preparedStatement.executeQuery();// 4. 处理结果集ResultSetwhile (resultSet1.next()) {// Process each row of the result set hereint id = resultSet1.getInt("id");String name = resultSet1.getString("name");// Do something with the retrieved dataSystem.out.println("ID: " + id + ", Name: " + name);}} catch (SQLException e) {e.printStackTrace();} finally {// 5. 释放资源try {if (resultSet != null) {resultSet.close();}if (statement != null) {statement.close();}if (connection != null) {connection.close();}} catch (SQLException e) {e.printStackTrace();}}}
}
注意更换成你自己的数据库和自己的root账户的密码,创建一个student表里面包含id、name属性
执行结果:
5.重点总结
学习下面两个问题你能答得上来吗?
1. 数据库连接有哪些方式?分别有什么区别
2. 数据库Statement和PreparedStatement有什么区别?
答案见博文3.2&&3.2