【搞定JavaWeb】之JDBC

Java数据库连接,简称JDBC(java Database Connectivity),是一种用于执行SQL语句的java API,它由一组java编写的类和接口组成。JDBC为工具/数据库开发人员提供了一个标准的API,据此可以构建更高级的工具和接口,使数据的开发人员能够用纯java API编写数据库应用程序。

有了JDBC 向各种关系型数据库发送SQL语句就是一件很容易的事。是一个独立于数据库的管理系统,提供了通用的SQL数据库存取操作的接口(CRUD:Create Read Update Delete),定义了一组同一标准,为访问不同数据库提供同一途径。它是由sum公司开发的一套系统组件,供开发者直接调用。

1  程序是如何同数据库进行沟通的?

数据库本身就是一个独立运行的应用程序,编写应用程序就是利用网络通信协议与数据库进行命令交换,来进行命令的增删查找。

双层架构:

 

  • 问题的重点在于,你的应用程序如何调用这组程序库?

因为每个数据库的通常有不同的通信协议,用于连接不同数据库在API上也会有所不同。【JDBC解决的问题】

JDBC 基本上就是用来解决这些问题,当应用程序需要连接数据库就调用这组标准的API,而标准的API中的接口由数据库厂商实现,通常称为JDBC驱动程序(Driver)。

2  JDBC分为两部分

  1. JDBC 应用程序开发者接口((ApplicationDeveloper Interface);
  2. JDBC 驱动程序开发者接口 (Driver Developer Interface)),驱动程序接口是数据库厂商要实现驱动程序时的规范,一般开发者并不用了解。

3、使用JDBC代码进行数据库连接处理

开发应用程序过程中,如果要操作数据库,我们是通过JDBC所提供的接口来实现设计程序的,理论上必须更换数据库的时候,应用程序不用修改,直接更换数据库驱动程序实现数据库的更换。

Connection conn = DriverManager.getConnection(.....);  //  驱动
Statement st = conn.createStatement();   // 声明
ResultSet rs = st.executeQuery("select * from User");   // executeQyery执行命令

假设上面段代码是连接MySQL数据库,你会需要在Classpath中设置MySQL对应JDBC的驱动程序。具体来说,就是在Classpath 中设置一个JAR文件此时应用程序、JDBC 与数据库的关系如下图所示:

连接MySQL数据库:

连接Oracle数据库:

总之:安装好数据库之后,我们的应用程序并不能直接使用数据库,必须通过相应的数据库驱动程序去和数据库打交道。其实也就是数据库厂商的JDBC接口的实现,即对Connection等接口的实现类的jar文件。

4  常用接口

1、Driver接口

Driver接口由数据库厂商提供,作为java开发人员,之需要使用Driver接口就可以了。在编程中要连接数据库必须先安装特定厂商的数据库驱动程序,不同的数据库有不同的装载方法。

装载MySQL驱动:Class.forName("com.mysql.jdbc.Driver");

装载Oracle驱动:Class.forName("oracle.jabc.driver.OracleDriver");

2、DriverManager

JDBC的DriverManager是一个工厂类,我们通过它来创建数据库连接。当JDBC的Driver类被加载进来时,它会自己注册到DriverManager类里面,你可以看下JDBC Driver类的源码来了解一下。

3、Connection 接口

Connection 与特定数据库的连接(会话),在上下文中执行sql语句并返回结果。此接口有接触数据库的所有方法,连接对象便是通信上下文,即与数据库中所有的通信都是通过此唯一的连接对象。

     DriverManager.getConnection(url, user, password) ;  建立url 中定义的数据库连接
    
     连接Mysql 数据库(其他数据库类似):

Connection connection = DriverManager.getConnection("jdbc:mysql://host:port/database","user","password");

常用的方法:

  • createStatement(): 创建向数据库发送sql的statement对象。
  • prepareStatement(sql):创建相数据库发送预编译sql的PrepredStatement对象。
  • prepareCall(sql) : 创建执行存储过程的callableStatement对象。
  • setAutoCommit(boolean autoCommit) : 设置事务是否自动提交。
  • commit() :在连接上提交事务
  • rollback():在此连接上回滚事务

4、 Statement 接口

作用:用于执行静态sql语句并返回它所生成结果的对象。

三种Statement 类:

1、Statement:由createStatement创建,用于发送简单的sql语句(不带参数)

2、PreparedStatement:继承自Statement 接口,由prepareStatement(sql)创建,用于发送含有一个或者多个参数的sql语句。PreparedStatement对象采用预编译,比Statement对象效率更高。并且可以防止sql注入。

