窥见JDBC
一、JDBC概述
在Web开发中,不可避免的要使用数据库存储和管理数据。为了在Java语言中提供对数据库访问的支持,SUN公司于1996年提供了一套访问数据库的标准Java类库,即JDBC(Java Database Connectivity-Java数据库连接)。
它是一套用于执行SQL语句的Java API。应用程序可通过这套API连接到关系型数据库,并使用SQL语句完成对数据库中数据的查询、更新、新增和删除操作。
1、JDBC的优势
统一规范,一套解决
- 不同种类的数据库(如MySQL、Oracle等)在内部处理数据的方式是不同的,如果直接使用数据库厂商提供的访问接口操作数据库,应用程序的可移植性就会变得很差。例如,用户当前在程序中使用的是MySQL提供的接口操作数据库,如果换成Oracle数据库,则需要重新使用Oracle数据库提供的接口,这样代码的改动量会非常大。
- 有了JDBC后,这问题就不复存在了,因为它要求各个数据库厂商按照统一的规范提供数据库驱动,而在程序中由JDBC和具体的数据库驱动联系,所以用户就不必直接与底层的数据库交互,这使得代码的通用性更强。
2、JDBC连接数据库的方式
JDBC在应用程序与数据库之间起到了一个桥梁作用,当应用程序使用JDBC访问特定的数据库时,需要通过不同数据库驱动与不同的数据库进行连接,连接后即可对数据库进行相应的操作。
二、JDBC常用API
1、Driver接口
Driver接口是所有JDBC驱动程序必须实现的接口,该接口专门提供给数据库厂商使用。需要注意的是,在编写JDBC程序时,必须要把所使用的数据库驱动程序或类库加载到项目的classpath中(这里指MySQL驱动JAR包-即mysql-connector-java)。
2、DriverManager接口
DriverManager类用于加载JDBC驱动并且创建与数据库的连接。在DriverManager类中,定义了两个比较重要的静态方法,如下表所示。
方法名称 | 功能描述 |
---|---|
registerDriver(Driver driver) | 该方法用于向DriverManager中注册给定的JDBC驱动程序 |
getConnection(String url,String user,String pwd) | 该方法用于建立和数据库的连接,在方法中传入数据库的路径,用户名和密码,并返回表示连接的Connection对象 |
3、Connection接口
Connection接口表示Java程序和数据库的连接,只有获得该连接对象后才能访问数据库,并操作数据表。
方法名称 | 功能描述 |
---|---|
prepareStatement(String sql) | 传入操作数据库表的sql语句,用于创建一个PreparedStatement对象并将参数化的SQL语句发送到数据库。 |
4、PreparedStatement接口
PreparedStatement是Statement的子接口,用于执行预编译的SQL语句。PreparedStatement接口扩展了带有参数的SQL语句的执行操作,应用该接口中的SQL语句可以使用占位符“?”代替参数,然后通过setter()方法为SQL语句的参数赋值。常用方法如下表。
方法名称 | 功能描述 |
---|---|
executeUpdate() | 在此PreparedStatement对象中执行SQL语句,该语句必须是一个DML语句或者是无返回内容的SQL语句,比如 DDL 语句(添加-insert、删除-delete、修改-update),返回受影响的行。 |
executeQuery() | 在此PreparedStatement对象中执行SQL查询(select),该方法返回的是ResultSet对象 |
setInt(int parameterIndex, int x) | 第一个参数表示占位符的位置,第二个参数表示传入数据表的int类型的值 |
setFloat(int parameterIndex, float x) | 将指定参数设置为给定的float值 |
setString(int parameterIndex, String x) | 将指定参数设置为给定的String值 |
setDate(int parameterIndex, Date x) | 将指定参数设置为给定的Date值,参数Date的类型是java.sql.Date,而不是java.util.Date |
addBatch() | 将一组参数添加到此PreparedStatement对象的批处理命令中 |
setCharacterStream(int parameterIndex, java.io.Reader reader,int length) | 将指定的输入流写入数据库的文本字段 |
setBinaryStream(int parameterIndex, java.io.InputStream x, int length) | 将二进制的输入流数据写入到二进制字段中 |
5、ResultSet接口
ResultSet接口用于保存JDBC执行查询时返回的结果集,该结果集封装在一个逻辑表格中。在ResultSet接口内部有一个指向表格数据行的游标(指针),ResultSet对象初始化时,游标在表格的第一行之前,调用next()方法可将游标移动到下一行。如果下一行没有数据,则返回false。在应用程序中经常调用next()方法作为while循环的条件来迭代ResultSet结果集。
方法名称 | 功能描述 |
---|---|
next() | 将游标从当前位置向下移一行 |
getString(int columnIndex) | 用于获取指定字段的String类型的值,参数columnIndex代表字段的索引 |
getString(String columnName) | 用于获取指定字段的String类型的值,参数columnName代表字段的名称 |
getInt(int columnIndex) | 用于获取指定字段的int类型的值,参数columnIndex代表字段的索引 |
getInt(String columnName) | 用于获取指定字段的int类型的值,参数columnName代表字段的名称 |
getDate(int columnIndex) | 用于获取指定字段的Date类型的值,参数columnIndex代表字段的索引 |
getDate(String columnName) | 用于获取指定字段的Date类型的值,参数columnName代表字段的名称 |
ResultSet接口中定义了大量的getter()方法,而采用哪种getter()方法获取数据,取决于字段的数据类型。程序既可以通过字段的名称获取指定数据,也可以通过字段的索引获取指定的数据,字段的索引是从1开始编号的。例如,数据表的第一列字段名为id,字段类型为int,那么既可以调用getInt(1),以字段索引的方式获取该列的值,也可以调用getInt(“id”),以字段名称的方式获取该列的值。
三、第一个JDBC程序
1、在项目中导入mysql的驱动包
将mysql-connector-java.jar导入项目中,并添加到项目的jar包库。
或者
使用maven导入mysql驱动jar包
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
2、具体实现步骤
2.1、加载并注册数据库驱动
Class.forName("DriverName");
2.2、通过DriverManager获取数据库连接
Connection conn = DriverManager.getConnection(String url, String user, String pwd);
2.3、编写SQL语句
添加:String sqlAdd = "insert into 表名(字段)values(值)"
删除:String sqlDel = "delete from 表名 where 删除条件"
修改:String sqlUpd = "update 表名 set 字段 = 值 where 修改条件"
查询:String sqlSel = "select * from 表名 where 查询条件 group by 分组条件 order by 排序条件 limit m,n"
2.4、通过Connection对象获取PreparedStatement对象
PreparedStatement pst = conn.prepareStatement(sql)
2.5、执行sql操作
ResultSet rs = pst.executeQuery():通常执行查询语句,执行后返回代表结果集的ResultSet对象。
int row = pst.executeUpdate():主要用于执行DML和DDL语句。执行DML语句如 INSERT、UPDATE或 DELETE时,返回受SQL语句影响的行数。
2.6、关闭连接,释放资源
调用close()方法。每次操作数据库结束后都要关闭数据库连接,释放资源,包括ResultSet、Statement和Connection等资源。
3、main方法测试
3.1、准备数据
-- 创建数据库
create database kdftest;
-- 使用数据库
use kdftest;
-- 创建部门表
DROP TABLE IF EXISTS `tb_dept`;
CREATE TABLE `tb_dept` (
`id` int(0) NOT NULL AUTO_INCREMENT COMMENT '部门编号',
`dept_name` varchar(20) NOT NULL COMMENT '部门名称',
`dept_desc` varchar(200)DEFAULT NULL COMMENT '部门简介',
PRIMARY KEY (`id`)
) ;
-- 添加数据
INSERT INTO `tb_dept` VALUES (1, '研发部', '负责公司的产品研发工作');
INSERT INTO `tb_dept` VALUES (2, '市场部', '负责市场推广和客户关系管理');
INSERT INTO `tb_dept` VALUES (3, '销售部', '负责销售和客户关系维护');
INSERT INTO `tb_dept` VALUES (4, '人力资源部', '负责招聘、培训和员工关系管理');
INSERT INTO `tb_dept` VALUES (5, '财务部', '负责公司的财务管理和报表制作');
3.2、main方法测试代码1
需求1:往部门表添加一条数据。
import java.sql.*;
public class AddTest {
public static void main(String[] args) {
Connection con = null;
PreparedStatement pst = null;
ResultSet rs = null;
//1、加载驱动
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//2.获取连接,传入数据库的路径,用户名和密码
try {
con = DriverManager.getConnection("jdbc:mysql://localhost:3306/kdftest", "root", "root");
//3.编写sql语句,变量使用占位符填充
String addSql = "insert into tb_dept(dept_name,dept_desc) values (?,?)";
//4.预编译sql语句
pst = con.prepareStatement(addSql);
//5.给占位符赋值
pst.setString(1,"开发不");
pst.setString(2,"需求编写");
//6.执行查询操作,返回受影响的行,成功返回1,失败返回0
int row = pst.executeUpdate();
System.out.println("添加结果:"+row);
} catch (SQLException e) {
e.printStackTrace();
}finally {
//7.释放资源
try {
if(pst!=null){
pst.close();
}
if(con!=null){
con.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
3.2、main方法测试代码2
需求2:查询部门表中id为1,或者部门名称是财务部的数据。
import java.sql.*;
public class QueryTest {
public static void main(String[] args) {
Connection con = null;
PreparedStatement pst = null;
ResultSet rs = null;
//1、加载驱动
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//2.获取连接,传入数据库的路径,用户名和密码
try {
con = DriverManager.getConnection("jdbc:mysql://localhost:3306/kdftest", "root", "root");
//3.编写sql语句,变量使用占位符填充
String addSql = "select * from tb_dept where id = ? or dept_name = ?";
//4.预编译sql语句
pst = con.prepareStatement(addSql);
//5.给占位符赋值
pst.setInt(1,1);
pst.setString(2,"财务部");
//6.执行查询操作,返回结果集
rs = pst.executeQuery();
//循环结果集,获取数据
while (rs.next()){
String dept_name = rs.getString("dept_name");//参数为数据库字段
String dept_desc = rs.getString("dept_desc");
//在控制台输出
System.out.println(dept_name+","+dept_desc);
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
//7.释放资源
try {
if (rs!=null){
rs.close();
}
if(pst!=null){
pst.close();
}
if(con!=null){
con.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
3.3查询成功
运行main方法,在控制台看到如下结果即为查询成功
四、提取工具类
由于每次操作数据库时,都需要加载数据库驱动、建立数据库连接以及关闭数据库连接,为了避免代码的重复书写,所以需要建立一个专门用于操作数据库的工具类。在src下创建一个包comkdf.utils,在包中创建一个封装了上述操作的工具类JDBCUtils。
import java.sql.*;
public class JDBCUtils {
private static final String DRIVER = "com.mysql.cj.jdbc.Driver";
private static final String URL = "jdbc:mysql://localhost:3306/kdftest";
private static final String NAME = "root";
private static final String PASSWORD = "root";
static {
try {
//加载驱动
Class.forName(DRIVER);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
//获取连接
public static Connection getCon(){
Connection connection = null;
try {
connection = DriverManager.getConnection(URL, NAME, PASSWORD);
} catch (SQLException e) {
e.printStackTrace();
}
return connection;
}
//释放资源
public static void close(Connection con, PreparedStatement pst, ResultSet rs){
try {
if (con!=null){
con.close();
}
if(pst!=null){
pst.close();
}
if(rs!=null){
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
恭喜你耐心学完了JDBC基础,奖励自己一个大笔都吧!