1 简介
Java的JDBC是Java应用访问数据库的通用编程接口,一个紧凑、简单的软件层,主要功能在于定义了应用程序如何打开数据库连接、如何与数据库通信、如何执行SQL语句和如何检索查询结果等,同时兼顾了通用性、速度和兼容性,即
1)通用:跨数据库、跨平台,通过对不同的数据库的客户端的抽象,提供统一的编程接口
2)速度:运用不同的JDBC驱动程序,可提高访问数据库的速度。通用性是速度的敌人,从底层实现JDBC接口可以提高速度。oracle thin driver从tcp层实现jdbc接口。所以速度的问题就是采用何种jdbc驱动的问题。
3)兼容性:适应主流数据库产品的通用特征,如是否支持可修改游标,所支持的二进制对象的最大长度是多少,在一个Connection里面能同时支持多少个语句,同时能打开多少个表等。JDBC充分支持兼容性问题。
具体来看,JDBC的特性有:
1)不限制传递到底层DBMS驱动程序的查询类型
2)JDBC机制易于理解和使用
3)提供与Java系统的其他部分保持一致的Java接口
4)JDBC可以在常见SQL层API的顶层实现
同时,JDBC具有SQL一致性,即JDBC在类Java.SQL.Types中定义了一组通用SQL类型标识符,并且JDBC还通过以下几种方式处理SQL一致性问题:JDBC API允许将任何查询字符串传递到底层DBMS驱动程序;提供内置功能,便于将包含转义序列的SQL查询转换为数据库可理解的格式;提供DatabaseMetaData接口,允许用户检索关于所使用的DBMS信息。
JDBC支持两种模型:二层模型和三层模型。二层模型表现为Java applet和应用程序直接与数据库交互,被称为客户端/服务器配置;三层模型
使用中间层,可以在不同语言中实现中间层。
2、JDBC驱动程序的类型
客户端访问数据库服务器(DBMS)从本质上来说,是一个网络通信应用:TCP应用。具体为首先客户端发送SQL语句,然后DBMS返回SQL语句的结果。如SQL Server通过1433端口,Oralce通过1521端口来进行TCP通信。一般情况下,不需要直接与DBMS进行TCP通信,也不可能,因为厂家通常没有公开访问方法的细节。相反,DBMS厂家提供了一些函数库,程序代码通过调用这些函数对DBMS进行访问。这些函数处理与DBMS的TCP通信,在Windows/Unix平台函数的定义接口是相同的。这些函数(DB API)以动态链接库进行提供,它们是DBMS客户端的核心部分。如.dll,.so等。不同的数据库产品的客户端,DB API的定义和使用方法不一样。如SQL Server是Msdblib3.dll,Oracle是OCI.dll等。
当然层次越高,速度越慢,通用性越好。
JDBC包含三个组件:应用程序、驱动程序管理器和驱动程序。其中JDBC驱动程序的类型可以分为JDBC-ODBC桥加ODBC驱动程序、本地API、JDBC网络纯Java驱动程序、本地协议纯Java驱动程序。
1)JDBC-ODBC桥加ODBC驱动程序
利用ODBC驱动程序提供JDBC的访问,优点在于易于实现,缺点在于客户端需要安装ODBC驱动
2)本地API
直接调用客户机上的数据库驱动程序(如DLL文件等),缺点依然是客户端需要安装二进制驱动
3)JDBC网络纯Java驱动程序
本质上不算是新的一种形式,只是利用各种容器中间件间接访问数据库
4)本地协议纯Java驱动程序
直接利用TCP通信与数据库通信,无需任何API(其实是绕过API直接来作),如Oracle中的Oracle thin连接
3 JDBC开发流程
3、1 加载JDBC驱动
注意早期版本需要注册JDBC驱动程序,现在通过驱动程序类中的静态块都能自动完成注册,所以不再需要了。
注册方法为:
DriverManager.registerDriver(driver);
其中的driver就是要注册的新JDBC驱动程序
加载JDBC驱动的方法为:
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); (sun.jdbc.odbc.JdbcOdbcDriver类也可以实例化)
这是一个静态方法,用于指示JVM动态的查找、加载和链接指定类(如果没有加载),如果找不到这个类,则抛出ClassNotFoundException。
也可以使用加载:
sun.jdbc.odbc.JdbcOdbcDriver dr = new sun.jdbc.odbc.JdbcOdbcDriver();
3、2 连接数据库(得到Connection)
通过DriverManager.getConnection方法进行连接,如
Connection conn = DriverManager.getConnection(URL,login_name, login_password);
其中JDBC连接由数据库URL标识jdbc:<subprotocol>:<subname>来表示。
3、3 执行查询
Statement对象能够将SQL语句发送到DBMS,创建方法为:
Statement:执行SQL语句。conn.createStatement();
PreparedStatement:执行带参数SQL语句。conn.prepareStatement(“...”);
CallableStatement:执行存储过程。conn.prepareCall(“...”);
3、4 得到结果集
Statement接口的executeQuery:得到结果集ResultSet
Statement接口的executeUpdate:dml语句的执行结果
Statement接口的execute方法:底层方法,可得到多个结果集(如sql的compute by子句就能返回多个结果集)
3、5 处理结果集
利用ResultSet中的方法,如next(),getXXX(...);有些方法涉及到兼容性:first(),last()。
3、6 关闭Connection(关闭很重要)
一个简单的例子:
import java.sql.*;
public class exec
{
public static void main(String [] args)
{
Connection con;
Statement stm;
ResultSet res;
try
{
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
con = DriverManager.getConnection("jdbc:odbc:StuSQL" );
stm=con.createStatement();
res=stm.executeQuery("select * from stu");
while (res.next())
{
System.out.println(res.getString(1));//begin at 1
}
con.close();
}
catch(Exception ex)
{}
}
}
说明:
运行这个程序,需要实现在“控制面板”——“管理工具”——“数据源(ODBC)”中注册一个新的数据源,名称为“StuSQL”,具体的数据库类型和名称自己可以定义,但数据库中一定存在一个名称为“stu”的表
显示所有记录的所有字段
import java.sql.*;
public class exec
{
public static void main(String [] args)
{
Connection con;
Statement stm;
ResultSet res;
ResultSetMetaData rsmd;
try
{
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
con = DriverManager.getConnection("jdbc:odbc:StuSQL" );
stm=con.createStatement();
res=stm.executeQuery("select * from stu");
rsmd = res.getMetaData();
while (res.next())
{
for(int i=1;i<=rsmd.getColumnCount();i++)
System.out.print(res.getObject(i)+"\t");
System.out.println();
}
con.close();
}
catch(Exception ex)
{}
}
}
说明:
ResultSetMetaData和DatabaseMetaData类(通过Connection类的getMetaData方法获取)能够获取数据库的相关元数据信息。
下面的程序能够让用户输入学号,并显示的学生详细信息,如:
import java.sql.*;
public class exec
{
public static void main(String [] args)
{
Connection con;
Statement stm;
ResultSet res;
ResultSetMetaData rsmd;
try
{
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
con = DriverManager.getConnection("jdbc:odbc:StuSQL" );
stm=con.createStatement();
String str=javax.swing.JOptionPane.showInputDialog("Please input the SID:");
res=stm.executeQuery("select * from stu where number='"+str+"'");
rsmd = res.getMetaData();
while (res.next())
{
for(int i=1;i<=rsmd.getColumnCount();i++)
System.out.print(res.getObject(i)+"\t");
System.out.println();
}
con.close();
System.exit(0);
}
catch(Exception ex)
{}
}
}
下面是一个swing版本的数据库演示程序:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.sql.*;
public class exec
{
public static void main(String args[])
{
MyFrame m=new MyFrame("JDBC");
Toolkit theKit = m.getToolkit();
Dimension wndSize = theKit.getScreenSize();
m.setBounds(wndSize.width/4, wndSize.height/4, wndSize.width/2, wndSize.height/2);
m.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
m.setVisible(true);
m.pack();
}
}
class MyFrame extends JFrame implements ActionListener
{
Connection con;
Statement stm;
ResultSet res;
JLabel j1=new JLabel("学号");
JLabel j2=new JLabel("姓名");
JTextField jtf1=new JTextField(10);
JTextField jtf2=new JTextField(10);
JButton jb=new JButton("确定");
JPanel jp1=new JPanel();
JPanel jp2=new JPanel();
public MyFrame(String title)
{
super(title);
jp1.setLayout(new FlowLayout());
jp1.add(j1);
jp1.add(jtf1);
jp1.setLayout(new FlowLayout());
jp1.add(j2);
jp1.add(jtf2);
getContentPane().setLayout(new FlowLayout());
getContentPane().add(jp1);
getContentPane().add(jp2);
getContentPane().add(jb);
jb.addActionListener(this);
try
{
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
con = DriverManager.getConnection("jdbc:odbc:StuSQL" );
stm=con.createStatement();
}
catch(Exception ex)
{}
}
public void actionPerformed(ActionEvent e)
{
try
{
res=stm.executeQuery("select name from stu where number='"+jtf1.getText()+"'");
res.next();
jtf2.setText(res.getString(1));
con.close();
}
catch(Exception ex)
{}
}
}
一般的统计记录个数程序:
import java.sql.*;
public class exec
{
public static void main(String [] args)
{
Connection con;
Statement stm;
ResultSet res;
try
{
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
con = DriverManager.getConnection("jdbc:odbc:StuSQL" );
stm=con.createStatement();
res=stm.executeQuery("select * from stu");
int count=0;
while (res.next())
{
count++;
}
System.out.println(count);
con.close();
}
catch(Exception ex)
{}
}
}
更好的统计记录个数程序:
import java.sql.*;
public class exec
{
public static void main(String [] args)
{
Connection con;
Statement stm;
ResultSet res;
try
{
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
con = DriverManager.getConnection("jdbc:odbc:StuSQL" );
stm=con.createStatement();
res=stm.executeQuery("select count(*) from stu");
res.next();
System.out.println(res.getInt(1));
con.close();
}
catch(Exception ex)
{}
}
}
4、执行DML语句
利用Statement对象的executeUpdate方法即可
import java.sql.*;
public class exec
{
public static void main(String [] args)throws Exception
{
Connection con;
Statement stm;
ResultSet res;
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
con = DriverManager.getConnection("jdbc:odbc:StuSQL" );
stm=con.createStatement();
stm.executeUpdate("insert into stu values('000111','Ben',1,'1985-10-10',1.76)");
con.close();
}
}
5、调用存储过程(CallableStatement)
它是PreparedStatement子类,可以执行存储过程
如建立存储过程:
create proc stu_proc1
as select count(*) from stu
create proc stu_proc2
@pname char(6)
as select * from stu where number=@pname
create proc stu_proc3
@pname char(6),
@result char(8) output
as select @result=name from stu where number=@pname
一般的使用方法为:
import java.sql.*;
public class exec
{
public static void main(String [] args)
{
Connection con;
CallableStatement cstm;
ResultSet res;
ResultSetMetaData rsmd;
try
{
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
con = DriverManager.getConnection("jdbc:odbc:StuSQLServer" );
cstm=con.prepareCall("{call stu_proc1}");
res=cstm.executeQuery();
rsmd = res.getMetaData();
while (res.next())
{
for(int i=1;i<=rsmd.getColumnCount();i++)
System.out.print(res.getObject(i)+"\t");
System.out.println();
}
con.close();
}
catch(Exception ex)
{}
}
}
说明:{call stu_proc1}为调用存储过程的语法,不是标准SQL的方法(所以不能用exec stu_proc1),而是JDBC的转义语法,它会自动将其转换成特定的DBMS格式
对于传入参数的使用方法为:
import java.sql.*;
public class exec
{
public static void main(String [] args)
{
Connection con;
CallableStatement cstm;
ResultSet res;
ResultSetMetaData rsmd;
try
{
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
con = DriverManager.getConnection("jdbc:odbc:StuSQLServer" );
cstm=con.prepareCall("{call stu_proc2(?)}");
cstm.setString(1,"000001");
res=cstm.executeQuery();
rsmd = res.getMetaData();
while (res.next())
{
for(int i=1;i<=rsmd.getColumnCount();i++)
System.out.print(res.getObject(i)+"\t");
System.out.println();
}
con.close();
}
catch(Exception ex)
{}
}
}
注意此程序要求的数据库字符型字段为char,而不能是nvarchar
对于传出参数的使用方法为:
SQLServer做法为
String pcall="{call stu_proc3(?,?)}";
CallableStatement cstmt=conn.prepareCall(pcall);
cstmt.setString(1,"000001");
cstmt.registerOutParameter(2,Types.VARCHAR);//重要,指定传出参数的类型
cstmt.execute();
System.out.println(cstmt.getString(2));//重要,得到传出参数
如:
import java.sql.*;
public class exec
{
public static void main(String [] args)
{
Connection con;
CallableStatement cstm;
ResultSet res;
ResultSetMetaData rsmd;
try
{
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
con = DriverManager.getConnection("jdbc:odbc:StuSQLServer" );
cstm=con.prepareCall("{call stu_proc3(?,?)}");
cstm.setString(1,"000001");
cstm.registerOutParameter(2,Types.VARCHAR);//重要,指定传出参数的类型
cstm.execute();
System.out.println(cstm.getString(2));//重要,得到传出参数
con.close();
}
catch(Exception ex)
{}
}
}
注意此程序要求的数据库字符型字段为char,而不能是nvarchar
[此贴子已经被作者于2010-12-12 08:03:28编辑过]