1JDBC原理==2jdbc 连接数据库代码示例

JDBC原理

.JDBC原理概述

 

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

 

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

 

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

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

 

驱动开发必须要实现Driver接口。 

数据库驱动的实现方式 

JDBC-ODBC桥接式 

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

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

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

 

.JDBCAPI

 

java.sql包和javax.sql包 

Driver接口(驱动),在加载某一 Driver 类时,它应该创建自己的实例并向 DriverManager 注册该实例。这意味着用户可以通过调用以下程序加载和注册一个驱动程序 

Class.forName("oracle.jdbc.driver.OracleDriver") 

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

 

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

 

访问Oracle的数据库的驱动名字叫ojdbc14.jar,要使用这个驱动程序,要先将他加到环境变量CLASSPATH中。

 

注册加载驱动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:XXXXIP地址及端口号):XXXXXXX(所使用的库名) 

例:jdbc:oracle:thin:@192.168.0.20:1521:tarenadb

 

MySql URL的写法 

例: jdbc:mysql://localhost:3306/tarena 

SQLServer URL的写法 

例:jdbc:microsoft:sqlserver://localhost:1433/test

 

java -Djdbc.drivers=驱动的完整类名

 

使用虚拟机参数,加载驱动 -D表示为虚拟机参数赋值 

java -Djdbc.drivers=oracle.jdbc.driver.OracleDriver:com.mysql.jdbc.Driver 

 

.JDBC基本方法 

DriverManager:如果有多个驱动可用的话,DriverManager会根据URL选择其中一个可用的驱动

 

Driver:可以选择固定的驱动 

Driver driver = new oracle.jdbc.driver.OracleDriver(); 

String user = "sd0613"; 

String password = "sd0613"; 

Properties prop = new Properties(); 

prop.setProperty("user",user); 

prop.setProperty("password",password); 

driver.connect(url,properties); 

 

executeQuery(sqlString);//返回结果集 

executeUpdate(sqlString);//返回值为该次操作影响的记录条数,create table返回

execute(sqlString); 

//适用于不知道具体的操作是什么,返回值是boolean类型的 

//如果返回值是true,代表执行查询操作;否则代表执行更新操作

 

ResultSet 

next()方法

1.判断是否存在下一条记录 

2.将游标移向下一条记录 

getXXX(字段名或字段序号)//注意:字段序号从1开始 

 

关闭问题

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

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

一、Statement 

execute(sql); 当不知道执行的SQL语句是什么类型的时候执行 ,返回值是boolean 

executeQuery(sql); 执行查询语句 

executeUpdate(sql); 执行更新语句

 

二、PreparedStatement 

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

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

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

 

例: 

public void insert(Student s){ 

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

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

PreparedStatement ps=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(...)方法,来执行这个存储过程,"..."是存储过程的名字。

 

对于系统时间要去数据库时间 

TimeStamp 和 Date都可以保存时间 

TimeStamp可以保存时、分、秒的数据,Date只保存日期年月的信息。

 

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),获得指定列的数据类型名

 

//打印结果集 

