JDBC详解

openkk 13年前

JDBC原理概述

 

1,JDBC是一套协议,是JAVA开发人员和数据库厂商达成的协议,也就是由Sun定义一组接口,由数据库厂商来实现,并规定了JAVA开发人员访问数据库所使用的方法的掉用规范。

 

2,JDBC的实现是由数据库厂商提供,以驱动程序形式提供。

 

3,JDBC在使用前要先加载驱动。

JDBC对于使用者要有一致性,对不同的数据库其使用方法都是相同的。

 

driver开发必须要实现Driver接口。

数据库驱动的实现方式

JDBC-ODBC桥接式

JDBC网络驱动,这种方式是通过中间服务器的协议转换来实现的

JDBC+本地驱动,这种方式的安全性比较差

JDBC驱动,由数据库厂商实现。

 

JDBC的API

 

java.sql包和javax.sql包

DriverManager类(驱动管理器),它可以创建连接,它本身就是一个创建Connection的工厂(Factory)。

Connection接口,会根据不同的驱动产生不同的连接

Statement接口,发送sql语句

ResultSet接口(结果集),是用来接收select语句返回的查寻结果的。其实质类似于集合。

 

JDBC应用步骤

1,注册加载一个driver驱动

2,创建数据库连接(Connection)

3,创建一个Statement(发送sql)

4,执行sql语句

5,处理sql结果(select语句)

6,关闭Statement

7,关闭连接Connection。

 

注意:6,7两个步骤势必须要做的,因为这些资源是不会自动释放的,必须要自己关闭

 

访问Oracle的数据库的驱动名字叫ojdbc14.jar,这个jar文件中出访的驱动程序的.class文件

要使用这个驱动程序,要先将他加到环境变量PATH中。

 

一,注册加载驱动driver,也就是强制类加载

   Class.forName(Driver包名.Driver类名)。

 

   Driver d=new Driver类();

   DriverManager.registerDriver(d);

 

   Oracle的Driver的全名oracle.jdbc.driver.OracleDriver

   mysql的Driver的全名com.mysql.jdbc.Driver

   SQLServer的Driver的全名com.microsoft.jdbc.sqlserver.SQLServerDriver

 

二,创建连接

   DriverManager.getConnection(String url,String username,String password);

   Connection连接是通过DriverManager的静态方法getConnection(.....)来得到的,这个方法的实质是把参数传到实际的Driver中的connect()方法中来获得数据库连接的。

   Oracle的URL值是由连接数据库的协议和数据库的IP地址及端口号还有要连接的库名(DatebaseName)

   Oracle URL的格式

   jdbc:oracle:thin:(协议)@XXX.XXX.X.XXX:XXXX(IP地址及端口号):XXXXXXX(所使用的库名)

    例:jdbc:oracle:thin:@192.168.0.39:1521:TARENADB

   MySql URL的写法

    例:jdbc:mysql://192.168.8.21:3306/test

   SQLServer URL的写法

    例:jdbc:microsoft:sqlserver://192.168.8.21:1433

 

   java -Djdbc.drivers=驱动全名 类名

 

    使用系统属性名,加载驱动 -D表示为系统属性赋值

   

    使用Connection对象获得一个Statement,Statement中的executeQuery(String sql) 方法可以使用select语句查询,并且返回一个结果集 ResultSet通过遍历这个结果集,可以获得select语句的查寻结果,ResultSet的next()方法会操作一个游标从第一条记录的前边开始读取,直到最后一条记录。executeUpdate(Stringsql) 方法用于执行DDL和DML语句,可以update,delete操作。

注意:要按先ResultSet结果集,后Statement,最后 Connection的顺序关闭资源,因为Statement和ResultSet是需要连接是才可以使用的,所以在使用结束之后有可能起他的 Statement还需要连接,所以不能现关闭Connection。

 

 

 

预编译的Statement

 

PreparedStatement 可以使用参数替代sql语句中的某些参数使用 "?"代替,他先将带参数的sql语句发送到数据库,进行编译,然后PreparedStatement会将参数发送给数据库。

