分享

JDBC 学习笔记

 沧海一声笑! 2009-05-13
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//驱动管理器类
 2class 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内容一样参数不同。
 1select id,first_name from s_emp
 2where dept_id = ? and name like ?;
 3
 4insert into t_user values(?,?,?,?);
 5-- 上面的这些情况就可以用setXXX的方法(绑定参数)来传参数。
 6-- 例:
 7Connection con = ConnectionFactory.getConnection();
 8String sql = "XXXXXXXXXXXXXXXXXXXXXXXXXX";
 9PreparedStatement pstm = con.PreparedStatement(sql);
10//绑定参数
11pstm.setInt(1,XXX);
12pstm.setString(2,XXX);
13. . . 
3.CallableStatement用来调用存储过程[procedure]
1String sql = "{call insert_user(?,?)}"
2CallableStatement cstm = con.prepareCall(sql);
3//绑定参数
4cstm.setString(1,"user");
5cstm.setString(2,"password");
6//执行过程,此返回值表示受影响的记录条数
7int res = cstm.executeUpdate();
8
4.执行SQL
 1statement:
 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.处理结果集
1ResultSet rs:
2    rs.next();   =>  boolean用于判断结果集中是否还有可读取的元素。
3     rs.getXXX();  get系列方法,用于读去结果集中的元素。
6.释放资源
1Connection.close();
2Statement.close();
3ResultSet.close();

 

[JDBC Advanced Topics]异常处理、元数据以及事务。
1、异常 Exception
    java.sql.SQLException          对底层数据库异常的封装,它是一个已检查异常。所以应用程序必须对它做处理。
        .getErrorCode();                获取特定于数据库供应商的错误代码。
    java.sql.SQLWaring extends SQLExecption         提供关于数据库访问警告信息的异常。

例:
1try{
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     同一个事务在两个不同的时刻读取不同结果
如:
1T1:select score from t_score where id= 48;
2T2:update t_score set score = score+10 where id = 48;
3T1:select score from t_score where id= 48;
解决重复读问题:
让T1在查询语句中加入for update子句,从而把选定的记录加上排他锁;那么T1事务完成之前,其他事务是不能来获取锁资源的。
3、幻影读            Phanton reads          记录数量变化,而不是行记录的列值
1T1:elcet count(*from t_class where classname = '******';
2T2:nsert into t_class values(.);
3    commit();
4T1: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);
这个也是默认事务隔离级别。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多