JDBC详细介绍
1、JDBC
1.1、JDBC是什么
JDBC是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法。使我们可以在Java应用程序中调用标准的SQL命令对数据库进行操作。
1.2、JDBC驱动
JDBC驱动是各数据库厂商对JDBC接口的具体实现。
1.3、JDBC驱动加载
在加载驱动之前需要先导入jar包
方式一:通过new方式加载驱动
Driver driver = new com.mysql.jdbc.Driver();
方式二:通过反射加载驱动
Class<?> aClass = Class.forName("com.mysql.cj.jdbc.Driver");
Driver driver = (Driver)aClass.newInstance();
方式三:通过forName( )方法加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");
Class.forName(“xxx.xx.xx”)的作用:要求JVM查找并加载指定类,然后执行该类的静态代码。
2、DriverManager类
DriverManager类是JDBC的管理层,作用于用户和驱动程序之间,用来管理数据库中所有驱动程序。
它主要的两个功能是:注册和管理驱动和获取连接对象。下面是一张JDBC框架图,可以清楚的看到DriverManger在整个框架中的位置:
2.1、drivers属性
DiverManager.class里有个drivers属性,它是一个vector(向量),可在列表中加入多个驱动。当调用DriverManager.getConnection( )方法请求连接时,DriverManager会按照注册顺序检查每个驱动程序,直到找到第一个与给定URL连接成功的驱动程序。
2.2、注册和管理驱动
JDBC规范中,每个 Driver 类都必须向 DriverManager 中注册自己。我们先看一下com.mysql.jdbc.Driver的源代码
static {
try {
java.sql.DriverManager.registerDriver(new Driver()); //
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
注册方式一
DriverManager.registerDriver(new com.mysql.jdbc.Driver( ));
注册方式一存在的缺点:
-
在加载注册之前,静态代码块已经执行一次,相当于是实例化了两个Driver对象
-
在进行实例化了com.mysql.jdbc.Driver.class时,必须导入该类,对具体驱动产生了依赖。因此不方方便代码的扩展。
注册方式二
Class.forName("com.mysql.jdbc.Driver");
推荐该方式注册驱动,该方式并不依赖具体的驱动类。
2.3、获取连接对象
drivermanager.getconnection( )方法:用来建立给定URL的连接。
3、JDBC连接数据库
建立连接的五大步骤
- 加载注册JDBC驱动
- 建立数据库连接
- 执行SQL语句
- 处理执行结果集
- 关闭数据库连接
具体实现代码如下:
//1、加载注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2、建立数据库连接
String url = "jdbc:mysql://localhost:3306/books?serverTimezone=UTC";
//将用户名和密码放入Properties对象
Properties properties = new Properties();
properties.setProperty("user","root");
properties.setProperty("password","123456");
Connection connect = DriverManager.getConnection(url, properties); //连接数据库
//3、执行sql语句
String sql = "......";
//创建statement对象:statement用于执行静态SQL语句并返回其生成的结果对象
Statement statement = connect.createStatement();
//ResultSet:存储查询语句返回的结果对象,并提供相应的方法对结果对象进行操纵。
ResultSet rs = statement.executeUpdate(sql);
//4、处理执行结果集
//5、关闭数据库连接
statement.close();
connect.close();
当程序比较大,需要多次进行多次数据库操作时,我们可以连接数据库的相关信息(url,user和password)放在配置文件(jdbc.Properties)中,方便修改。我们最常使用的也是下面的方式:
- 编写jdbc.properties文件
user=用户名
password=密码
url=jdbc:mysql://localhost:****/test?rewriteBatchedStatements=true
driverClass=com.mysql.jdbc.Driver
- 连接过程
//1、加载配置文件
Properties properties = new Properties();
properties.load(new FileInputStream("src\\jdbc.properties"));
//2、从配置文件中获取相关参数
String url = properties.getProperty("url");
String root = properties.getProperty("user");
String password = properties.getProperty("password");
String driver = properties.getProperty("driver");
//3、加载注册驱动
Class.forName(driver);
//4、建立连接
Connection connection = DriverManager.getConnection(url, root, password);
//5、执行sql语句
//6、处理执行结果集
//7、关闭数据库连接
也可以将数据库的连接和关闭连接的操作封装为工具类,方便使用。
4、Statement和PreparedStatement
在上述代码执行sql语句时,我们首先创建了Statement对象,其作用是:执行静态SQL语句并返回其生成的结果对象。其实还有PreparedStatement对象和Statement对象具有同样的作用,在实际中我们使用创建PreparedStatement对象,这是因为Statement中存在SQL注入的风险。下面进行具体陈述:
4.1、SQL注入是什么
-
SQL注入是什么?
利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的SQL语句或命令,恶意攻击数据库。 -
万能密码
用户名:1’ or
密码:or ‘1’ = '1
select * from 表名
where name = '1' OR ' AND pass = 'OR '1' = '1'
‘1’ = '1’永远成立,因此输入的用户名和密码会登录成功。
4.2、Statement与PreparedStatement的区别
在Statement中存在SQL注入的风险。PreparedStatement针对Statemnet不足进行了改进,PreparedStatement具有以下的优点:
- 有效解决了sql注入问题。
- 不再使用 + 拼接sql语句,减少了语法错误。
- 大大减少了编译的次数,提高效率。
5、 ResultSet结果集
5.1、ResultSet结果集的作用
Resultset用来存储查询语句返回的结果对象,并提供相应的方法对结果对象进行操纵。
5.2、ResultSet中的常用方法
1、next( )方法
ResultSet对象保持一个光标指向其当前的数据行。 next方法将光标移动到下一行,当ResultSet对象中没有更多行时返回false 。因此在while循环中使用next( )方法作为循环条件,遍历整个结果集。
2、getXXX(第几列/列名)方法
获取当前行中,第几列/列名的数据,并且该列数据类型为XXX。
3、previous( )
使光标向上移动一行,没有上一行时返回false。
6、事务
6.1、什么是事务
事务是指访问并可能更新数据库中各种数据项的一个程序执行单元,是一个不可分割的工作单元。
6.2、事务处理方法
jdbc中事务操作是自动提交的,也就是说:当执行完一条SQL语句后,会自动调用commit( )方法提交执行结果。但是由于事务往往是好几条数据库语句执行完后才允许进行提交,因此可以通过调用setAutoCommit(false)来禁止自动提交。
把多个数据库操作的SQL语句作为一个事务,在操作完成后调用commit()来进行整体提交,倘若其中一个表达式操作失败,都不会执行到commit(),并且将产生响应的异常;此时就可以在异常捕获时调用rollback()进行回滚。这样做可以保持多次更新操作后,相关数据的一致性。
7、JDBC批处理操作
当需要成批插入或者更新记录时,采用批处理技术。批处理允许多条语句一次性提交给数据库批量处理,提高处理效率。
7.1、批处理语句
- addBatch(String):添加需要批量处理的SQL语句或是参数;
- executeBatch():执行批量处理语句;
- clearBatch():清空批处理语句。
7.2、批处理应用
使用批操作处理操作之前,需要在url中添加: ?rewriteBatchedStatements=true。
批处理应用代码演示如下:
public void testUseBatch() throws Exception {
// 1.获取连接
Connection connection = JDBCUtils.getConnection();
// 2.访问数据库
PreparedStatement statement = connection.prepareStatement("insert into admin values(null,?,?)");
for (int i = 1; i < 50000; i++) {
statement.setString(1, "小花" + i);
statement.setString(2, "666");
// 批处理
statement.addBatch();// 将要执行的sql语句添加到批处理包
if ((i+1) % 1000 == 0) {
statement.executeBatch();// 满1000条记录提交一次批处理命令
statement.clearBatch();// 清空批处理包
}
}
// 3.关闭
JDBCUtils.closeConnection(null, statement, connection);
}
7.3、批处理源码解析:addBatch( )
public void addBatch() throws SQLException{
for synchronized(this.checkClosed().getConnectionMutex()){
//第一次调用addBatch( )方法:创建ArrayList - elementData=>Object[]
//elementData=>Object[] 存放预处理的sql语句
//当elementData满的时候,会进行0.5的扩充
if(this.batchedArgs == null){
this.batchedArgs = new ArrayList();
}
for(int i= 0;i<this.parameterValues.length;++i){
this.checkAllParameterSet(this.parameterValues[i],this.parameterStreams[i],i);
}
this.batchedArgs.add(new PreparedStatement.BatchParams(this.parameterValues,this.parameterStreams)
}
}