在使用PreparedStatement时,在设置相应参数时,要指明参数的位置和类型,以及给出参数值

根据不同的参数类型使用不同的setXXX(参数的位置,参数值)来设置参数

 

例:

public void insert(Student s){

              Connectioncon=ConnectionFactory.getConnection();//建立连接

              Stringsql="insert into student(id,name) values(?,?)";

              PreparedStatementps=null;

              try{

                     ps=con.prepareStatement(sql);//创建一个PreparedStatement

                        int index=1;

                     ps.setInt(index++,s.getStuId());

                     ps.setString(index++,s.getName());

                     ps.executeUpdate();

              }catch (SQLException e) {

                     e.printStackTrace();

              }finally{

                     if(ps!=null)

                            try{

                                   ps.close();

                            }catch (SQLException e) {

                                   e.printStackTrace();

                            }

                            if(con!=null)

                                   try{

                                          con.close();

                                   } catch (SQLException e) {

                                          e.printStackTrace();

                                   }

              }

       }

 

CallableStatement是可以用非sql语句来访问数据库,他是通过调用存储过程(PL/SQL)来访问数据库的。可以直接使用连接来调用prepareCall(...)方法,来执行这个存储过程,"..."是存储过程的名字。

 

 

SQLException是检查异常必须处理要么throws ,要么try{}catch(){}

getErrorCode()可以获得错误码,可以对错误进行查询。

 

源数据

 

JDBC中有两种源数据,一种是数据库源数据,另一种是ResultSet源数据。

 

源数据就是描述存储用户数据的容器的数据结构。

 

ResultSet rs=ps.executeQuery(sql);

ResultSetMetaData m=rs.getMetaData();

 

getColumnCount(),获得实际列数

getColumnName(int colnum),获得指定列的列名

getColumnType(int colnum),获得指定列的数据类型

getColumnTypeName(int colnum),获得指定列的数据类型名

 

数据库源数据

 

DatabaseMetaData

getURL(),获得连接数据库的URL

getDatabaseProductName() 获得数据库产品的名称

getDriverVersion() 获得JDBC驱动程序的String形式的版本号

getTables()获得数据库中该用户的所有表

getUserName() 获得数据库用户名。

 

事务(Transaction)

事务是针对原子操作的,要求原子操作不可再分,要求原子操作必须同时成功同时失败。

事务是捆绑的原子操作的边界。

JDBC中使用事务,先要使用连接调用setAutoCommite(false)方法,把自动提交(commit)置为false。打开事务就要关闭自动提交。不用事务是要把setAutoCommite(true)

 

在处理事务时,在发送sql语句后执行成功并确认时,就在try块中使用连接调用commit()方法来发送提交信息,在发送sql语句后执行失败时,会在catch语句块中使用连接调用rollback()方法来发送回滚信息,也可以在需要时做回滚操作(主观原因)。

 

 

 

JDBC事务并发产生的问题和事务隔离级别

 

1,脏读(dirty read),读取到了没有提交的数据。

2,不可重复读(UnPrpeatable Read),两次读取到了不同的数据,就是要保持在同一时间点上两次读取到的数据相同,不能够使查询数据时进行改变。

3,幻读(phantom),在两次查询同一时间点数据时,数据数量发生改变,要保持在同一时间点上两次读取到的数据相同。

 

事务隔离级别

 

TRANSACTION_NONE不使用事务。

TRANSACTION_READ_UNCOMMITTED 可以读取为提交数据。

TRANSACTION_READ_COMMITTED可以避免脏读,不能够读取没提交的数据,最常用的隔离级别  大部分数据库的默认隔离级别

TRANSACTION_REPEATABLE_READ可以避免脏读,重复读取,

TRANSACTION_SERIALIZABLE可以避免脏读,重复读取和幻读,(事务串行化)会降低数据库效率

 

以上的五个事务隔离级别都是在Connection类中定义的静态常量,使用setTransactionIsolation(intlevel) 方法可以设置事务隔离级别。

 

