浅析数据源在JDBC中的应用
副标题#e#
简介
众所周知,JDBC(Java数据库毗连)是Java 2企业版的重要构成部门。它是基于SQL层的API。通过把SQL语句嵌入JDBC接口的要领中,用户可以通过Java措施执行险些所有的数据库操纵。JDBC只提供了接口,详细的类的实现要求数据库的设计者完成。通过生成这些接口的实例,纵然对付差异的数据库,Java措施也可以正确地执行SQL挪用。所以对付措施员来说,不必把留意力放在如何向数据库发送SQL指令,因为措施员需要相识和用到的只是JDBC的接口,只有在少少数环境下会用到面向特定命据库的类,譬喻措施员但愿利用ORACLE的扩展API。
在JDBC措施中,首先需要做的是实现与数据库的毗连。在示例措施中,我们利用的是ORACLE8i的JDBC包。毗连数据库凡是需要实现以下几个步调:
1. 注册数据库驱动措施(driver)。可以通过挪用java.sql.DriverManager类的registerDriver要领显式注册驱动措施,也可以通过加载数据库驱动措施类隐式注册驱动措施。譬喻我们但愿向虚拟机注册Oracle8i JDBC驱动措施
// 显式注册
DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
// 隐式注册
Class.forName(“oracle.jdbc.driver.OracleDriver”);
关于虚拟机如何自动注册通过类加载器(ClassLoader)加载的数据库驱动措施高出了本文接头的范畴,在此不做具体接头。
2.成立毗连。挪用java.sql.DriverManager类的getConnection()要领可以成立与数据库的毗连。GetConnection()要领返回一个Connection工具。需要留意的是,getConnection()要了解自动从数据库驱动措施注册表中选择一个最符合的驱动措施。
3.成立毗连后,答允自动更新(AutoCommit)。挪用java.sql.Connection接口的serAutoCommit()要领可以设定当措施向数据库发出一条SQL指令后,数据库是否当即更新。
下面是一个详细的实例。在该实例中,作为getConnection()要领参数的url利用的是Net8 keyword-value pair名目。虽然也可以利用普通名目。数据库安装在名为Chicago的处事器上,利用的协议是TCP协议,利用的端口是1521,数据库的SID是chidb,利用的数据库驱动措施是Oracle JDBC Thin驱动措施。
import java.sql.*;
// 初始化常数
private static String url =
“jdbc:oracle:thin:@(description=(address=(host=Chicago)” +
“(protocol=tcp)(port=1521))(connect_data=(sid=chidb)))”;
// 也可以设定url为“jdbc:oracle:thin:@ Chicago:1521:chidb”
private static String username = “guest”;
private static String password = “guest”;
try
{
// 注册数据库
Class.forName(“oracle.jdbc.driver.OracleDriver”);
// 成立毗连
Connection conn =
DriverManager.getConnection(url, username, password);
// 答允自动更新
Conn.setAutoCommit(true);
}
catch(ClassNotFoundException e )
{
e.printStackTrace();
}
catch(SQLException e)
{
e.printStackTrace();
}
从实际应用的角度出发,我们可以看出采纳这种方法毗连到数据库存在几个问题。第一是安详性问题,由于措施代码中包括用户名和暗码,其他人假如能获得bytecode,可以通过反编译东西得到用户名和暗码。第二是代码的可移植性问题。假如但愿毗连的数据库名称或用户名有所变动,措施员需要修改源措施,然后把修悔改的措施发送给用户。也就是说,软件无法离开数据库独立存在。这样不只会大大提高软件的本钱,也倒霉于软件自己的成长。还大概呈现这样的环境:在某些环境下,提供数据的机构不但愿数据库的用户名和暗码让编写措施的措施员知道知道。这样就提出了一个问题,如何使Java和数据库之间成立毗连时埋没一些敏感的信息。
#p#副标题#e#
数据源(Data Source)及JNDI
数据源是在JDBC 2.0中引入的一个观念。在JDBC 2.0扩展包中界说了javax.sql.DataSource接口来描写这个观念。假如用户但愿成立一个数据库毗连,通过查询在JNDI处事中的数据源,可以从数据源中获取相应的数据库毗连。这样用户就只需要提供一个逻辑名称(Logic Name),而不是数据库登录的详细细节。
在这里有须要简朴先容一下JNDI。JNDI的全称是Java Naming and Directory Interface, 可以领略为Java名称和目次处事接口。JNDI向应用措施提供了一个查询和利用长途处事的机制。这些处事可以是任何企业处事。对付JDBC应用措施来说,JNDI提供的是数据库毗连处事。虽然JNDI也可以向数据库提供其他处事,可是这超出了本文范畴,在此不做阐述。
#p#分页标题#e#
其实JNDI并不难领略。简朴来说,名称处事提供了一个把文件,打印机,处事器等实体映射到一个逻辑名称的机制。譬喻在操纵系统中的名称处事就把打印机映射到一个I/O端口。而目次处事可以领略为名称处事的一个扩展,它答允在处事中的各项拥有本身的属性。又以打印机为例,打印机可以是彩色打印机,支持双面打印,支持网络打印,支持高速打印等。所有这些打印机的属性都可以储存在目次处事中,和相应的打印机接洽起来。一些常见的目次处事有NIS,NIS+,LDAP和Novell的NDS等。
JNDI使应用措施通过利用逻辑名称获取工具和工具提供的处事,从而使措施员可以制止利用与提供工具的机构有关联的代码。譬喻在下面的例子中利用了在JNDI中的数据源,措施员就不需要提供Oracle8i驱动措施的名称,这样代码的移植本领就更强。
下面具体先容一下数据源和javax.sql.DataSource接口。在数据源中存储了所有成立数据库毗连的信息。就象通过指定文件名你可以在文件系统中找到文件一样,通过提供正确的数据源名称,你可以找到相应的数据库毗连。javax.sql.DataSource接口界说了如何实现数据源。在该接口中界说了九个属性。表一列出了对这些属性的描写。由于本文是以Oracle8i为例,在Oracle8i中没有实现roleName属性,所以在表中没有对此属性做描写。
表一:数据源尺度属性
属性名称 | 属性数据范例 | 描写 |
databaseName | String | 数据库名称,即数据库的SID。 |
dataSourceName | String | 数据源接话柄现类的名称。 |
description | String | 对数据源的描写。 |
networkProtocol | String | 和处事器通讯利用的网络协议名。在Oracle8i中,该属性只在利用OCI驱动措施时有效,缺省协议是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界说的属性和要领外,Oracle8i还提供了本身的数据源属性和要领。这些要领和属性是在oracle.jdbc.pool.OracleDataSource中实现的。 Oracle8i扩展数据源属性如表二所示:
表二:
属性名称 | 属性数据范例 | 描写 |
driverType | String | 利用的Oracle JDBC驱动措施的范例,包罗oci8, thin和kprb |
url | String | 数据库毗连的URL。 |
tnsEntry | String | TNS条目名称 |
在oracle.jdbc.pool.OracleDataSource中除了实现javax.sql.DataSource接口中界说的要领外,还实现了以下要领:
#p#分页标题#e#
* 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()
同时,OracleDataSource还实现了java.io.Serializable和javax.naming.Referenceable接口。
独立利用数据源
实际应用中,你可以把OracleDataSource注册到JNDI,也可以单独利用。下面先给出一个单独利用OracleDataSource的例子:
// 初始化数据源实例
OracleDataSource ods = new OracleDataSource();
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();
// 通过数据库毗连举办数据操纵
………………
利用OracleDataSource时有几点需要留意:
假如利用的时处事器端内部驱动措施(server-side internal driver),driverType属性会被配置为kprb,其它所有属性失效。
假如利用Thin或OCI驱动措施:—www.bianceng.cn
URL中可以包罗用户登录名和用户登录暗码。譬喻:
jdbc:oracle:thin:guest/[email protected]:1521:chidb;
果设定了url属性,tnsEntry, driverType, portNumber, networkProtocol, serverName,和databaseName属性将失效。
在没有设定url属性的环境下,假如设定了tnsEntry属性,portNumber, networkProtocol, serverName,和databaseName属性将失效。
假如利用OCI驱动措施,而且networkProtocol属性被设定为ipc,除user和password外的所有其他属性将失效。
通过JNDI利用数据源
在本节首先给出了一个实际措施,然后通过措施来讲授如何通过JNDI查询数据源。
import java.sql.*;
import javax.sql.*;
import oracle.jdbc.driver.*;
import oracle.jdbc.pool.OracleDataSource;
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
{
// 建设一个OracleDataSource实例
OracleDataSource ods = new OracleDataSource();
ods.setDriverType("thin");
ods.setServerName("Chicago");
ods.setNetworkProtocol("tcp");
ods.setDatabaseName("chidb");
ods.setPortNumber(1521);
ods.setUser("guest");
ods.setPassword("guest");
// 把OracleDataSource实例注册到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中查询OracleDataSource实例
System.out.println ("Doing a lookup with the logical name : " + ln);
OracleDataSource ods = (OracleDataSource) ctx.lookup (ln);
System.out.println ("Successful lookup");
// 从查询到的OracleDataSource实例中获取数据库毗连
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;
}
}
#p#分页标题#e#
措施首先生成了一个Context实例。javax.naming.Context接口界说了名称处事情况(Naming 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目次下。
初始化了名称处事情况后,就可以把数据源实例注册到名称处事情况中。注册时挪用javax.naming.Context.bind()要领,参数为注册名称和注册工具。注册乐成后,在JNDI目次下会生成一个.binding文件,该文件记录了当前名称处事情况拥有的名称及工具。
当需要在名称处事情况中查询一个工具时,需要挪用javax.naming.Context.lookup()要领,并把查询到的工具显式转化为数据源工具。然后通过该数据源工具举办数据库操纵。
在这个例子中,措施和名称处事情况都是在同一台计较机上运行。在实际的应用中,措施可以通过RMI或CORBA向名称处事情况注册或查询工具。譬喻在一个处事器-客户机布局中,客户机上的应用措施只需要知道数据源工具在处事器名称处事情况中的逻辑名称,就可以通过RMI向处事器查询数据源,然后通过成立与数据库的毗连.这样就可以办理本文最开始提出的问题。