public static void printRS(ResultSet rs)throws SQLException{ 

ResultSetMetaData rsmd = rs.getMetaData(); 

while(rs.next()){ 

for(int i = 1 ; i < = rsmd.getColumnCount() ; i++){ 

String colName = rsmd.getColumnName(i); 

String colValue = rs.getString(i); 

if(i>1){ 

System.out.print(","); 

System.out.print(name+"="+value); 

System.out.println(); 

}

 

四、数据库源数据

 

DatabaseMetaData 

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

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

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

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

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

 

五、异常的处理 

try{} 

catch(SQLException){} 

try{} 

catch(Exception){}

 

.事务(Transaction) 

原子操作:不可再分的操作,一个操作不能再分成比它更细小的操作

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

事务就是把一些非原子操作,变成原子操作,由应用服务器来提出要求,由数据库服务器来执行操作.

 

JDBC中默认是自动提交的,如果要想使用事务,需要按以下步骤执行

1.要调用con.setAutoCommite(false)方法,把自动提交(commit)置为false。 

2.进行正常的数据库操作 

3.如果操作成功了可以选择con.commit(),或者操作失败时选择con.roolback(); 

注意:打开事务就要关闭自动提交,当不需要再使用事务的时候调用setAutoCommite(true). 

 

.事务并发产生的问题 

三种并发产生的后果

1,脏读:一个事务读取到了另外一个事务没有提交的数据。 

2,重复读:一个事务读取到了另外一个事务提交的数据。它是要保持在同一时间点上读取到的数据相同,希望在一段时间内的数据是不变的。 

3,幻读:一个事务读取到了另外一个事务提交的数据。用同样的操作读取两次,得到的记录数不相同。

 

.事务隔离级别 

五种控制级别

TRANSACTION_NONE不使用事务。 

TRANSACTION_READ_UNCOMMITTED 允许脏读。 

TRANSACTION_READ_COMMITTED防止脏读,最常用的隔离级别,并且是大多数数据库的默认隔离级别 

TRANSACTION_REPEATABLE_READ可以防止脏读和不可重复读, 

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

 

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

:con.setTransactionIsolation(Connection.REPEATABLE_READ);

 

.JDBC2.0新特性 

1.可滚动特性和可更新特性 

JDBC1.0中是指游标的移动的方向和方式是单向,单步(相对)移动,功能比较简单

JDBC2.0中游标可以双向,相对或者绝对移动

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

 

1)滚动特性 

定位函数

boolean absolute(int row),定位到指定的记录位置。定位成功返回true,不成功返回false。 

void afterLast() ,把游标移动到最后一条记录的后面(逻辑位置)。 

void beforeFirst() ,把游标移动到第一条记录的前面(逻辑位置)。 

//由于第一条记录的前面和最后一条记录的后面这两个位置肯定存在,所以无需判断是否存在,返回值设为void. 

boolean first(),把游标定位到第一条记录。 

boolean last(),把游标定位到最后一条记录。 

//当结果集为空的时候,这两个方法会返回false. 

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

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

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

 

判断函数

ifBeforeFirst()判断是否在在第一条记录之前

ifAfterLast()判断是否在在最后一条记录之后

ifFirst()判断是否为第一条记录

ifLast()判断是否为最后一条记录.

 

要使用可滚动结果集时,需要一次设置更新特性与滚动特性,不能分开.

 

1.更新特性常量

CONCUR_READ_ONLY 只读结果集(默认

CONCUR_UPDATABLE 可更新结果集

 

2.滚动特性常量

TYPE_FORWARD_ONLY ,该常量表示指针只能向前移动的 ResultSet 对象的类型。(默认

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

TYPE_SCROLL_SENSITIVE ,该常量指示可滚动并且通常受其他更改影响的 ResultSet 对象的类型。 

//敏感:数据库改变,结果集改变

语法

Statement st=null; 

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

在创建Statement的时候就要指定这两个参数,使用Statement,第一个参数代表滚动特性常量,第二个代表更新特性常量

 

2)可更新特性 

a.moveToInsertRow();记录当前游标位置,将游标移到和结果集结构类似的缓冲区

b.使用updateXxx(int column,columnType value)方法来更新指定列数据

c.使用insertRow() 方法插入记录

d.将游标指回原位,moveToCurrentRow() 

 

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

还有只能用于单表且表中有主键字段(可能会是联合主键),不能够有表连接,会取 

可更新操作必须满足以下条件

a.查询只能引用一张表

b.不能包含任何连接操作

c.必须把完整的主键查到结果集里面

d.保证所有字段为非空字段并且没有默认值。

 

.数据库元数据

DatabaseMetaData dbmd = con.getMetaData();//得到数据库元数据 

dbmd.supportsResultSetConcurrency(ResultSet.TYPE_FORWARD_ONLY, 

ResultSet.CONCUR_UPDATABLE);//判断是否支持可更新操作

 

.批量更新 

优势

1.节省传递时间 

2.并发处理

 

PreparedStatement: 

1.addBatch() 将一组参数添加到 PreparedStatement对象内部 

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

 

Statement: 

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

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

 

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

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

 

.SQL3中的数据类型 

Array:数组 

Sturct:结构 

大对象

Blob:大的二进制数据文件对象。 

Clob:大的文本文件对象。 

优点

1.理论上大小没有上限,受制于数据库表空间的大小

2.流式读取.

 

使用大对象的步骤

1.先插入一个空的占位对象empty_blob()(oracle的函数):insert into t_blob values(?,?,empty_blob()); 

2.获得大对象:select blob_data from t_blob where name = ? for update; 

3.获取流进行写入:blob.setBinaryStream(0); 

4.通过流来获取blob中存储的数据:blob.getBinaryStream()

 

一、IDHigh/Low算法 

高位数字分别与低位数字相匹配,得到的数字是唯一的 

减少与数据库的交互

 

二、ORM 

1、类映射成表 

类名与表名对应 

2、属性定义映射成列,类型之间必须是兼容的 

3、类关系映射成表关系

 

一对一双向关系 

内存中都保存对方的一个引用 

数据库中,表bid是主键,也是外键,引用a表的id主键 -- share pk 

b中有一个字段aid是外键,引用a表的主键,并且有唯一约束  -- pk+fk 

共享主键: 

create table car_pk ( 

id number(10,0) not null, 

name varchar2(15), 

serial varchar2(30), 

manufacturer varchar2(50), 

producedate date, 

primary key (id) 

);

 

create table engine_pk ( 

id number(10,0) not null, 

model varchar2(20), 

manufacturer varchar2(50), 

producedate date, 

primary key (id) 

);

 

alter table engine_pk 

add constraint fk_engine_car_pk 

foreign key (id) 

references car_pk(id); 

 

外键+唯一约束 

create table car_fk ( 

id number(10,0) not null, 

name varchar2(15) not null, 

serial varchar2(30) not null, 

manufacturer varchar2(50) not null, 

producedate date, 

primary key (id) 

);

 

create table engine_fk ( 

id number(10,0) not null, 

model varchar2(20) not null, 

manufacturer varchar2(50) not null, 

producedate date, 

carid number(10,0) unique, 

primary key (id) 

);

 

alter table engine_fk 

add constraint fk_engine_car_fk 

foreign key (carid) 

references car_fk(id); 

 

实体对象:在内存中有id属性的 

值对象:没有id的,依赖其他对象存在

 

一对多关系 

一的一方保存多一方的一个集合,最好使用set,保证无重复元素 

多的一方保存一一方的一个对象的引用 

public class Order implements Serializable{ 

private int id; 

private String owner; 

private String phone; 

private String address; 

private Set<Item> items = new HashSet<Item>(); 

public class Item implements Serializable{ 

private int id; 

private String product; 

private int amount; 

private Order order; 

 

create table ec_item ( 

id number(10,0) not null, 

product varchar2(15) not null, 

amount number(10,0) not null, 

orderid number(10,0) not null, 

primary key (id) 

);

 

create table ec_order ( 

id number(10,0) not null, 

owner varchar2(15) not null, 

phone varchar2(15) not null, 

address varchar2(50), 

primary key (id) 

);

 

alter table ec_item 

add constraint fk_item_order 

foreign key (orderid) 

references ec_order(id); 

 

 

多对多 

双方都保存对方的多个引用 

例子:学生选课 

public class TarenaCourse implements Serializable{ 

private int id; 

private String name; 

private int period; 

private Set<TarenaStudent> students = new HashSet<TarenaStudent>(); 

public class TarenaStudent implements Serializable{ 

private int id; 

private String name; 

private Date birthday; 

private Set<TarenaCourse> courses = new HashSet<TarenaCourse>(); 

}

 

create table student ( 

id number(10,0) not null, 

name varchar2(15) not null, 

birthday date, 

primary key (id) 

);

 

create table student_course ( 

sid number(10,0) not null, 

cid number(10,0) not null, 

primary key (sid, cid) 

);

 

create table course ( 

id number(10,0) not null, 

name varchar2(15) not null, 

perion number(10,0), 

primary key (id) 

);

 

alter table student_course 

add constraint fk_student 

foreign key (sid) 

references student(id);

 

alter table student_course 

add constraint fk_course 

foreign key (cid) 

references course(id);

 

通过学生姓名找课程 

select c.name from cource c,student s,student_course sc 

where c.id=sc.cid and s.id=sc.sid 

and s.name = 's1' 

 

三、继承关系 

public abstract class Computer implements Serializable{ 

private int id; 

private int price; 

private String manufacturer; 

}

 

public class Desktop extends Computer{ 

private boolean isLCD; 

}

 

public class Notepad extends Computer{ 

private float weight; 

private float thickness; 

 

1、建3张表 table per class 

子类中保存父类的主键作为外键 

create table computer_tpc ( 

id number(10,0) not null, 

price number(10,0) not null, 

manufacturer varchar2(30) not null, 

primary key (id) 

);

 

create table desktop_tpc ( 

computerid number(10,0) not null, 

islcd char(1), 

primary key (computerid) 

);

 

create table notepad_tpc ( 

computerid number(10,0) not null, 

weight float, 

thickness float, 

primary key (computerid) 

);

 

alter table desktop_tpc 

add constraint fk_desk_computer_tpc 

foreign key (computerid) 

references computer_tpc(id);

 

alter table notepad_tpc 

add constraint fk_note_computer_tpc 

foreign key (computerid) 

references computer_tpc(id);

 

查找所有电脑的配制(只要是电脑就能被查出来) 

select c.id,c.price,d.islcd,n.weight,n.thickness 

from computer c, desktop d,notepad n 

where c.id = d.computerid(+) 

and c.id = n.computer(+) 

 

2、建2张表 

create table desktop ( 

id number(10,0) not null, 

price number(10,0) not null, 

manufacturer varchar2(30) not null, 

islcd char(1), 

primary key (id) 

);

 

create table notepad ( 

id number(10,0) not null, 

price number(10,0) not null, 

manufacturer varchar2(30) not null, 

weight float, 

thickness float, 

primary key (id) 

);

 

3、建1张表 

create table computer_tph ( 

id number(10,0) not null, 

category char(1) not null, 

price number(10,0) not null, 

manufacturer varchar2(30) not null, 

islcd char(1), 

weight float, 

thickness float, 

primary key (id) 

);

 

四、JDBC2.0扩展

 

1、JDBC DataSource 

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

所以经常使用包含数据库连接信息的数据源。 

 

JDBC取连接有2种方式:Driver Manager 和 数据源 

 

2、JNDIDataSourse 

主要功能:定位服务 

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

JNDI的方法是在javax.naming包下

 

InitialContext 连接,初始化上下文,这个类的提供者一般也是服务器的提供者 

查找和绑定 

查找由我们做,绑定我们并不关心,只配制数据源就好了 

 

代替DriverManager定位数据源 

遍布式企业的数据源的属性可以存储在同一个目录(JNDI)中 

以这种方式集中管理用户名、密码、数据库名和JDBC URL 

创建连接: 

Context jndiContext = new InitialContext(); 

DataSource source = (DataSource)jndiContext.lookup(" "); 

COnnection con = source.getConnection(); 

 

3、连接池 

要提供连接池数据源,带缓存的连接 

带缓存的连接,即可池化的连接,其close()方法,在物理上并没有被关闭,而是保留在一个队列中并被反复使用。 

 

4、分布式事务 

事务分为JDBC事务和JTA 

JDBC事务,由容器管理 

JTA,分布式事务,由容器管理

<!--EndFragment-->

jdbc 连接数据库代码示例

#使用JDBC操作数据库

    #1、JDBC基础

    #JDBC简介:JDBC(Java Database Connectivity)是一种可以执行SQL的Java API,通过它可以用一种API操作不同的数据库.

    #JDBC驱动:不同数据库间,标准的SQL语句可以移植,而数据库实际通信协议及某些数据库特征不可移植,因此,JDBC和数据库之间须还有一层,用于将JDBC调用映射成特定的数据库调用,此特殊层就是JDBC驱动程序.

    常见的JDBC驱动有四种:

    》JDBC-ODBC桥,是最早实现的JDBC驱动程序,目的为了快速推广JDBC,非多线程,能力有限,此驱动程序将JDBC API映射成ODBC API

    》直接将JDBC API映射成数据库特定的客户端API,这种驱动程序包含特定数据库的本地代码,可用于特定数据库的客户端

    》支持三层结构的JDBC访问方式,主要用于Applet阶段,通过Applet访问数据库

    》纯java的,直接与数据库实例交互,智能型的,知道数据库使用的底层协议,是目前最流行的JDBC驱动

    #JDBC常用接口和类简介

    DriverManager:用于管理JDBC驱动的服务类,主要方法是获得Connection对象

    public static synchronized Connection getConnection(String url,String user,String pass) throws SQLException

    Connection:代表一个数据库连接物理会话,若放我数据库,须先获得数据库连接.常用方法

    》Statement createStatement() throws SQLException 返回Statement对象

    》PreparedStatement prepareStatement(Strin sql) throws SQLException 返回编译的Statement对象

    》CallableStatement preparedCall(Strin sql) throws SQLException 返回的CallableStatement对象用于存储过程调度

    Statement:执行DML SQL的工具接口,常用方法:

    》ResultSet executeQuery(String sql) throws SQLException 执行查询,返回结果集对应的ResultSet对象

    》int executeUpdate(String sql) throws SQLException 执行DML并返回受影响的行数

    》boolean execute(String sql) throws SQLException 返回boolean表式执行成功与否

    PreparedStatement :是Statement的子接口,允许数据库预编译SQL,避免数据库每次重新编译,以后每次只改变SQL的参数,性能较好,常用方法

    》ResultSet executeQuery() throws SQLException 执行查询,返回结果集对应的ResultSet对象

    》int executeUpdate() throws SQLException 执行DML并返回受影响的行数

    》boolean execute() throws SQLException 返回boolean表式执行成功与否

    注:上述方法因SQL预编译,无须接手SQL字符串,只是需要接收参数,故有如下方法 void setXxx(int paramIndex,Xxx value)

ResultSet:包含访问结果集的方法,可通过列索引或列名获得列数据,常用方法

    》boolean next() throws SQLException,将ResultSet定位到下一行,结果集的起始位在第一行之前

》void close()throws SQLException 释放ResultSet对象

    》boolean absolute(int row)throws SQLException 将结果集移到指定行,若row是负值,则倒数移动

    注:默认方法创建的ResultSet不支持absolute方法因为结果集不支持后移,若想支持,需要如此创建

    :Statement stmt=conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);

    :ResultSet rs=stmt.executeQuery(sql);

    #传统JDBC访问数据库步骤

    》通过Class.forName(String driverClass)注册数据库驱动

    》通过DriverManager.getConnection(String url,String user,String password)获得数据库连接对象

    》通过Connnection.createStatement()或者Connection.createPreparedStatement(String sql)创建相应的Statement对象

    》通过Statement.execute(String sql)或者PreparedStatement.execute()执行相应的SQL,并返回ResultSet对象

    》操作ResultSet

    #2、数据库连接池

    》数据库连接的建立及关闭极其耗资源,对系统性能影响尤为明显.

    》传统数据库连接方式:一个数据库连接均对应一个物理连接,每次操作都要打开、关闭该物理操作,这种频繁性,会造成系统性能下降,此时,考虑数据库连接池.

    》数据库连接池解决方案:当应用程序启动时,系统主动建立足够的连接(按指定的初始化数据),并将这些连接组成一个池.每次应用程序请求数据库连接时,无需重新打开连接,而是从池中取出已有的连接,使用完后,不再关闭,而是直接将该连接归还池,使用连接池,可大大提高系统运行效率.

    》数据库连接池介绍

    》 对于共享资源的情况,有一个通用的设计模式--资源池(Resource Pool),用于解决资源的频繁请求、释放所造成的性能下降.为解决数据库连接的这种频繁性,JDBC2.0规范引入了数据库连接池技术,实际上,数据库连接池是Connection对象的工厂,常用参数有:

    @数据库的初始连接数   @连接池的最大连接数   @连接池每次增加的连接数

    》连接池的工作示意图 {JDBC标准的API并没有提供连接池的实现,仅仅提供了DataSource接口具体的实现有一些厂商提供}

    》 连接池的分配与释放

    @程序启动,分配初始化数目的连接,按需分配,用过归还、超时归还,当申请时无或者达到指定的最小值,按增量参数值分配新的连接

    @为确保连接池中最小的连接数,通常有如下策略:

    :动态--定时检查连接池,一旦发现数量小于最小连接数,则补充相应的新连接,保证连接池正常运转

    :静态--空闲连接不足时,系统才检测是否达到最小连接

    》连接池的实现

    连接池通常包括连接池类(DBConnectionPool)和连接池管理类(DBConnectionPoolManager):

    》连接池类是某一数据库所有连接的缓冲池,主要实现功能:@从连接池获取或者创建可用连接,@使用完毕,归还给池连接,@系统关闭前,断开所有连接并释放连接占用的资源,@处理无效连接,@限制池中连接的数节目,介于最小值和最大值之间

    》连接池管理类是连接池类的包装类,该类采用单态模式设计,保证系统中只有一个实例,主要用于管理多个连接池对象,主要实现以下功能:@注册数据库驱动程序,@根据配置文件,创建连接池对象,@命名、管理连接池,@跟踪连接池的使用,需要时关闭并释放资源

    :数据库连接池的管理是个难点,管理不当,造成系统开销过大,将成为性能瓶颈.对于高并发的WEB应用,采用连接池技术效率和稳定性比传统的连接方式要好的多

    :并发问题--数据库必须考虑此问题,对于并发,Java语言提供管理并发的支持,使用synchronized关键字可确保方法线程的安全.故,DataSource的getConnection方法必须以该该关键字修饰

    public synchronized Connection getConnection(); //保证线程安全

    :事务处理--JDBC的Connection本身通过设置Connection的AutoCommit属性为false,提供对事务的支持,然后,显式地调用commit或rollback方法提交或回滚事务.

    连接池需要复用connection,因此,必须提供相应的事务支持机制.考虑采用每个事务独占一个连接,此法可降低事务管理复杂性.

    :多数据库服务器和多用户--JDBC规范中,DataSource具备同时连接不同数据库的能力,如同时连oracle和sql server.此时,考虑使用xml配置文件来配置连接所需的相关信息.然后,提供一个Singleton模式的连接池管理类,该管理类每次启动时读取配置文件信息,创建多个连接池类的实例,每个实例对应一个数据库连接池.连接池管理类实例命名每个连接池实例,通过不同的名称管理不同的连接池.

    对于多个用户访问同一个数据库的情况,也可考虑使用xml配置文件.

    》常见的数据库连接池

通常,没有必要自己实现连接池.商用应用服务器都有自己的连接池实现,如WebLogic和WebSphere,其性能和稳定性绝佳,即使没有商用应用服务器,也可使用开源的连接池.目前流行的有2个:@DBCP连接池   @C3P0连接池

》DBCP连接池

    Appache提供的开源实现,依赖于两个jar文件

    @commons-dbcp-1.2.1.jar:连接池的实现

    @commons-pool.jar:连接池实现的依赖库

    Tomcat的连接池正是采用该连接池实现的.连接该连接池,既允许与应用服务器整合使用,又可由应用程序独立使用.下面的代码DBConn类通过DBCP获得数据库连接:

    public class DBConn

    {

    private static DBConn dc;

    private Connection conn = null;

    private Statement stmt = null;

    private DBConn()

    {

    }

    public static DBConn instance()

    {

    if (dc == null)

    {

    dc = new DBConn();

    }

    return dc;

    }

    public Statement openStmt()

    {

    if (stmt == null)

    {

    conn = getConn();

    try

    {

    stmt = conn.createStatement();

    }

    catch (Exception e)

    {

    System.err.println("创建Statement异常: " + e.getMessage());

    }

    }

    return stmt;

    }

    public void closeStmt()

    {

    if (stmt != null)

    {

    try

    {

    stmt.close();

    }

    catch (Exception e)

    {

    System.err.println("Statement关闭异常");

    }

    }

    if (conn != null)

    {

    try

    {

    conn.close();

    }

    catch (Exception e)

    {

    System.err.println("数据库关闭异常");

    }

    }

    }

    private Connection getConn()

    {

    if (conn == null)

    {

    try

    {

    BasicDataSource ds = new BasicDataSource();

    ds.setDriverClassName("com.mysql.jdbc.Driver");

    ds.setUrl("jdbc:mysql://localhost:3306/j2ee");

    ds.setUsername("root");

    ds.setPassword("123456");

    conn = ds.getConnection();

    }

    catch (Exception e)

    {

    e.printStackTrace();

    }

    }

    return conn;

    }

    }

    》C3P0连接池

Hibernate推荐使用该优秀的连接池,它实现了JDBC3.0规范的部分功能,故其性能更加突出,该池不仅可自动清理不再使用的Connection,还可以自动清理Statement和ResultSet.C3P0连接池需要jre1.3以上,推荐jre1.4

若需使用C3P0连接池,应将包c3p0-0.8.5.jar文件复制进系统.下面代码是通过C3P0连接池获得数据库连接:

    public class DBConn

    {

    private static DBConn dc;

    private Connection conn = null;

    private Statement stmt = null;

    private DBConn()

    {

    }

    public static DBConn instance()

    {

    if (dc == null)

    {

    dc = new DBConn();

    }

    return dc;

    }

    public Statement openStmt()

    {

    if (stmt == null)

    {

    conn = getConn();

    try

    {

    stmt = conn.createStatement();

    }

    catch (Exception e)

    {

    System.err.println("创建Statement异常: " + e.getMessage());

    }

    }

    return stmt;

    }

    public void closeStmt()

    {

    if (stmt != null)

    {

    try

    {

    stmt.close();

    }

    catch (Exception e)

    {

    System.err.println("Statement关闭异常");

    }

    }

    if (conn != null)

    {

    try

    {

    conn.close();

    }

    catch (Exception e)

    {

    System.err.println("数据库关闭异常");

    }

    }

    }

    public Connection getConn()

    {

    if (conn == null)

    {

    try

    {

    ComboPooledDataSource ds = new ComboPooledDataSource();

    ds.setDriverClass("com.mysql.jdbc.Driver");

    ds.setJdbcUrl("jdbc:mysql://localhost:3306/j2ee");

    ds.setUser("root");

    ds.setPassword("32147");

    ds.setMaxPoolSize(40);

    ds.setMinPoolSize(2);

    ds.setMaxStatements(180);

    conn = ds.getConnection();

    }

    catch (Exception e)

    {

    e.printStackTrace();

    }

    }

    return conn;

    }

    }

    #3、Spring的JDBC体系

    》Spring提供的JDBC抽象框架由core、datasource、object和support4个包组成.

    core包含Spring JDBC抽象的核心类,包含各种SQLExceptionTranslator,用于将SQLException转化成spring的异常继承体系,还有DataFileMaxValueIncrementer实现,以及JdbcTemplate,持久层访问模板类.

    datasource包含简化数据源连接的工具类,以及各种数据源的简单实现.通过这些实现,spring可以在j2ee之外测试jdbc代码.通过该工具类,可以从JNDI获得连接,并可关闭连接.

    object包里的工具类,可将数据库的查询、更新等过程封装成类.这种方式模拟JDO的访问方式,纵横四海查询返回的"值对象"不与数据库关联.support包含了JdbcDaoSupport等工具类.

    底层数据库异常被包装成org.springframework.dao中的异常,是运行时异常,故,通过JDBC抽象进行的持久操作,无需处理jdbc访问特定的异常.Spring允许将包装后的异常传播到特定的层.

    》Spring的JDBC封装核心是JdbcTemplate,简化了JDBC的使用,可以处理数据库的连接和释放,故,可避免没有数据库关闭造成的连接泄露.

    使用构造器JdbcTemplate(DataSource dataSource),可通过数据源引用创建JdbcTemplate实例,实际应用中,可以通过JdbcDaoSupport取得,JdbcDaoSupport的getJdbcTemplate()方法会创建JdbcTemplate实例,所需的数据源有JdbcDaoSupport提供.

    JdbcTemplate提供系列方法简化数据库访问,主要常用有:

    》void execute(String sql) 主要用于执行DDL语句

    》List query(String sql,Object[] args,RowMapper) 执行SQL查询,并将每条记录映射成bean实例,返回bean的实例集合

    》List queryForList(String sql,Object[] args)

    》Object queryForObject(String sql,RowMapper rowMapper)

    》int update(String sql)

    》int update(String sql,Object[] args)

    》JDBC封装的回调接口

    这些回调接口允许在spring的JDBC抽象体系内,使用原生JDBC查询,避免spring对JDBC封装后的灵活性不足缺点.Spring JDBC抽象体系内的回调接口有:

    》CallableStatementCallback:通过该接口,可使用原生的JDBC命令调用存储过程和函数

    》CallableStatementCreator:是JdbcTemplate使用的两个核心回调接口之一,通过该接口可以获得CallableStatement对象

    》PreparedStatementCallback:通过该接口,可以使用原生的JDBC命令访问数据库

    》PreparedStatementCreator:是JdbcTemplate使用的两个核心回调接口之一,通过该接口可以获得PreparedStatement对象

    》StatementCallback:作用类似PreparedStatementCallback,只是不具备预编译功能

    》Spring JDBC与传统JDBC对比

    》简化连接获取方式--无须每次采用DriverManager获得连接,也不需使用JNDI查找获得连接.Spring的JDBC连接依赖IOC容器注入

    》模板化操作方式--无需繁琐的getConnection,createStatement等操作

    》优秀的面向对象操作方式--结果直接转化成JavaBean传出

    》一致的异常继承体系--无需捕获JDBC特定的数据库异常,JDBC的checked异常被包装成了Runtime异常,不再要求强制捕捉

#4、JdbcTemplate访问数据库

    》执行简单的查询 {API详见Spring API 2.0}

  int queryForXxx(String sql):静态SQL命令执行查询,Xxx可以是int或long,返回一个标量值,而且只能返回一行记录

    int queryForXxx(String sql,Object[] args):同上,带参数而已

    Object queryForObject(String sql,Class requiredType):返回执行单行单列的查询,将返回结果转换成requiredType类型的对象

    注:只支持特定类型的转换,比如,varchar只能转成String

    List queryForList(String sql):

    List queryForList(String sql,Object[] args):

    List queryForList(String sql,Class requiredType):

    List queryForList(String sql, Object[] args, Class elementType):

    /*

    *示例

    */

    public class JdbcTemplateQuery

    {

    public static void main(String[] args)throws Exception

    {

    ComboPooledDataSource ds = new ComboPooledDataSource();

    ds.setDriverClass("com.mysql.jdbc.Driver");

    ds.setJdbcUrl("jdbc:mysql://localhost:3306/j2ee");

    ds.setUser("root");

    ds.setPassword("32147");

    ds.setMaxPoolSize(40);

    ds.setMinPoolSize(2);

    ds.setMaxStatements(180);

    //创建一个JdbcTemplate

    JdbcTemplate jt = new JdbcTemplate();

    //为JdbcTemplate指定DataSource

    jt.setDataSource(ds);

    //如果只需返回一个特定值,可直接查询

    int count = jt.queryForInt("select count(*) from mytable");

    System.out.println(count);

    //此处的转换实际非常简单:只支持Varchar->String的转换.

    String nametmp = (String)jt.queryForObject("select name from mytable where name='wawa2'",String.class);

    System.out.println(nametmp);

    List namelist = jt.queryForList("select name from mytable");

    for (Iterator it = namelist.iterator();it.hasNext(); )

    {

    System.out.println(it.next().getClass());

    }

    //返回系列值

    List list = jt.queryForList("select * from mytable");

    for (Iterator it = list.iterator();it.hasNext(); )

    {

    System.out.println(it.next().getClass());

    System.out.println((Map)it.next());

    }

    }

    }

    》执行更新的

    int update(String sql)

    int update(String sql,Object[] args)

    int update(String sql,Object[] args,int[] argTypes):使用PreparedStatement执行更新,args用于传入参数,argTypes指定参数的SQL类型

    /*

    *示例

    */

    public class JdbcTemplateUpdate

    {

    public static void main(String[] args)throws Exception

    {

    ComboPooledDataSource ds = new ComboPooledDataSource();

    ds.setDriverClass("com.mysql.jdbc.Driver");

    ds.setJdbcUrl("jdbc:mysql://localhost:3306/j2ee");

    ds.setUser("root");

    ds.setPassword("32147");

    ds.setMaxPoolSize(40);

    ds.setMinPoolSize(2);

    ds.setMaxStatements(180);

    //创建一个JdbcTemplate

    JdbcTemplate jt = new JdbcTemplate();

    //为JdbcTemplate指定DataSource

    jt.setDataSource(ds);

    jt.update("update mytable set name = 'china' where name='aaa'");

    String[] values = {"American"} ;

    jt.update("update mytable set name = ? where name='china'" , values);

    }

    }

    》执行简单DDL的

    void execute(String sql)

    /*

    *示例

    */

    public class JdbcTemplateDdl

    {

    public static void main(String[] args)throws Exception

    {

    ComboPooledDataSource ds = new ComboPooledDataSource();

    ds.setDriverClass("com.mysql.jdbc.Driver");

    ds.setJdbcUrl("jdbc:mysql://localhost:3306/j2ee");

    ds.setUser("root");

    ds.setPassword("123456");

    ds.setMaxPoolSize(40);

    ds.setMinPoolSize(2);

    ds.setMaxStatements(180);

    //创建一个JdbcTemplate

    JdbcTemplate jt = new JdbcTemplate();

    //为JdbcTemplate指定DataSource

    jt.setDataSource(ds);

    jt.execute("drop table if exists wawa");

    jt.execute("create table xxx(name varchar(100))");

    System.out.println("正常结束");

    }

    }

    #5、StatementCallback访问数据库

    JdbcTemplate对数据库访问包装,持久化操作更简单,但降低了访问的灵活性,而通过回调接口的使用,可弥补此损失.

    通过StatementCallback接口,可获得JdbcTemplate的Statement回调,使用原生的SQL命令,此时,同样可利用JdbcTemplate提供的系列优点,如一致的异常体系.

    StatementCallback回调接口只有一个方法需实现:

    Object doInStatement(Statement stmt)throws SQLException,DataAccessException

    在该方法的执行体内,可获得Statement的引用,从而完成数据库的访问.

    /*

    *示例

    */

    public class ExecuteStatementCallback

    {

    public static void main(String[] args)throws Exception

    {

    ComboPooledDataSource ds = new ComboPooledDataSource();

    ds.setDriverClass("com.mysql.jdbc.Driver");

    ds.setJdbcUrl("jdbc:mysql://localhost:3306/j2ee");

    ds.setUser("root");

    ds.setPassword("32147");

    ds.setMaxPoolSize(40);

    ds.setMinPoolSize(2);

    ds.setMaxStatements(180);

    //创建一个JdbcTemplate

    JdbcTemplate jt = new JdbcTemplate();

    //为JdbcTemplate指定DataSource

    jt.setDataSource(ds);

    jt.execute(new StatementCallback()

    {

    public Object doInStatement(Statement stmt)throws SQLException

    {

    stmt.execute("update mytable set name = 'xx' where name ='American' ");

    return null;

    }

    });

    }

    }

    程序中,通常使用StatementCallback的匿名内部类创建StatementCallback实例,实现该接口,则要求实现doInStatement(Statement stmt)方法,其方法体就是实际要执行的SQL操作

    #6、PreparedStatementCallback访问数据库

    作用类似于StatementCallback接口,只是使用了PreparedStatement对象

    /*

    *示例

    */

    public class ExecutePreparedStatementCallback

    {

    public static void main(String[] args)throws Exception

    {

    ComboPooledDataSource ds = new ComboPooledDataSource();

    ds.setDriverClass("com.mysql.jdbc.Driver");

    ds.setJdbcUrl("jdbc:mysql://localhost:3306/j2ee");

    ds.setUser("root");

    ds.setPassword("32147");

    ds.setMaxPoolSize(40);

    ds.setMinPoolSize(2);

    ds.setMaxStatements(180);

    //创建一个JdbcTemplate

    JdbcTemplate jt = new JdbcTemplate();

    //为JdbcTemplate指定DataSource

    jt.setDataSource(ds);

    jt.execute("update mytable set name=? where name = 'xx'", new PreparedStatementCallback()

    {

    public Object doInPreparedStatement(PreparedStatement pstmt)throws SQLException

    {

    pstmt.setString(1,"-----");

    pstmt.execute();

    return null;

    }

    }

    );

    }

    }

    #7、连接数据库的辅助类

    》DataSourceUtils工具类:通过DataSource获取连接,支持线程的绑定,如用于DataSourceTransactionManager.含有很多静态方法,主要有2个:

    》static Connection getConnection(DataSource dataSource)

    》static voic releaseConnection(Connection con,DataSource dataSource)

    通常情况下建议使用spring的IOC容器管理DataSource.

    /*

    *示例

    */

    public class BeanTest

    {

    public static void main(String[] args)throws Exception

    {

    //创建DataSource实例

    ComboPooledDataSource ds = new ComboPooledDataSource();

    //加载DataSource驱动

    ds.setDriverClass("com.mysql.jdbc.Driver");

    //设置连接数据库的url

    ds.setJdbcUrl("jdbc:mysql://localhost:3306/j2ee");

    //设置数据库用户名

    ds.setUser("root");

    //设置数据库密码

    ds.setPassword("32147");

    //设置池的最大连接数

    ds.setMaxPoolSize(40);

    //设置池的最小连接数

    ds.setMinPoolSize(2);

    ds.setMaxStatements(180);

    //以下才是真正使用Spring的JDBC的事务方法

    Connection conn = DataSourceUtils.getConnection(ds);

    java.sql.Statement stmt = conn.createStatement();

    stmt.execute("insert into mytable values('wddda2')");

    }

    }

    通过DataSourceUtils获得连接比使用DataSource的getConnection直接获取的更智能,前者的连接在必要时自动关闭.在Spring的事务管理结合时,更具有独特的能力.

    》SmartDataSource接口:继承DataSource接口,提供一些额外的功能,在恰当的时候关闭连接,更加智能化.

    》SingelConnectionDataSource类:采用单态模式实现SmartDataSource接口

    /*

    *示例

    */

    public class BeanTest

    {

    public static void main(String[] args)throws Exception

    {

    SingleConnectionDataSource ds = new SingleConnectionDataSource();

    ds.setDriverClassName("com.mysql.jdbc.Driver");

    ds.setPassword("32147");

    ds.setUrl("jdbc:mysql://localhost:3306/j2ee");

    ds.setUsername("root");

    //ds.setSuppressClose(true);

    Connection conn = DataSourceUtils.getConnection(ds);

    System.out.println(conn);

    //java.sql.Statement stmt = conn.createStatement();

    //stmt.execute("insert into mytable values('w52x')");

    //conn.close();

    //conn.createStatement();

    System.out.println(DataSourceUtils.getConnection(ds));

    System.out.println(DataSourceUtils.getConnection(ds));

    }

    }

    》DriverManagerDataSource类继承Spring的抽象类AbstractDataSource,实际上,DriverManagerDataSource常被作为SmartDataSource的一个实现,而不是作为JDBC2.0规范的DataSource实现.因为DriverManagerDataSource并不具备连接池的能力.

    该类主要用于测试,它可以脱离j2ee容器独立运行,可以作为不同ApplicationContext的数据源bean,也可以和简易的JNDI环境一起工作.

    /*

    *示例

    */

    public class TransactionTest

    {

    public static void main(String[] args)

    {

    final ApplicationContext ctx = new FileSystemXmlApplicationContext("bean.xml");

    System.out.println("============");

    PlatformTransactionManager transactionManager = (PlatformTransactionManager)ctx.getBean("transactionManager");

    TransactionTemplate tt = new TransactionTemplate(transactionManager);

    tt.execute(new TransactionCallbackWithoutResult()

    {

    protected void doInTransactionWithoutResult(TransactionStatus ts)

    {

    try

    {

    DataSource ds = (DataSource)ctx.getBean("dataSource");

    Connection conn = DataSourceUtils.getConnection(ds);

    java.sql.Statement stmt = conn.createStatement();

    stmt.execute("insert into mytable values('china')");

    stmt.execute("insert into mytable values('wawa2')");//让此句引发异常就可看出事务的管理

    }

    catch (SQLException sqle)

    {

    //这种方式无须显式提交,但需要在出现异常的时候显式回滚

    ts.setRollbackOnly();

    sqle.printStackTrace();

    }

    }

    });

    }

    }

<!--EndFragment-->



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值