3、CallableStatement: 继承自PreparedStatement接口,由方法prepareCall()创建,用于存储调用过程。

常用的Statement方法:

  • execute(String sql)                  运行语句,返回是否有结果集
  • executeQuery(String sql)   运行select语句,返回ResulySet结果集
  • executeUpdate(String sql) 运行insert/delete/update 操作,返回更新的行数。
  • addBatch(String sql)          把多条sql语句放到一个批处理中
  • executeBatch()                   想数据库发送一批sql语句执行

5、ResultSet接口

查询结果集,提供了检索不同类型字段的方法,常用的有:

  • getString(int index)   /  getString(String columnName)  获得在数据库里是varchar ,char 类型的数据对象
  • getFloat            获得在数据库里是float类型的数据对象
  • getDate            获得在数据库里是日期类型的数据对象
  • getBoolean      获得在数据库里是布尔类型的数据对象
  • getObjejct        获得在数据库里是对象类型的数据对象 

ResultSet还提供了对结果集进行滚动的方法:

  • next() 移动到下一行
  • previous() 移动到前一行
  • absolute(int row) 移动到指定行
  • beforeFirst()移动到ResultSet 的最前面
  • afterLast()移动到ResultSet 的最后面
  • 使用后依次关闭对象及连接:ResultSet ->  Statement ->Connection

5  使用JDBC步骤

加载JDBC驱动程序 --> 建立数据库连接Connection  -->  创建执行sql的语句Statement  --> 处理执行结果 ResultSet  --> 释放资源

1. 注册驱动(一次即可)

方式一:Class.forName("com.Mysql.jdbc.Driver");   

推荐使用该方式,不会对具体的驱动列产生依赖。

方式二: DriverManager.registerDriver(com.mysql.jdbc.Driver);

会造成DriverManager中产生两个一样的驱动,并会对具体的驱动类产生依赖

2.建立连接

Connection con = DriverManager.getConnection(url,uesr,password);

3. 创建执行sql语句的Statement

// 创建执行sql语句的Statement 
String id = "5";
String sql = "delete from table where id=" +  id;
Statement st = conn.createStatement();  
st.executeQuery(sql);  

说明:上诉的SQL语句存在SQL注入的危险。如果用户传入的id为“5 or 1=1”,那么将删除表中的所有记录。

解决办法:PreparedStatement 有效的防止sql注入

SQL语句在程序运行前已经进行了预编译,当运行时动态地把参数传给PreprareStatement时,即使参数里有敏感字符如 or '1=1'也数据库会作为一个参数一个字段的属性值来处理而不会作为一个SQL指令。

// PreparedStatement 有效的防止sql注入
String sql = “insert into user (name,pwd) values(?,?)”;  
PreparedStatement ps = conn.preparedStatement(sql);  
ps.setString(1, “col_value”);  // 占位符顺序从1开始
ps.setString(2, “123456”);     // 也可以使用setObject
ps.executeQuery();

4. 处理结果集ResultSet

ResultSet rs = ps.executeQuery();
while(rs.next()){
    rs.getString("col_name");
    rs.getInt(2);
...
 
}

5、释放资源

数据库连接(Connection)非常耗资源,尽量晚创建,尽量早的释放。都要加try catch 以防前面关闭出错,后面的就不执行了。先创建的后关闭,后创建的先关闭。

