一、概述JDBC
JDBC从物理结构上说就是Java语言访问数据库的一套接口集合。
从本质上来说就是调用者(程序员)和实现者(数据库厂商)之间的协议。
JDBC API 使得开发人员可以使用纯Java的方式来连接数据库,并进行操作。
ODBC:基于C语言的数据库访问接口。
JDBC:是Java版的ODBC。
JDBC 特性:高度的一致性、简单性(常用的接口只有4、5个)。
二、JDBC 编程的步骤
import java.sql.*;
0.参数化
String driverName = "com.mysql.jdbc.Driver";
String url = "jdbc:mysql://localhost:3306/test"; //协议;库或服务器名称;服务器IP,端口
String username = "root";
String password="";
/* Oracle的连接
String driverName = "oracle.jdbc.driver.OracleDriver";
String url = "jdbc:oracle:thin:@192.168.0.23:1521:ora10g";
String username = "openlab";
String password="open123";*/
//以下这些都需要写在有异常的代码块里,所以需要提取出来。
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;//建议用PreparedStatement
1.加载和注册数据库驱动
Class.forName(driverName);
//需要处理异常
2.连接数据库
Connection conn = DriverManager.getConnection(url,username,password);
3.创建Statement对象 //为了类型安全和批量更新的效率,改用PreparedStatement
PreparedStatement ps = conn.prepareStatement("sql");
4.操作数据库,执行SQL语句
ps.executeQuery();//select
返回查询结果对象ResultSet ps.executeUpdate();//insert/delete/update
返回int,几行记录收到影响
ps.execute();//other
返回boolean,成功失败
5.处理查询数据(游标)
ResultSet rs = ps.executeQuery();
while(rs.next()){
int pid = rs.getInt("字段名或字段索引");
String pname = rs.getString("pname");
int age = rs.getInt("age");
String sex = rs.getString("sex");
System.out.println(pid+" "+pname+" "+age+" "+sex);
}
6.释放资源,断开与数据库的连接
rs.close();
ps.close();
conn.close();
因为加载驱动是个一次性工作,所以可以采用静态初始化块来加载驱动;
释放数据库资源的方法要考虑到ResultSet、Statement、Connection的不同情况,并处理相关异常。
三、JDBC几个重要接口重点讲解
在JDBC 中包括了两个包:java.sql和javax.sql。
① java.sql 基本功能。
这个包中的类和接口主要针对基本的数据库编程服务,如生成连接、执行语句以及准备语句和运行批处理查询等。
同时也有一些高级的处理,比如批处理更新、事务隔离和可滚动结果集等。
② javax.sql 扩展功能。
它主要为数据库方面的高级操作提供了接口和类。
如为连接管理、分布式事务和旧有的连接提供了更好的抽象,它引入了容器管理的连接池、分布式事务和行集等。
API 说明
Connection 与特定数据库的连接(会话)。能够通过getMetaData方法获得数据库提供的信息、所支持的SQL语法、存储过程和此连接的功能等信息。代表了数据库。
Driver 每个驱动程序类必需实现的接口,每个数据库驱动程序也都应该提供一个实现Driver接口的类。
DriverManager(Class) 管理一组JDBC驱动程序的基本服务。作为初始化的一部分,此接口会尝试加载在”jdbc.drivers”系统属性中引用的驱动程序。只是一个辅助类,是工具。
Statement 用于执行静态SQL语句并返回其生成结果的对象。
PreparedStatement 继承Statement接口,表示预编译的SQL语句的对象,SQL语句被预编译并且存储在PreparedStatement对象中。然后可以使用此对象高效地多次执行该语句。
CallableStatement 用来访问数据库中的存储过程。它提供了一些方法来指定语句所使用的输入/输出参数。
ResultSet 指的是查询返回的数据库结果集。
ResultSetMetaData 可用于获取关于ResultSet对象中列的类型和属性信息的对象。
注:除了标出的Class,其它均为接口。每个都是“java.sql.”包下的。
1. Statement —— SQL语句执行接口
代表了一个数据库的状态,在向数据库发送相应的SQL语句时,都需要创建Statement接口或PreparedStatement接口。
在具体应用中,Statement主要用于操作不带参数(可以直接运行)的SQL语句,比如删除语句、添加或更新。
2. PreparedStatement:预编译的Statement
第一步:通过连接获得PreparedStatement对象,用带占位符(?)的sql语句构造。
PreparedStatement pstm = con.preparedStatement(“select * from test where id=?”);
第二步:设置参数
pstm.setString(1,“ganbin”);//第一个字段是“ganbin”;需一个个字段写
第三步:执行sql语句
Rs = pstm.excuteQuery();
statement发送完整的Sql语句到数据库不是直接执行而是由数据库先编译,再运行。每次都需要编译。
而PreparedStatement是先发送带参数的Sql语句,由数据库先编译,再发送一组组参数值。(同构时不需重复编译)
如果是同构的sql语句,PreparedStatement的效率要比statement高。而对于异构的sql则两者效率差不多。
一般都用PreparedStatement代替Statement,因为它是类型安全的。Statement对参数类型不作检查,故不够安全。
同构:两个Sql语句可编译部分是相同的,只有参数值不同。
异构:整个sql语句的格式是不同的
注意点:1、使用预编译的Statement编译多条Sql语句一次执行
2、可以跨数据库使用,编写通用程序
3、能用预编译时尽量用预编译
4、如果第二个SQL语句与前一个是异构的,需要再次编译“ps = con.prepareStatement(sql);“
3. ResultSet —— 结果集操作接口
ResultSet接口是查询结果集接口,它对返回的结果集进行处理。ResultSet是程序员进行JDBC操作的必需接口。
4. ResultSetMetaData —— 元数据操作接口
ResultSetMetaData是对元数据进行操作的接口,可以实现很多高级功能。
Hibernate运行数据库的操作,大部分都是通过此接口。可以认为,此接口是SQL查询语言的一种反射机制。
ResultSetMetaData接口可以通过数组的形式,遍历数据库的各个字段的属性,对于开发者来说,此机制的意义重大。
JDBC通过元数据(MetaData)来获得具体的表的相关信息,例如,可以查询数据库中有哪些表,表有哪些字段,以及字段的属性等。MetaData中通过一系列getXXX将这些信息返回给我们。
数据库元数据 Database MetaData 用connection.getMetaData()获得;包含了关于数据库整体元数据信息。
结果集元数据 ResultSet MetaData 用resultSet.getMetaData()获得;比较重要的是获得表的列名,列数等信息。
结果集元数据对象:ResultSetMetaData meta = rs.getMetaData();
字段个数:meta.getColomnCount();
字段名字:meta.getColumnName();
字段JDBC类型:meta.getColumnType();
字段数据库类型:meta.getColumnTypeName();
数据库元数据对象:DatabaseMetaData dbmd = con.getMetaData();
数据库名:dbmd.getDatabaseProductName();
数据库版本号:dbmd.getDatabaseProductVersion();
数据库驱动名:dbmd.getDriverName();
数据库驱动版本号:dbmd.getDriverVersion();
数据库Url:dbmd.getURL();
该连接的登陆名:dbmd.getUserName();
5、 Batch updates 可批量更新。
将多组对数据库的更新操作发送到数据库统一执行(数据库支持并发执行操作),以提高效率。
主要是通过减少数据(Sql语句或参数)在网络上传输的次数来节省时间。//数据有两组以上都应该用这批量更新
四、JDBC 中使用Transaction编程(事务编程)
1. 事务是具备以下特征(ACID)的工作单元:
原子性(Atomicity)—— 如果因故障而中断,则所有结果均被撤消;
一致性(Consistency)—— 事务的结果保留不变;
隔离性(Isolation)—— 中间状态对其它事务是不可见的;
持久性(Durability)—— 已完成的事务结果上持久的。
原子操作,也就是不可分割的操作,必须一起成功一起失败。
2. 事务处理三步曲:(事务是一个边界)
① connection.setAutoCommit(false); //把自动提交关闭;在创建Statement对象之前。
② 正常的DB操作 //若有一条SQL语句失败了,自动回滚
③ connection.commit() //主动提交
和 connection.rollback() //主动回滚,一般写在catch语句里,而前三个都写在try语句里
3.JDBC事务并发产生的问题和事务隔离级别(难,理解)
JDBC事务并发产生的问题:
① 脏读(Dirty Reads) 一个事务读取了另一个并行事务还未提交的数据。(产生原因:读-写)
② 不可重复读(UnPrpeatable Read)一个事务前后两次读取数据时,得到的数据不一致,被另一个已提交的事务修改。
③ 幻读(Phantom Read) 一个事务再次查询,记录中的量变化了。(仅对统计有影响)
为了避免以上三种情况的出现,则采用事务隔离级别:
TRANSACTION_NONE 不使用事务(不可能用,只是理论的)
TRANSACTION_READ_UNCOMMITTED 可以读取未提交数据(允许脏读,也不可能)
TRANSACTION_READ_COMMITTED 只读提交的数据:可防止脏读;大部分数据库的默认隔离级别
TRANSACTION_REPEATABLE_READ 重复读取;只可以避免脏读
TRANSACTION_SERIALIZABLE 事务串行化:可以避免脏读,重复读取和幻读,但会降低数据库效率(最常用)
以上的五个事务隔离级别都是在Connection类中定义的静态常量。隔离级别越高,数据越安全,并发能力越差。
使用setTransactionIsolation(int level) 方法可以设置事务隔离级别。
五、JDBC应用的分层(DAO)
分层就是对功能的隔离,降低层与层之间的耦合性。
软件的分层初步:
View(界面) --> Controlle --> Service/Biz --> DAO ----> DB
软件的分层设计,便于任务的划分、降低层间的耦合。
BS架构和CS架构:
C-S架构:两层体系结构,主要应用于局域网中。
B-S架构:三层体系结构,表现层+业务逻辑层+数据存储层
注:层面越多,软件越复杂,但更灵活。分层是必须的但是要有个度。
层次一但确定,数据必须按层访问,不能跨层访问。
层与层之间最好时单向依赖(单向调用)。
纵向划分:按功能划分。分成三层体系结构(也有两层的)。
横向划分:按抽象划分。分成抽象部分和实现部分。
六、 面向对象的数据库设计
类的关联,继承在数据库中的体现:
类定义―――>表定义
类属性―――>表字段
类关系―――>表关系
对 象―――>表记录
(一)类继承关系对应表,
1、 为每一个类建一张表。通过父类的id来体现继承关系。
特点:在子类表中引用父类表的主建作为自己的外建。
优点:方便查询。属性没有冗余。支持多态。
缺点:表多,读写效率低。生成报表比较麻烦。
2、 为每一个具体实现类建一个表
特点:父类的属性被分配到每一个子类表中。
优点:报表比较容易
缺点:如果父类发生改变会引起所有子类表随之更改。并且不支持多态。数据有少量冗余。
3、 所有的类在一张表中体现,加一个类型辨别字段
特点:效率高,查询不方便,用于字段不多时。
优点:支持多态,生成报表很简单。
缺点:如果任何一个类发生变化,必须改表。字段多,难以维护。
(二)类关联关系对应表
1、 一对一关联,类关系对应成表时有两种做法:
一是引用主键,也就是一方引用另一方的主键既作为外键有作为自身的主键。
二是外键引用,一方引用另一方的主键作为自身的外键,并且自己拥有主键。
2、 一对多关联,也就是多端引用一端的主键当作外键,多端自身拥有主键。
3、 多对多关系,多对多关系是通过中间表来实现的,中间表引用两表的主键当作联合主键,就可以实现多对多关联。
JDBC从物理结构上说就是Java语言访问数据库的一套接口集合。
从本质上来说就是调用者(程序员)和实现者(数据库厂商)之间的协议。
JDBC API 使得开发人员可以使用纯Java的方式来连接数据库,并进行操作。
ODBC:基于C语言的数据库访问接口。
JDBC:是Java版的ODBC。
JDBC 特性:高度的一致性、简单性(常用的接口只有4、5个)。
二、JDBC 编程的步骤
import java.sql.*;
0.参数化
String driverName = "com.mysql.jdbc.Driver";
String url = "jdbc:mysql://localhost:3306/test"; //协议;库或服务器名称;服务器IP,端口
String username = "root";
String password="";
/* Oracle的连接
String driverName = "oracle.jdbc.driver.OracleDriver";
String url = "jdbc:oracle:thin:@192.168.0.23:1521:ora10g";
String username = "openlab";
String password="open123";*/
//以下这些都需要写在有异常的代码块里,所以需要提取出来。
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;//建议用PreparedStatement
1.加载和注册数据库驱动
Class.forName(driverName);
//需要处理异常
2.连接数据库
Connection conn = DriverManager.getConnection(url,username,password);
3.创建Statement对象 //为了类型安全和批量更新的效率,改用PreparedStatement
PreparedStatement ps = conn.prepareStatement("sql");
4.操作数据库,执行SQL语句
ps.executeQuery();//select
返回查询结果对象ResultSet ps.executeUpdate();//insert/delete/update
返回int,几行记录收到影响
ps.execute();//other
返回boolean,成功失败
5.处理查询数据(游标)
ResultSet rs = ps.executeQuery();
while(rs.next()){
int pid = rs.getInt("字段名或字段索引");
String pname = rs.getString("pname");
int age = rs.getInt("age");
String sex = rs.getString("sex");
System.out.println(pid+" "+pname+" "+age+" "+sex);
}
6.释放资源,断开与数据库的连接
rs.close();
ps.close();
conn.close();
因为加载驱动是个一次性工作,所以可以采用静态初始化块来加载驱动;
释放数据库资源的方法要考虑到ResultSet、Statement、Connection的不同情况,并处理相关异常。
三、JDBC几个重要接口重点讲解
在JDBC 中包括了两个包:java.sql和javax.sql。
① java.sql 基本功能。
这个包中的类和接口主要针对基本的数据库编程服务,如生成连接、执行语句以及准备语句和运行批处理查询等。
同时也有一些高级的处理,比如批处理更新、事务隔离和可滚动结果集等。
② javax.sql 扩展功能。
它主要为数据库方面的高级操作提供了接口和类。
如为连接管理、分布式事务和旧有的连接提供了更好的抽象,它引入了容器管理的连接池、分布式事务和行集等。
API 说明
Connection 与特定数据库的连接(会话)。能够通过getMetaData方法获得数据库提供的信息、所支持的SQL语法、存储过程和此连接的功能等信息。代表了数据库。
Driver 每个驱动程序类必需实现的接口,每个数据库驱动程序也都应该提供一个实现Driver接口的类。
DriverManager(Class) 管理一组JDBC驱动程序的基本服务。作为初始化的一部分,此接口会尝试加载在”jdbc.drivers”系统属性中引用的驱动程序。只是一个辅助类,是工具。
Statement 用于执行静态SQL语句并返回其生成结果的对象。
PreparedStatement 继承Statement接口,表示预编译的SQL语句的对象,SQL语句被预编译并且存储在PreparedStatement对象中。然后可以使用此对象高效地多次执行该语句。
CallableStatement 用来访问数据库中的存储过程。它提供了一些方法来指定语句所使用的输入/输出参数。
ResultSet 指的是查询返回的数据库结果集。
ResultSetMetaData 可用于获取关于ResultSet对象中列的类型和属性信息的对象。
注:除了标出的Class,其它均为接口。每个都是“java.sql.”包下的。
1. Statement —— SQL语句执行接口
代表了一个数据库的状态,在向数据库发送相应的SQL语句时,都需要创建Statement接口或PreparedStatement接口。
在具体应用中,Statement主要用于操作不带参数(可以直接运行)的SQL语句,比如删除语句、添加或更新。
2. PreparedStatement:预编译的Statement
第一步:通过连接获得PreparedStatement对象,用带占位符(?)的sql语句构造。
PreparedStatement pstm = con.preparedStatement(“select * from test where id=?”);
第二步:设置参数
pstm.setString(1,“ganbin”);//第一个字段是“ganbin”;需一个个字段写
第三步:执行sql语句
Rs = pstm.excuteQuery();
statement发送完整的Sql语句到数据库不是直接执行而是由数据库先编译,再运行。每次都需要编译。
而PreparedStatement是先发送带参数的Sql语句,由数据库先编译,再发送一组组参数值。(同构时不需重复编译)
如果是同构的sql语句,PreparedStatement的效率要比statement高。而对于异构的sql则两者效率差不多。
一般都用PreparedStatement代替Statement,因为它是类型安全的。Statement对参数类型不作检查,故不够安全。
同构:两个Sql语句可编译部分是相同的,只有参数值不同。
异构:整个sql语句的格式是不同的
注意点:1、使用预编译的Statement编译多条Sql语句一次执行
2、可以跨数据库使用,编写通用程序
3、能用预编译时尽量用预编译
4、如果第二个SQL语句与前一个是异构的,需要再次编译“ps = con.prepareStatement(sql);“
3. ResultSet —— 结果集操作接口
ResultSet接口是查询结果集接口,它对返回的结果集进行处理。ResultSet是程序员进行JDBC操作的必需接口。
4. ResultSetMetaData —— 元数据操作接口
ResultSetMetaData是对元数据进行操作的接口,可以实现很多高级功能。
Hibernate运行数据库的操作,大部分都是通过此接口。可以认为,此接口是SQL查询语言的一种反射机制。
ResultSetMetaData接口可以通过数组的形式,遍历数据库的各个字段的属性,对于开发者来说,此机制的意义重大。
JDBC通过元数据(MetaData)来获得具体的表的相关信息,例如,可以查询数据库中有哪些表,表有哪些字段,以及字段的属性等。MetaData中通过一系列getXXX将这些信息返回给我们。
数据库元数据 Database MetaData 用connection.getMetaData()获得;包含了关于数据库整体元数据信息。
结果集元数据 ResultSet MetaData 用resultSet.getMetaData()获得;比较重要的是获得表的列名,列数等信息。
结果集元数据对象:ResultSetMetaData meta = rs.getMetaData();
字段个数:meta.getColomnCount();
字段名字:meta.getColumnName();
字段JDBC类型:meta.getColumnType();
字段数据库类型:meta.getColumnTypeName();
数据库元数据对象:DatabaseMetaData dbmd = con.getMetaData();
数据库名:dbmd.getDatabaseProductName();
数据库版本号:dbmd.getDatabaseProductVersion();
数据库驱动名:dbmd.getDriverName();
数据库驱动版本号:dbmd.getDriverVersion();
数据库Url:dbmd.getURL();
该连接的登陆名:dbmd.getUserName();
5、 Batch updates 可批量更新。
将多组对数据库的更新操作发送到数据库统一执行(数据库支持并发执行操作),以提高效率。
主要是通过减少数据(Sql语句或参数)在网络上传输的次数来节省时间。//数据有两组以上都应该用这批量更新
四、JDBC 中使用Transaction编程(事务编程)
1. 事务是具备以下特征(ACID)的工作单元:
原子性(Atomicity)—— 如果因故障而中断,则所有结果均被撤消;
一致性(Consistency)—— 事务的结果保留不变;
隔离性(Isolation)—— 中间状态对其它事务是不可见的;
持久性(Durability)—— 已完成的事务结果上持久的。
原子操作,也就是不可分割的操作,必须一起成功一起失败。
2. 事务处理三步曲:(事务是一个边界)
① connection.setAutoCommit(false); //把自动提交关闭;在创建Statement对象之前。
② 正常的DB操作 //若有一条SQL语句失败了,自动回滚
③ connection.commit() //主动提交
和 connection.rollback() //主动回滚,一般写在catch语句里,而前三个都写在try语句里
3.JDBC事务并发产生的问题和事务隔离级别(难,理解)
JDBC事务并发产生的问题:
① 脏读(Dirty Reads) 一个事务读取了另一个并行事务还未提交的数据。(产生原因:读-写)
② 不可重复读(UnPrpeatable Read)一个事务前后两次读取数据时,得到的数据不一致,被另一个已提交的事务修改。
③ 幻读(Phantom Read) 一个事务再次查询,记录中的量变化了。(仅对统计有影响)
为了避免以上三种情况的出现,则采用事务隔离级别:
TRANSACTION_NONE 不使用事务(不可能用,只是理论的)
TRANSACTION_READ_UNCOMMITTED 可以读取未提交数据(允许脏读,也不可能)
TRANSACTION_READ_COMMITTED 只读提交的数据:可防止脏读;大部分数据库的默认隔离级别
TRANSACTION_REPEATABLE_READ 重复读取;只可以避免脏读
TRANSACTION_SERIALIZABLE 事务串行化:可以避免脏读,重复读取和幻读,但会降低数据库效率(最常用)
以上的五个事务隔离级别都是在Connection类中定义的静态常量。隔离级别越高,数据越安全,并发能力越差。
使用setTransactionIsolation(int level) 方法可以设置事务隔离级别。
五、JDBC应用的分层(DAO)
分层就是对功能的隔离,降低层与层之间的耦合性。
软件的分层初步:
View(界面) --> Controlle --> Service/Biz --> DAO ----> DB
软件的分层设计,便于任务的划分、降低层间的耦合。
BS架构和CS架构:
C-S架构:两层体系结构,主要应用于局域网中。
B-S架构:三层体系结构,表现层+业务逻辑层+数据存储层
注:层面越多,软件越复杂,但更灵活。分层是必须的但是要有个度。
层次一但确定,数据必须按层访问,不能跨层访问。
层与层之间最好时单向依赖(单向调用)。
纵向划分:按功能划分。分成三层体系结构(也有两层的)。
横向划分:按抽象划分。分成抽象部分和实现部分。
六、 面向对象的数据库设计
类的关联,继承在数据库中的体现:
类定义―――>表定义
类属性―――>表字段
类关系―――>表关系
对 象―――>表记录
(一)类继承关系对应表,
1、 为每一个类建一张表。通过父类的id来体现继承关系。
特点:在子类表中引用父类表的主建作为自己的外建。
优点:方便查询。属性没有冗余。支持多态。
缺点:表多,读写效率低。生成报表比较麻烦。
2、 为每一个具体实现类建一个表
特点:父类的属性被分配到每一个子类表中。
优点:报表比较容易
缺点:如果父类发生改变会引起所有子类表随之更改。并且不支持多态。数据有少量冗余。
3、 所有的类在一张表中体现,加一个类型辨别字段
特点:效率高,查询不方便,用于字段不多时。
优点:支持多态,生成报表很简单。
缺点:如果任何一个类发生变化,必须改表。字段多,难以维护。
(二)类关联关系对应表
1、 一对一关联,类关系对应成表时有两种做法:
一是引用主键,也就是一方引用另一方的主键既作为外键有作为自身的主键。
二是外键引用,一方引用另一方的主键作为自身的外键,并且自己拥有主键。
2、 一对多关联,也就是多端引用一端的主键当作外键,多端自身拥有主键。
3、 多对多关系,多对多关系是通过中间表来实现的,中间表引用两表的主键当作联合主键,就可以实现多对多关联。