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分为两部分
- JDBC 应用程序开发者接口((ApplicationDeveloper Interface);
- 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
- atomicity 原子性:表示一个事务内的所有操作是一个整体,要么全成功,要么全失败。
- consistency 一致性:表示一个事务内有一个操作失败时,所有的更改过的数据都必须回滚到修改前的状态。
- isolation 隔离性:事务查看数据时数据所处的状态,要么是另一个并发事务修改它之前的状态,要么是另一事物修改它之后的状态,事务不会查看中间状态的数据。
- durability 持久性:持久性事务完成之后,它对于系统的影响是永久性的。
6.3 事务的隔离级别
- 读取未提交数据:READ UNCOMMITED
- 读取已提交数据:READ COMMITED
- 可重复读:REPETABLE READ
- 序列化: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的区别是什么?
- Statement的execute(String query)方法用来执行任意的SQL查询,如果查询的结果是一个ResultSet,这个方法就返回true。如果结果不是ResultSet,比如insert或者update查询,它就会返回false。我们可以通过它的getResultSet方法来获取ResultSet,或者通过getUpdateCount()方法来获取更新的记录条数。
- Statement的executeQuery(String query)接口用来执行select查询,并且返回ResultSet。即使查询不到记录返回的ResultSet也不会为null。我们通常使用executeQuery来执行查询语句,这样的话如果传进来的是insert或者update语句的话,它会抛出错误信息为 “executeQuery method can not be used for update”的java.util.SQLException。
- 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