JDBC:
|-- 数据库互联。
|-- 由SUN公司所制定的用来访问数据库的规范。
|-- 数据库数据 <= 交互 => 应用程序。
JDBC包含主要两个部分:
|-- part 1.由SUN公司制定的规范接口
|-- java.sql --> 核心包
|-- javax.sql --> 扩展包
|-- part 2.由数据库厂商所提供的数据库驱动程序,它们实现了part 1部分。
JDBC的驱动程序包含以下四种类型:
|-- 第一代驱动(JDBC - ODBC Bridge)
|-- 首先把JDBC的调用转换成ODBC的调用,再用ODBC与具体的数据库系统通信。
|-- 第二代驱动 (Native - API party - Java driver 本地API部分Java驱动)
|-- Conrerts JDBC calls into calls on the native client API of a specific RODMS.
|-- 缺点:不同的数据库需要安装不同的客户端(每台电脑都要装)。
|-- 第三代驱动 (JDBC - net pure Java driver 网络纯Java驱动)
|-- 提供网络节点,通过网络节点访问数据库。
|-- Translates JDBC calls into a DBMS independent net protocol, which is then translated
to a DBMS protocol by a server.
|-- 第四代驱动 (Native protocol pure Java driver 本地协议纯Java驱动)
|-- Conrerts JDBC calls directly into the network protocol used by DBMS.
JDBC核心API
java.sql
|-- Driver [Interface] 所有驱动必需实现的接口
|-- DriverManager [Class] 用来管理一组数据库的驱动
|-- Connection [Interface] 与数据库的一个连接对象
|-- Statement [Interface] 用于执行一个静态的SQL语句,并返回它所生成的结果对象
|-- PreparedStatement [Interface] 表示预编译SQL语句对象(动态)
|-- CallableStatement [interface] 表示用来调用存储过程对象
|-- ResultSet [Interface] 存放查询结果集的对象
|-- Type [Class]
|-- Blob [Interface] 二进制大对象
|-- Clob [Interface] 字符大对象
|-- SQLException [Class] 数据库最底层异常(已检测异常)
|-- SQLWarning [Class] 警告
|-- DatabaseMetaData [Class] 基本数据的元数据
|-- ResultSetMetaDate [Class] 结果集的元数据
编写JDBC程序步骤
step1:注册驱动程序 [也就是把驱动加载到JVM中]
1.Class.forName("驱动类的全限定名");
如:Class.forName("oracle.jdbc.driver.OracleDriver");
Class.forName("com.mysql.jdbc.Driver");
2.使用new操作符来创建驱动类对象
如:Driver d = new oracle.jdbc.driver.OracleDriver();
//DriverManager.registerDriver(d);
3.使用系统属性-Djava.drivers来指定驱动类
如:-Djava.drivers = com.mysql.jdbc.Driver;
step2:获取数据库连接 [Connection]
如:
String url = "主协议:此协议:thin:@ip地址:端口号:数据库实例名";
String user = "XXX";
String pwd = "XXX";
Connection con = DriverManager.getConnection(url,user,pwd);
step3:创建执行的SQL的环境 [也就是创建Statement]
如:Statement stm = con.createStatement();
step4:执行SQL语句
如:String sql = "select ..........";
ResultSet rs = stm.executeQuary(sql);
step5:如果是select语句,则处理结果集 [ResultSet]
如:while(rs.next()){
rs.getXXX系列的方法();
}
step6:释放资源 [close]
如:if(con != null) con.close();
if(stm != null) stm.close();
if(rs != null) rs.close();
下面就简单的分析一下JDBC的这六个步骤:
1.获取连接Connection对象分析
a.获取数据库的三大要素
|-- URL:不同的数据库系统的URL不相同,但是至少有如下的内容:
主协议:次协议:HOST(主机):PORT(端口):SID(库名)
|-- USER:用户名
|-- PASSWORD:密码
b.DriverManager获取连接的原理:
1
//驱动管理器类
2
class DriverManager
{
3
static Vector<Driver> drivers = new Vector<Driver>();
4
public static void registerDriver(Driver d)
{
5
driver.add(d);
6
}
7
/** *//**获取连接的方法*/
8
public static Connection getConnection(String url,String user,String pwd) throws SQLException
{
9
Properties info = new Properties();
10
info.setProperty("user",user);
11
info.setProperty("password",pwd);
12
return getConnection(url,info);
13
}
14
public static Connection getConnection(String url,Properties info)throws SQLException
{
15
/**//*迭代多由的驱动,并且一次与给定的URL进行匹配,如果成功则返回当前驱动的Connection对象*/
16
Iterator<Driver> iter = drivers.iterator();
17
while(iter.hasNext())
{
18
Driver d = iter.next();
19
if(匹配(url,d))
{
20
return d.connect(url,info);
21
}else
{
22
continue;
23
}
24
}
25
/**//*到此都没有返回,说明没有匹配成功,则抛出SQLException异常*/
26
throw new SQLException("no suitable driver");
27
}
28
}
2. Statement 调用静态SQL
PreparedStatement 来执行动态SQL,可以为SQL动态绑定参数。
如:同构SQL,SQL内容一样参数不同。
1
select id,first_name from s_emp
2
where dept_id = ? and name like ?;
3
4
insert into t_user values(?,?,?,?);
5
-- 上面的这些情况就可以用setXXX的方法(绑定参数)来传参数。
6
-- 例:
7
Connection con = ConnectionFactory.getConnection();
8
String sql = "XXXXXXXXXXXXXXXXXXXXXXXXXX";
9
PreparedStatement pstm = con.PreparedStatement(sql);
10
//绑定参数
11
pstm.setInt(1,XXX);
12
pstm.setString(2,XXX);
13
. . .
3.CallableStatement用来调用存储过程[procedure]
1
String sql = "{call insert_user(?,?)}"
2
CallableStatement cstm = con.prepareCall(sql);
3
//绑定参数
4
cstm.setString(1,"user");
5
cstm.setString(2,"password");
6
//执行过程,此返回值表示受影响的记录条数
7
int res = cstm.executeUpdate();
8
4.执行SQL
1
statement:
2

