现在的位置: 首页 > 综合 > 正文

数据源在jdbc中的应用

2012年08月13日 ⁄ 综合 ⁄ 共 7877字 ⁄ 字号 评论关闭
数据源在jdbc中的应用(1)

简介
众所周知,JDBC(Java数据库连接)是Java 2企业版的重要组成部分。它是基于SQL层的
API。通过把SQL语句嵌入JDBC接口的方法中,用户可以通过Java程序执行几乎所有的数
据库操作。JDBC只提供了接口,具体的类的实现要求数据库的设计者完成。通过生成这
些接口的实例,即使对于不同的数据库,Java程序也可以正确地执行SQL调用。所以对于
程序员来说,不必把注意力放在如何向数据库发送SQL指令,因为程序员需要了解和用到
的只是JDBC的接口,只有在极少数情况下会用到面向特定数据库的类,例如程序员希望
使用ORACLE的扩展API。
在JDBC程序中,首先需要做的是实现与数据库的连接。在示例程序中,我们使用的是OR
ACLE8i的JDBC包。连接数据库通常需要实现以下几个步骤:
1. 注册数据库驱动程序(driver)。可以通过调用java.sql.DriverManager类的regis
terDriver方法显式注册驱动程序,也可以通过加载数据库驱动程序类隐式注册驱动程序
。例如我们希望向虚拟机注册 // 显式注册
DriverManager.registerDriver(new oracle.jdbc.driver.// 隐式注册
Class.forName(“oracle.jdbc.driver.
关于虚拟机如何自动注册通过类加载器(ClassLoader)加载的数据库驱动程序超过了本
文讨论的范围,在此不做详细讨论。
2. 建立连接。调用java.sql.DriverManager类的getConnection()方法可以建立与数
据库的连接。GetConnection()方法返回一个Connection对象。需要注意的是,getCo
nnection()方法会自动从数据库驱动程序注册表中选择一个最合适的驱动程序。
3. 建立连接后,允许自动更新(AutoCommit)。调用java.sql.Connection接口的ser
AutoCommit()方法可以设定当程序向数据库发出一条SQL指令后,数据库是否立即更新

下面是一个具体的实例。在该实例中,作为getConnection()方法参数的url使用的是
Net8 keyword-value pair格式。当然也可以使用普通格式。数据库安装在名为Chicago
的服务器上,使用的协议是TCP协议,使用的端口是1521,数据库的SID是chidb,使用的
数据库驱动程序是 import java.sql.*;
// 初始化常数
private static String url =
  “jdbc困惑racle:thin:@(description=(address=(host=Chicago)” +
  “(protocol=tcp)(port=1521))(connect_data=(sid=chidb)))”;
  // 也可以设定url为“jdbc困惑racle:thin:@ Chicago:1521:chidb”
  private static String username = “guest”;
private static String password = “guest”;
try
{
  // 注册数据库
  Class.forName(“oracle.jdbc.driver.  // 建立连接
  Connection conn =
  DriverManager.getConnection(url, username, password);
// 允许自动更新
Conn.setAutoCommit(true);
}
catch(ClassNotFoundException e )
{
e.printStackTrace();
}
catch(SQLException e)
{
e.printStackTrace();
}
从实际应用的角度出发,我们可以看出采取这种方式连接到数据库存在几个问题。第一
是安全性问题,由于程序代码中包含用户名和密码,其他人如果能得到bytecode,可以
通过反编译工具获得用户名和密码。第二是代码的可移植性问题。如果希望连接的数据
库名称或用户名有所更改,程序员需要修改源程序,然后把修改过的程序发送给用户。
也就是说,软件无法脱离数据库独立存在。这样不仅会大大提高软件的成本,也不利于
软件本身的发展。还可能出现这样的情况:在某些情况下,提供数据的机构不希望数据
库的用户名和密码让编写程序的程序员知道知道。这样就提出了一个问题,如何使Java
和数据库之间建立连接时隐藏一些敏感的信息。
数据源(Data Source)及JNDI
数据源是在JDBC 2.0中引入的一个概念。在JDBC 2.0扩展包中定义了javax.sql.DataSo
urce接口来描述这个概念。如果用户希望建立一个数据库连接,通过查询在JNDI服务中
的数据源,可以从数据源中获取相应的数据库连接。这样用户就只需要提供一个逻辑名
称(Logic Name),而不是数据库登录的具体细节。
在这里有必要简单介绍一下JNDI。JNDI的全称是Java Naming and Directory Interfac
e, 可以理解为Java名称和目录服务接口。JNDI向应用程序提供了一个查询和使用远程
服务的机制。这些服务可以是任何企业服务。对于JDBC应用程序来说,JNDI提供的是数
据库连接服务。当然JNDI也可以向数据库提供其他服务,但是这超出了本文范围,在此
不做论述。
其实JNDI并不难理解。简单来说,名称服务提供了一个把文件,打印机,服务器等实体
映射到一个逻辑名称的机制。例如在操作系统中的名称服务就把打印机映射到一个I/O端
口。而目录服务可以理解为名称服务的一个扩展,它允许在服务中的各项拥有自己的属
性。又以打印机为例,打印机可以是彩色打印机,支持双面打印,支持网络打印,支持
高速打印等。所有这些打印机的属性都可以储存在目录服务中,和相应的打印机联系起
来。一些常见的目录服务有NIS,NIS+,LDAP和Novell的NDS等。
JNDI使应用程序通过使用逻辑名称获取对象和对象提供的服务,从而使程序员可以避免
使用与提供对象的机构有关联的代码。例如在下面的例子中使用了在JNDI中的数据源,
程序员就不需要提供

下面详细介绍一下数据源和javax.sql.DataSource接口。在数据源中存储了所有建立数
据库连接的信息。就象通过指定文件名你可以在文件系统中找到文件一样,通过提供正
确的数据源名称,你可以找到相应的数据库连接。javax.sql.DataSource接口定义了如
何实现数据源。在该接口中定义了九个属性。表一列出了对这些属性的描述。由于本文
是以8i为例,在8i中没有实现roleName属性,所以在表中没有对此属性做描述。
表一:数据源标准属性
属性名称
属性名称
属性数据类型 描述
databaseName String 数据库名称,即数据库的SID。
dataSourceName String 数据源接口实现类的名称。
description String 对数据源的描述。
networkProtocol String 和服务器通讯使用的网络协议名。在8i中,该属性只在使用O
CI驱动程序时有效,缺省协议是TCP协议。
password String 用户登录密码。
portNumber Int 数据库服务器使用的端口,缺省值为1521。
serverName String 数据库服务器名称。
user String 用户登录名。
在javax.sql.DataSource接口中定义了以下方法:
* public synchronized void setDatabaseName(String dbname)
* public synchronized String getDatabaseName()
* public synchronized void setDataSourceName(String dsname)
* public synchronized String getDataSourceName()
* public synchronized void setDescription(String desc)
* public synchronized String getDescription()
* public synchronized void setNetworkProtocol(String np)
* public synchronized String getNetworkProtocol()
* public synchronized void setPassword(String pwd)
* public synchronized void setPortNumber(int pn)
* public synchronized int getPortNumber()
* public synchronized void setServerName(String sn)
* public synchronized String getServerName()
* public synchronized void setUser(String user)
* public synchronized String getUser()
通过这些方法,程序员可以获得建立连接需要的所有信息。需要注意的是,程序员不可
以获取登陆密码,这就在一定程度上保证了安全性。需要注意的另一点是所有的方法都
是synchronized方法,这是为了保证应用程序的线程安全(Thread-safe)。如果在调用
该方法时,即使数据源实例发生变化不会影响程序的正确运行。
除了实现由SUN定义的属性和方法外,8i还提供了自己的数据源属性和方法。这些方法和
属性是在oracle.jdbc.pool.DataSource中实现的。 8i扩展数据源属性如表二所示:
表二:
属性名称 属性数据类型 描述
driverType String 使用的 JDBC驱动程序的类型,包括oci8, thin和kprb
url String 数据库连接的URL。
tnsEntry String TNS条目名称
在oracle.jdbc.pool.DataSource中除了实现javax.sql.DataSource接口中定义的方法外
,还实现了以下方法:
* public synchronized void setDriverType(String dt)
* public synchronized String getDriverType()
* public synchronized void setURL(String url)
* public synchronized String getURL()
* public synchronized void setTNSEntryName(String tns)
* public synchronized String getTNSEntryName()
同时,DataSource还实现了java.io.Serializable和javax.naming.Referenceable接口

独立使用数据源
实际应用中,你可以把DataSource注册到JNDI,也可以单独使用。下面先给出一个单独
使用DataSource的例子:
// 初始化数据源实例
DataSource ods = new DataSource();
ods.setDriverType("thin");
ods.setServerName("Chicago");
ods.setNetworkProtocol("tcp");
ods.setDatabaseName("chidb");
ods.setPortNumber(1521);
ods.setUser("guest");
ods.setPassword("guest");
// 从数据源中获取数据库连接
Connection conn = ods.getConnection();
// 通过数据库连接进行数据操作
   ………………
使用DataSource时有几点需要注意:
如果使用的时服务器端内部驱动程序(server-side internal driver),driverType属
性会被设置为kprb,其它所有属性失效。
如果使用Thin或OCI驱动程序:
URL中可以包括用户登录名和用户登录密码。例如:
jdbc困惑racle:thin:guest/guest@Chicago:1521:chidb;
如果设定了url属性,tnsEntry, driverType, portNumber, networkProtocol, server
Name,和databaseName属性将失效。
在没有设定url属性的情况下,如果设定了tnsEntry属性,portNumber, networkProtoc
ol, serverName,和databaseName属性将失效。
如果使用OCI驱动程序,并且networkProtocol属性被设定为ipc,除user和password外的
所有其他属性将失效。
通过JNDI使用数据源
在本节首先给出了一个实际程序,然后通过程序来讲解如何通过JNDI查询数据源。
import java.sql.*;
import javax.sql.*;
import oracle.jdbc.driver.*;
import oracle.jdbc.pool.DataSource;
import javax.naming.*;
import javax.naming.spi.*;
import java.util.Hashtable;
public class DataSourceJNDI
{
public static void main (String args [])
throws SQLException
{
// 初始化名称服务环境
Context ctx = null;
try
{
Hashtable env = new Hashtable (5);
env.put (Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.fscontext.RefFSContextFactory");
env.put (Context.PROVIDER_URL, "file:JNDI");
ctx = new InitialContext(env);
}
catch (NamingException ne)
{
ne.printStackTrace();
}
bind(ctx, "jdbc/chidb");
lookup(ctx, "jdbc/chidb");
}
static void bind (Context ctx, String ln)
throws NamingException, SQLException
{
// 创建一个DataSource实例
DataSource ods = new DataSource();
ods.setDriverType("thin");
ods.setServerName("Chicago");
ods.setNetworkProtocol("tcp");
ods.setDatabaseName("chidb");
ods.setPortNumber(1521);
ods.setUser("guest");
ods.setPassword("guest");
// 把DataSource实例注册到JNDI中
System.out.println ("Doing a bind with the logical name : " + ln);
ctx.bind (ln,ods);
System.out.println ("Successfully bound");
}
static void lookup (Context ctx, String ln)
throws NamingException, SQLException
{
// 从JNDI中查询DataSource实例
System.out.println ("Doing a lookup with the logical name : " + ln);
DataSource ods = (DataSource) ctx.lookup (ln);
System.out.println ("Successful lookup");
// 从查询到的DataSource实例中获取数据库连接
  Connection conn = ods.getConnection();
  // 进行数据库操作
getUserName(conn);
// 关闭连接
conn.close();
conn = null;
}
static void getUserName(Connection conn)
throws SQLException
{
// 生成一个Statement实例
Statement stmt = conn.createStatement ();
// 从addressbook表中选中姓名列
ResultSet rset = stmt.executeQuery ("select NAME from addressbook");
// 列出addressbook表所有人的姓名
while (rset.next ())
System.out.println ("Name is " + rset.getString (1));
// 关闭RseultSet实例
rset.close();
rset = null;
// 关闭Statement实例
stmt.close();
stmt = null;
stmt = null;
}
}
程序首先生成了一个Context实例。javax.naming.Context接口定义了名称服务环境(N
aming Context)及该环境支持的操作。名称服务环境实际上是由名称和对象间的相互映
射组成。程序中初始化名称服务环境的环境工厂(Context Factory)是com.sun.jndi.
fscontext.RefFSContextFactory(该类在fscontext.jar中可以找到,由于fscontext.
jar中包含的不是标准的API,用户需要从www.javasoft.com中的JNDI专区下载一个名为
fscontext1_2beta3.zip的压缩文件,在该文件中可以找到fscontext.jar)。环境工厂的
作用是生成名称服务环境的实例,javax.naming.spi.InitialContextFactory接口定义
了环境工厂应该如何初始化名称服务环境。在初始化名称服务环境时还需要定义环境的
URL。程序中使用的是"file:JNDI",也就是把环境保存在本地硬盘的JNDI目录下。
初始化了名称服务环境后,就可以把数据源实例注册到名称服务环境中。注册时调用ja
vax.naming.Context.bind()方法,参数为注册名称和注册对象。注册成功后,在JNDI目
录下会生成一个.binding文件,该文件记录了当前名称服务环境拥有的名称及对象。
当需要在名称服务环境中查询一个对象时,需要调用javax.naming.Context.lookup()
方法,并把查询到的对象显式转化为数据源对象。然后通过该数据源对象进行数据库操
作。
在这个例子中,程序和名称服务环境都是在同一台计算机上运行。在实际的应用中,程
序可以通过RMI或CORBA向名称服务环境注册或查询对象。例如在一个服务器-客户机结构
中,客户机上的应用程序只需要知道数据源对象在服务器名称服务环境中的逻辑名称,就
可以通过RMI向服务器查询数据源,然后通过建立与数据库的连接.这样就可以解决本文最
开始提出的问题。

抱歉!评论已关闭.