JDBC2.0新特性

 

可滚动结果集(可双向滚动),这种结果集不但可以双向滚动,相对定位,绝对定位,并且可以修改数据信息。

 

滚动特性

next(),此方法是使游标向下一条记录移动。

previous() ,此方法可以使游标上一条记录移动,前提前面还有记录。

 

absolute(int row),可以使用此方法跳到指定的记录位置。定位成功返回true,不成功返回false,返回值为false,则游标不会移动。

 

afterLast() ,游标跳到最后一条记录之后,(结果集一回来时就有的位置)。

beforeFirst() ,游标跳到第一条记录 之前,(结果集一回来时就有的位置)。(跳到游标初始位)

 

first(),游标指向第一条记录。

last(),有彪指向最后一条记录。

 

relative(int rows) ,相对定位方法,参数值可正可负,参数为正,游标从当前位置向下移动指定值,参数为负,游标从当前位置向上移动指定值。

 

TYPE_FORWARD_ONLY ,单向,该常量指示指针只能向前移动的 ResultSet 对象的类型。不可滚动。

 

TYPE_SCROLL_INSENSITIVE ,双向,该常量指示可滚动但通常不受其他的更改影响的 ResultSet 对象的类型。

TYPE_SCROLL_SENSITIVE ,双向,该常量指示可滚动并且通常受其他的更改影响的 ResultSet 对象的类型。该特性某些数据库不支持。

         

要使用可滚动结果集时,要在Statement创建时指定参数,才可以使用

Statement st=null;

(int,int)(可滚动特性,可更新特性)

st=con.createStatement(ReusltSet.TYPE_SCROLL_INSENSITIVE,ResuleSet.CONCUR_UPDATABLE)

 

ResultSet结果集中,先使用moveToInsertRow(),将游标移到和结果集结构类似的缓冲区中

然后可以使用updateXxx(intcolumn,columnType value)方法来更新指定列数据,再使用insertRow() 方法插入记录,最后将游标指回原位,moveToCurrentRow() 。

 

能否使用可更新结果集,要看使用的数据库驱动是否支持,还有只能用于单表且表中有主键字段(可能会是联合主键),不能够有表连接,会取所有非空字段且没有默认值。结果集用select * from t也不行,不能用*,不能排序

 

能否使用JDBC2.0ResultSet的新特性要看数据库驱动程序是否支持。

 

批处理更新

Statement.

addBatch(String sql), 方法会在批处理缓存中加入一条sql语句

executeBatch() ,执行批处理缓存中的所有sql语句。

 

PreparedStatement.   先准备一组参数

addBatch() 将一组参数添加到此 PreparedStatement 对象的批处理命令中。

executeBatch() 将一批命令提交给数据库来执行,如果全部命令执行成功,则返回更新计数组成的数组。

PreparedStatement中使用批量更新时,要先设置好参数后使用addBatch()方法加入缓存。

注意:批量更新中只能使用更新或插入语句

 

execute(String sql),这个方法的返回值是boolean类型,如果返回true就表示sql是一个select语句,可以通过getResultSet()获得结果集,如果是false,sql就是DML语句或者是DDL语句。

 

 

SQL3.0中的行类型

 

Array,数组

Sturct,结构类似给该列类型起个描述性的别名

Blob,大的二进制数据文件       create table t_blob(idnumber(12) primary key,filename varchar(20),blobData blob);

                             ps=con.prepareStatement("insert intot_blob" +

                                          "values(?,?,empty_blob())");。

Clob,大文本文件对象。

在使用上述大对象的时候,在使用JDBC插入记录时要先插入一个空的占位对象,然后使用

select blobdata from t_blob where id =" + id + " for update 这样的语法来对获得的大对象,进行实际的写入操作 Blod通过getBinaryOutputStream()方法获取流进行写入。getBinaryStream()方法获得流来获取blob中存储的数据。

clob的操作也和blob相同。getAsciiStream()方法用于读取存储的文本对象,getAsciiOutputStream()方法之获得流用来向文件对象写入的。

 

 