3
executeQuery(selectSQL); => ResultSet
4
executeUpdate(dmlSQL); => int
5
execute(arbiSQL); => boolean
6

7
if(stm.execute(sql))
{
8
//为真,表示执行select语句
9
ResultSet rs = stm.getResultSet();
10
}else
{
11
//为假,表示执行非select语句
12
int res = stm.getUpdateCount();
13
}
5.处理结果集
1
ResultSet rs:
2
rs.next(); => boolean用于判断结果集中是否还有可读取的元素。
3
rs.getXXX(); get系列方法,用于读去结果集中的元素。
6.释放资源
1
Connection.close();
2
Statement.close();
3
ResultSet.close();
[JDBC Advanced Topics]异常处理、元数据以及事务。
1、异常 Exception
java.sql.SQLException 对底层数据库异常的封装,它是一个已检查异常。所以应用程序必须对它做处理。
.getErrorCode(); 获取特定于数据库供应商的错误代码。
java.sql.SQLWaring extends SQLExecption 提供关于数据库访问警告信息的异常。
例:
1
try
{
2
//此处省略
3
} catch(SQLException e)
{
4
while(e != null)
{
5
e.printStackTrace();
6
e = e.getNextExecption();
7
}
8
}
2、元数据:用来描述其他数据的数据
JDBC中的元数据:
ResultSetMetaDate: 结果集元数据,它用来描述结果集本身。
ResultSet.getMetaDate(); 获取当前结果集的元数据。
DatabaseMetaDate: 数据库元数据,用来描述数据库本身。
Connection.getMetaDate(); 获取当前数据库的元数据。
例:
//利用元数据打印出结果集