try {
    if (rs != null) {
        rs.close();
    }
} catch (SQLException e) {
    e.printStackTrace();
} finally {
    try {
        if (st != null) {
            st.close();
        }
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        try {
            if (conn != null) {
                conn.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

6  事务

事务主要重点是:ACID特点、隔离级别、提交、回滚。

6.1  事务的基本概念

一组要么同时成功,要么同时失败的sql语句,是数据库操作的一个基本执行单元。

事务开始于:

  • 连接到数据库上,并执行一条DML语句(Insert/delete/update)。
  • 前一个事务结束后,又输入了另外一条DML语句。

事务结束于:

  • 执行commit或rollback 语句。
  • 执行一条DCL语句,例如create table 语句,在次此情况下,会自动执行commit语句。
  • 执行一条DCL语句,例如grant语句,在此情况下,会自动自行commit语句。
  • 断开与数据库的连接。
  • 执行了一条DML语句,该语句失败了,在此情况下,会为这个无效的DML语句执行rollback。

6.2  事务的ACID

  1. atomicity 原子性:表示一个事务内的所有操作是一个整体,要么全成功,要么全失败。
  2. consistency 一致性:表示一个事务内有一个操作失败时,所有的更改过的数据都必须回滚到修改前的状态。
  3. isolation 隔离性:事务查看数据时数据所处的状态,要么是另一个并发事务修改它之前的状态,要么是另一事物修改它之后的状态,事务不会查看中间状态的数据。
  4. durability 持久性:持久性事务完成之后,它对于系统的影响是永久性的。

6.3  事务的隔离级别

  1. 读取未提交数据:READ  UNCOMMITED
  2. 读取已提交数据:READ  COMMITED
  3. 可重复读:REPETABLE  READ
  4. 序列化:SERIALIZABLE

具体事务隔离级别的讲解:https://blog.csdn.net/pcwl1206/article/details/84478127



下面MARK下JDBC常问的面试题:

1.什么是JDBC,在什么时候会用到它? 

  • JDBC的全称是Java DataBase Connection,也就是Java数据库连接,我们可以用它来操作关系型数据库。JDBC接口及相关类在java.sql包和javax.sql包里。我们可以用它来连接数据库,执行SQL查询,存储过程,并处理返回的结果。
  • JDBC接口让Java程序和JDBC驱动实现了松耦合,使得切换不同的数据库变得更加简单。

2.JDBC是如何实现Java程序和JDBC驱动的松耦合的? 

  • JDBC API使用Java的反射机制来实现Java程序和JDBC驱动的松耦合。随便看一个简单的JDBC示例,你会发现所有操作都是通过JDBC接口完成的,而驱动只有在通过Class.forName反射机制来加载的时候才会出现。
  • 我觉得这是Java核心库里反射机制的最佳实践之一,它使得应用程序和驱动程序之间进行了隔离,让迁移数据库的工作变得更简单。在这里可以看到更多JDBC的使用示例。

3 .什么是JDBC连接,在Java中如何创建一个JDBC连接?

JDBC连接是和数据库服务器建立的一个会话。你可以想像成是一个和数据库的Socket连接。创建JDBC连接很简单,只需要两步:

  • A. 注册并加载驱动:使用Class.forName(),驱动类就会注册到DriverManager里面并加载到内存里。
  • B. 用DriverManager获取连接对象:调用DriverManager.getConnnection()方法并传入数据库连接的URL,用户名及密码,就能获取到连接对象。

4 .JDBC的DriverManager是用来做什么的?

  • JDBC的DriverManager是一个工厂类,我们通过它来创建数据库连接。当JDBC的Driver类被加载进来时,它会自己注册到DriverManager类里面,你可以看下JDBC Driver类的源码来了解一下。
  • 然后我们会把数据库配置信息传入DriverManager.getConnection()方法,DriverManager会使用注册到它里面的驱动来获取数据库连接,并返回给调用的程序。

5 . 在Java程序中,如何获取数据库服务器的相关信息? 

  • 使用DatabaseMetaData可以获取到服务器的信息。当和数据库的连接成功建立了之后,可以通过调用getMetaData()方法来获取数据库的元信息。DatabaseMetaData里面有很多方法,通过它们可以获取到数据库的产品名称,版本号,配置信息等。 
DatabaseMetaData metaData = con.getMetaData(); 
String dbProduct = metaData.getDatabaseProductName();

6.JDBC的Statement是什么? 

  • Statement是JDBC中用来执行数据库SQL查询语句的接口。通过调用连接对象的getStatement()方法我们可以生成一个Statement对象。我们可以通过调用它的execute(),executeQuery(),executeUpdate()方法来执行静态SQL查询。
  • 默认情况下,一个Statement同时只能打开一个ResultSet。如果想操作多个ResultSet对象的话,需要创建多个Statement。Statement接口的所有execute方法开始执行时都默认会关闭当前打开的ResultSet。

7 .execute,executeQuery,executeUpdate的区别是什么?

  1. Statement的execute(String query)方法用来执行任意的SQL查询,如果查询的结果是一个ResultSet,这个方法就返回true。如果结果不是ResultSet,比如insert或者update查询,它就会返回false。我们可以通过它的getResultSet方法来获取ResultSet,或者通过getUpdateCount()方法来获取更新的记录条数。
  2. Statement的executeQuery(String query)接口用来执行select查询,并且返回ResultSet。即使查询不到记录返回的ResultSet也不会为null。我们通常使用executeQuery来执行查询语句,这样的话如果传进来的是insert或者update语句的话,它会抛出错误信息为 “executeQuery method can not be used for update”的java.util.SQLException。
  3. Statement的executeUpdate(String query)方法用来执行insert或者update/delete(DML)语句,或者什么也不返回的DDL语句。返回值是int类型,如果是DML语句的话,它就是更新的条数;如果是DDL的话,就返回0。
  • 只有当你不确定是什么语句的时候才应该使用execute()方法,否则应该使用executeQuery或者executeUpdate方法。

8 .JDBC的PreparedStatement是什么?

  • PreparedStatement对象代表的是一个预编译的SQL语句。用它提供的setter方法可以传入查询的变量。
  • 由于PreparedStatement是预编译的,通过它可以将对应的SQL语句高效的执行多次。由于PreparedStatement自动对特殊字符转义,避免了SQL注入攻击,因此应当尽量的使用它。

9 . PreparedStatement中如何注入NULL值? 

  • 可以使用它的setNull方法来把null值绑定到指定的变量上。setNull方法需要传入参数的索引以及SQL字段的类型,像这样: ps.setNull(10, java.sql.Types.INTEGER);

10 .相对于Statement,PreparedStatement的优点是什么? 

  • PreparedStatement有助于防止SQL注入,因为它会自动对特殊字符转义。
  • PreparedStatement可以用来进行动态查询。
  • PreparedStatement执行更快。尤其当你重用它或者使用它的拼量查询接口执行多条语句时。
  • 使用PreparedStatement的setter方法更容易写出面向对象的代码,而Statement的话,我们得拼接字符串来生成查询语句。如果参数太多了,字符串拼接看起来会非常丑陋并且容易出错。

11 .如何回滚事务?

  • 通过Connection对象的rollback方法可以回滚事务。它会回滚这次事务中的所有修改操作,并释放当前连接所持有的数据库锁。

12 .JDBC的DataSource是什么,有什么好处?

DataSource即数据源,它是定义在javax.sql中的一个接口,跟DriverManager相比,它的功能要更强大。我们可以用它来创建数据库连接,当然驱动的实现类会实际去完成这个工作。除了能创建连接外,它还提供了如下的特性:

  • 缓存PreparedStatement以便更快的执行
  • 可以设置连接超时时间
  • 提供日志记录的功能
  • ResultSet大小的最大阈值设置
  • 通过JNDI的支持,可以为servlet容器提供连接池的功能

13.如何通过JDBC的DataSource和Apache Tomcat的JNDI来创建连接池? 

  • -------

14.什么是数据库的隔离级别?

  • 当我们为了数据的一致性使用事务时,数据库系统用锁来防止别人访问事务中用到的数据。数据库通过锁来防止脏读,不可重复读(Non-Repeatable Reads)及幻读(Phantom-Read)的问题。
  • 数据库使用JDBC设置的隔离级别来决定它使用何种锁机制,我们可以通过Connection的getTransactionIsolation和setTransactionIsolation方法来获取和设置数据库的隔离级别。

15.JDBC的RowSet是什么,有哪些不同的RowSet? 

  • RowSet用于存储查询的数据结果,和ResultSet相比,它更具灵活性。RowSet继承自ResultSet,因此ResultSet能干的,它们也能,而ResultSet做不到的,它们还是可以。RowSet接口定义在javax.sql包里。

RowSet提供的额外的特性有: 

  • 提供了Java Bean的功能,可以通过settter和getter方法来设置和获取属性。RowSet使用了JavaBean的事件驱动模型,它可以给注册的组件发送事件通知,比如游标的移动,行的增删改,以及RowSet内容的修改等。
  • RowSet对象默认是可滚动,可更新的,因此如果数据库系统不支持ResultSet实现类似的功能,可以使用RowSet来实现。

16 .常见的JDBC异常有哪些? 

  • java.sql.SQLException——这是JDBC异常的基类。
  • java.sql.BatchUpdateException——当批处理操作执行失败的时候可能会抛出这个异常。这取决于具体的JDBC驱动的实现,它也可能直接抛出基类异常java.sql.SQLException。
  • java.sql.SQLWarning——SQL操作出现的警告信息。
  • java.sql.DataTruncation——字段值由于某些非正常原因被截断了(不是因为超过对应字段类型的长度限制)。

转载自:

1、https://mp.weixin.qq.com/s/pje-umK0ySzzaS_yX4qsuw

2、https://blog.csdn.net/limin0983/article/details/73500035

3、https://blog.csdn.net/qq_33290787/article/details/51924963

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值