JDBC2.0扩展

 

JNDI和DataSourse

 

JNDI,(命名路径服务)也用于存储数据,但是他所存储的是一写零散的信息。

JNDI的方法是在javax.naming包下

 

bind(String name, Object obj) 将名称绑定到对象资源,建立指定的字符串和对象资源的关联

lookup(String name) ,通过指定的字符串获得先前绑定的资源

以下是将资源和JNDI命名绑定的方法

 public static void bind(String context, Object obj) throwsNamingException

    {

        Properties pro = new Properties();

       //Weblogic的JNDI服务器参数

       pro.put(InitialContext.INITIAL_CONTEXT_FACTORY,"weblogic.jndi.WLInitialContextFactory");

       pro.put(InitialContext.PROVIDER_URL, "t3://localhost:7001");

      

       Context ctx = new InitialContext(pro);

       ctx.bind(context, obj);//建立指定的字符串和对象资源的关联

    }

 

 

DataSourse(数据源),包含了连接数据库所需的信息,可以通过数据源或的数据库连接,有时由于某些连接数据库的信息会变更,所以经常使用包含数据库连接信息的数据源。

 

通过JNDI获得绑定的资源

 public static Object lookup(String context)throws NamingException

    {

        Properties pro = new Properties();

       //Weblogic的JNDI服务器参数

       pro.put(InitialContext.INITIAL_CONTEXT_FACTORY,"weblogic.jndi.WLInitialContextFactory");

       pro.put(InitialContext.PROVIDER_URL, "t3://localhost:7001");

      

       Context ctx = new InitialContext(pro);

       return ctx.lookup(context);//通过指定的字符串获得先前绑定的资源。

    }

 

连接池,保持连接池中有指定个数的连接,并在程序使用过之后不关闭连接,再放回连接池中等待其他的程序在需要时来取用,这样可以大量的节省销毁和创建连接的资源消耗。

 

JTA分布式的事务

 

分布式事务是针对多个不同数据库同时操作,要保证原子操作的不可分,也不用再自己写commit,和rollback,全部都交给中间服务器来处理。(两阶段提交),也就是在中间服务器发送sql语句等待数据库回应,都回应操作成功才提交,否则同时回滚。

 

 

RowSet

 

行集,这是一个JavaBean(事件机制),它增强了ResultSet的功能,通过RowSet可以获得数据源,设置隔离级别,也可以发送查寻语句,也实现了离线的操作遍历,RowSet也支持预编译的Statement。

RowSet中的方法大致上和ResultSet相同,当需要使用时请查阅JAVA API参考文档。

 

面向对象的数据库设计

 

Id通常是用来表示记录的唯一性的,通常会使用业务无关的数字类型

Object id 对象的id,sequence只有Oracle才可用,对象id(OID)使用高低位算法先生成高位,在生成低位,通过运算获得对象id。

 

类应当对象到表,属性对应字段,对象对应记录。

 

类继承关系对应表,

 

1,每个类建一个表,为父子类每个类都对应的创建表,这种方法类关系清晰,但是如果类比较多就不适合了

 

2,只有具体类才建表,也就是把父类中的属性均匀分配到子类的表中,也就是父类不建表,这种表关系不能使用多态

 

3,所有类对应一张表,这种方法是在标中加上一个字段来区分父子类,但是只能用于类属性较少的情况下,而且数据会有冗余。

 

类关联关系对应表

 

1,一对一关联,类关系对应成表时有两种做法,一是引用主键,也就是一方引用另一方的主键既作为外键有作为自身的主键。二是外键引用,一方引用另一方的主键作为自身的外键,并且自己拥有主键。

 

2,一对多关联,也就是多端引用一端的主键当作外键,多端自身拥有主键。

 

3,多对多关系,多对多关系是通过中间表来实现的,中间表引用两表的主键当作联合主键,就可以实现多对多关联。

 

JDCB应用的分层

 

分层就是对功能的隔离,降低层与层间的耦合性。