public void executeSQL(String sqlcommand)
{
Connection con = null;
Statement stm = null;
ResultSet rs = null;

try
{
//这里的ConnectionFactory是写的一个连接工厂,这里就不做介绍了。
con = ConnectionFactory.getConnection();
stm = con.createStatement();
boolean flag = stm.execute(sqlcommand);

if(flag)
{
rs = stm.getResultSet();
ResultSetMetaDate rsmd = rs.getMetaDate();
int columns = rsmd.getColumnCount();

for(int i=1;i<columns;i++)
{
String columnName = rsmd.getColumnLabel(i);
System.out.print(columnName+"\t");
}
System.out.println();

while(rs.next())
{

for(int i=1;i<columns;i++)
{
Object content = rs.getObject(i);
System.out.print(content+"\t");
}
System.out.print();

}else
{
int results = stm.getUpdateCount();
System.out.println(results+" resultes was update
");
}
}

}catch(SQLExecption e)
{
//此处省略若干
}
}
3、DML的操作 [JDBC的事务] 以事务为单位
JDBC事务:
定义:一组相关的操作,不可分割,一起成功,一起失败。
相关操作:
Connection:
commit(); JDBC事务提交
rollback(); JDBC事务回滚
JDBC的事务默认情况下是自动提交的,每执行一次SQL命令,就提交commit一次。[AutoCommit]
如:
转帐(){
...
update t_account1 set balance = balance-100;
update t_account2 set balance = balance+100;
...
}
如何让以上两句SQL语句,一起执行,一起失败。所以就需要把他们放在一个事务中。
如:
转帐(){
...
con.setAutoCommit(false);//关闭自动事务提交
update t_account1 set balance = balance-100;
update t_account2 set balance = balance+100;
如果以上两句都成功,则:
con.commit();//提交事务
如果以上任意一个失败,则:
con.rollback();//事务回滚
}
获取主键方式(主键值生成策略)
主键值不应该由UI界面来收集,而应该由应用程序自动生成,常见的策略如下:
a. max(id)方法 select max(id) from table_name;
此方法当有多线程并发时,就会产生问题。
b. sequence 序列
select sequence.nextval from dual;
此方法只适用支持序列的数据库产品,所以不是很通用。
c. 维护单行单列得表
1
create table id_table(next_id number not null);
2
insert into id_table next_id values(1);
3
commit;
4
--使用以下SQL自动维护
5
select next_id from id_table for update;
6
update id_table set next_id = next_id+1;
7
commit;
并发控制:多个事务同时操作同一个对象
有三种并发情况,如下:
1、脏读 Dirty reads 读到其他事务没有提交的数据。注:Oracle不支持
2、不可重复读 Repeatable reads 同一个事务在两个不同的时刻读取不同结果
如:
1
T1:select score from t_score where id= 48;
2
T2:update t_score set score = score+10 where id = 48;
3
T1:select score from t_score where id= 48;
解决重复读问题:
让T1在查询语句中加入for update子句,从而把选定的记录加上排他锁;那么T1事务完成之前,其他事务是不能来获取锁资源的。
3、幻影读 Phanton reads 记录数量变化,而不是行记录的列值
1
T1:elcet count(*) from t_class where classname = '******';
2
T2:nsert into t_class values(
.);
3
commit();
4
T1:selcet count(*) from t_class where classname = '******';
5
解决幻影读问题:
锁表:lock table table_name in mode;
事务隔离级别:SUN在JDBC规范中制定了5个级别,分别用来满足不同的并发性。
Connection:
0 .TRANSACTION_NONE 无
1 .TRANSACTION_READ_UNCOMMITED 可以脏读
2 .TRANSACTION_READ_COMMITED 不可以脏读
4 .TRANSACTION_REPEATABLE 不可以重复读
8 .TRANSACTION_SERIALIZABLE 为每个事务都开辟一个空间
事务的隔离界别设置得越高,则并发性能越差
注:不同数据库,对隔离级别的支持也是不一样的。
如:oracle就不支持0和1两种。
如何设置隔离级别?
如:con.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITED);
这个也是默认事务隔离级别。