原文:DataSource Resource Definition in Java EE 6
作者:Jagadish Ramu
出处:http://blogs.sun.com/enterprisetechtips/entry/datasource_resource_definition_in_java
Java EE应用在通过JDBC API访问关系数据库时用到了DataSource对象,DataSource有一组用来标识和描述其所代表的真实世界中的数据源的属性,这些属性包括了诸如数据库服务器的位置、数据库名称以及用来与服务器通信的网络协议等一类信息。此外,DataSource对象还可与Java命名和目录接口(Java Naming and Directory Interface,JNDI)这一命名服务一起工作,DataSource对象在JNDI命名服务中注册了之后,应用就可以使用JNDI API来访问该DataSource对象,该对象随即可被用来连接其所代表的数据源。
在Java EE 6之前,你可以使用厂商特有的诸如命令一类的机制来创建DataSource对象,然后在应用中使用该Datasource对象,尽管如此,Java EE 6还是为数据源的定义带来了更多的灵活性,其引入了一种新的数据源定义资源,你可以为横跨任何兼容Java EE 6的应用服务器的数据库连接做出可移植的声明,可以通过注解(annotation)或者使用部署描述符来声明数据源定义。
本篇技术贴士给你展示了如何使用注解或者部署描述符来创建数据源定义,其中还包括了演示这些功能的用法的示例应用。
使用注解来创建数据源定义
Java EE 6提供了两个新的用于创建数据源定义的注解:@DataSourceDefinition和@DataSourceDefinitions,这两个注解都位于javax.annotation.sql包中,使用@DataSourceDefinition注解来创建单个的数据源定义,使用@DataSourceDefinitions注解来创建多个数据源定义。
@DataSourceDefinition注解
你可以使用@DataSourceDefinition注解来创建一个数据源定义,可以在诸如应用客户端、servlet或者Enterprise JavaBean(EJB)组件等一类的应用组件类中详细说明该注解,其定义了一个DataSource对象并把它注册到JNDI中。你可以通过使用常用的DataSource属性设置注解元素来配置DataSource对象,可以在注解的属性元素中指明附加的标准和厂商特有的属性。
例如,下面的注解创建了一个用于Derby数据库的数据源定义:
@DataSourceDefinition(name = "java:app/env/Servlet_DataSource", minPoolSize = 0, initialPoolSize = 0, className = "org.apache.derby.jdbc.ClientXADataSource", user = "APP", password = "APP", databaseName = "testdb", properties = {"connectionAttributes=;create=true"} )
数据源定义由name这一元素来唯一标识,这是数据源注册时用到的JDNI名称,可以注意到,在name元素中指定的名称以一个命名空间范围为开始,本例中是java:app。Java EE 6引入了应用组件的环境命名空间,其包括以下的这些范围。
-
java:comp。该命名空间中的名称有对每组件来说的可见性。
-
java:module。该命名空间中的名称由某个模块中的所有组件共享。例如,定义在某个ejb-jar.xml文件中的EJB组件。
-
java:app。该命名空间中的名称由应用中的所有的组件和模块共享。例如,某个.ear文件中的应用客户端、web和EJB组件等。
-
java:gobal。在该命名空间中的名称由服务器中的所有应用共享。
根据在name元素中指定的命名空间范围,你可以让数据源在整个模块、应用或者整个服务器中都是可用的。如果使用这些内容来作为name元素的值的前缀:
-
java:comp/env/,数据源可用于该数据源在其之中定义的那个组件,例如servlet、EJB或者应用的客户端组件等。
-
java:module/env/,数据源可以用于所有那些定义在它的ejb-jar.xml文件中的组件。
-
java:app/env/,数据源可用于应用中的所有组件和模块,例如EJB、servlet和应用的客户端组件等。
@DataSourceDefinitions注解
你可以使用@DataSourceDefinitions注解来在一个组件类中创建多个数据源定义,在该注解的value元素中具体说明每一个数据源定义,这儿有一个在EJB类中创建多个用于Derby数据库的数据源定义的例子:
@DataSourceDefinitions( value = { @DataSourceDefinition(name = "java:app/env/HelloStatefulEJB_DataSource", minPoolSize = 0, initialPoolSize = 0, className = "org.apache.derby.jdbc.ClientXADataSource", portNumber = 1527, serverName = "localhost", user = "APP", password = "APP", databaseName = "testdb", properties = {"connectionAttributes=;create=true"} ), @DataSourceDefinition(name = "java:comp/env/HelloStatefulEJB_DataSource", minPoolSize = 0, initialPoolSize = 0, className = "org.apache.derby.jdbc.ClientXADataSource", portNumber = 1527, serverName = "localhost", user = "APP", password = "APP", databaseName = "testdb", properties = {"connectionAttributes=;create=true"} ) } ) @Stateful public class HelloStatefulEJB implements HelloStateful { ... ... }
使用部署描述符来创建数据源的定义
你可以在application.xml、application-client.xml、web.xml或者ejb-jar.xml文件中使用部署描述符来创建数据源定义资源,例如,以下的部署描述符创建了与在之前的@DataSourceDefinition注解例子中的定义相同的数据源定义:
<data-source> <name>java:app/env/Application_Level_DataSource</name> <class-name>org.apache.derby.jdbc.ClientXADataSource</class-name> <server-name>localhost</server-name> <port-number>1527</port-number> <database-name>testdb</database-name> <user>APP</user> <password>APP</password> <property> <name>connectionAttributes</name> <value>;create=true</value> </property> </data-source>
可见到这两个例子都创建了同样的数据源定义,一种使用注解而另一种使用部署描述符。实际上,可以通过这种方法来创建有相同名称的两个数据源定义,在这种情况下,在部署描述符中指定的值优先于在注解中指定的值。例如,如果你使用相同的名称创建了两个数据源定义,一个使用注解而另一个使用部署描述符,并且分别指定了一个数据库名称,则在部署描述符中指定的数据库名称将会被使用。不过,如果你为注解中的某个元素指定了值,但没有在部署描述符中说明相等的元素,则来自注解的值将会与基于部署描述符的信息合并使用。
示范应用
伴随着这一贴士而来的是一个示例应用,该应用演示了使用注解和部署描述符的数据源定义,其为各种Java EE组件和在各种命名空间范围中做了定义。该应用包括以下的Java EE组件:
-
Web 组件: Servlet.java
-
EJB 组件: HelloStatefulEJB.java 和 HelloEJB.java
-
应用客户端: Client.java
有一个数据源定义的名称可提供给该应用使用,应用随后在应用客户端、EJB和servlet组件中查找该数据源,基于组件范围内的现有可用资源,应用或者使用所提供的数据源,或者使用默认的数据源java:app/env/Application_DD_DataSource。
以下是应用创建的或者导出的数据源定义:
-
java:app/env/Application_DD_DataSource
-
java:comp/env/Servlet_DD_DataSource
-
java:module/env/Servlet_DataSource
-
java:app/env/Servlet_DataSource
-
java:comp/env/HelloStatefulEJB_DataSource
-
java:app/env/HelloStatefulEJB_DataSource
-
java:comp/env/HelloEJB_DataSource
-
java:module/env/HelloEJB_DataSource
-
java:app/env/HelloEJB_DD_DataSource
-
java:comp/env/Appclient_DD_DataSource
-
java:module/env/Appclient_DD_DataSource
-
java:app/env/Appclient_DataSource
从资源的名称和命名空间范围就可以识别出该资源所在其中定义的组件,例如,对于被命名为java:app/env/HelloEJB_DD_DataSource的数据源定义来说,其命名空间范围是java:app,并且该资源是在部署描述符“DD”中为“HelloEJB”定义的。
要运行该应用,请执行以下操作:
-
确保你已经安装了 Java Platform Standard Edition(Java SE)6 SDK 这一版本。
-
确保 PATH 这一环境变量包含了 JAVA_HOME/bin ,其中的 JAVA_HOME 设置成 Java SE 6 SDK 的安装路径。
-
确保 ANT 工具已经安装,它应该是作为 GlassFish v3 的一部分被安装了的,以及确保 PATH 这一环境变量中包含了 ANT_HOME/bin ,其中的 ANT_HOME 设置成 ANT 工具的安装路径。
-
输入以下命令来启动 GlassFish v3 应用服务器: <GF_HOME>/bin/asadmin start_domain
-
输入以下命令来启动与 GlassFish v3 打包在一起的 Derby 数据库: <GF_HOME>/bin/asadmin start-database
-
下载并解压缩示例应用 DSD.zip
-
改变路径到示例目录下,如果你安装的 GlassFish v3 要求管理密码的话,给出密码作为 config.properties 文件中的 admin.password 属性的值。
-
执行以下命令: ant all
该命令编译、组装、部署并运行应用,然后再反部署该应用。
作为响应,你应该会看到各种数据源的输出,以下是你应该会见到的名为java:app/env/Servlet_DataSource的数据源的输出:
[exec] Mode : appclient [exec] Data-Source name : java:app/env/Servlet_DataSource [exec] ---------------------------------------------------------------------------------------------------------- [exec] REQUESTED DATA SOURCE : java:app/env/Servlet_DataSource [exec] ACTUAL DATA SOURCE USED : java:app/env/Servlet_DataSource [exec] RESULT : DATASOURCE ACCESSIBLE [exec] ---------------------------------------------------------------------------------------------------------- [exec] [exec] Mode : servlet [exec] Data-Source name : java:app/env/Servlet_DataSource [exec] ---------------------------------------------------------------------------------------------------------- [exec] REQUESTED DATA SOURCE : java:app/env/Servlet_DataSource [exec] ACTUAL DATA SOURCE USED : java:app/env/Servlet_DataSource [exec] RESULT : DATASOURCE ACCESSIBLE [exec] ---------------------------------------------------------------------------------------------------------- [exec] [exec] Mode : stateful_ejb [exec] Data-Source name : java:app/env/Servlet_DataSource [exec] ---------------------------------------------------------------------------------------------------------- [exec] REQUESTED DATA SOURCE : java:app/env/Servlet_DataSource [exec] ACTUAL DATA SOURCE USED : java:app/env/Servlet_DataSource [exec] RESULT : DATASOURCE ACCESSIBLE [exec] ---------------------------------------------------------------------------------------------------------- [exec] [exec] Mode : stateless_ejb [exec] Data-Source name : java:app/env/Servlet_DataSource [exec] ---------------------------------------------------------------------------------------------------------- [exec] REQUESTED DATA SOURCE : java:app/env/Servlet_DataSource [exec] ACTUAL DATA SOURCE USED : java:app/env/Servlet_DataSource [exec] RESULT : DATASOURCE ACCESSIBLE [exec] ----------------------------------------------------------------------------------------------------------
在上面的输出中,RESULT一行中的DATASOURCE ACCESSIBLE表明名为java:app/env/Servlet_DataSource的数据源在所有的组件中都是可用的,也就是可用于EJB、servelet和应用的客户端组件中。
同样的,以下名为java:module/env/HelloEJB_DataSource的输出内容看起来应该类似这个样子:
[exec] Mode : appclient [exec] Data-Source name : java:module/env/HelloEJB_DataSource [exec] ---------------------------------------------------------------------------------------------------------- [exec] REQUESTED DATA SOURCE : java:module/env/HelloEJB_DataSource [exec] ACTUAL DATA SOURCE USED : java:global/default-datasource [exec] RESULT : DATASOURCE NOT ACCESSIBLE [exec] ---------------------------------------------------------------------------------------------------------- [exec] [exec] Mode : servlet [exec] Data-Source name : java:module/env/HelloEJB_DataSource [exec] ---------------------------------------------------------------------------------------------------------- [exec] REQUESTED DATA SOURCE : java:module/env/HelloEJB_DataSource [exec] ACTUAL DATA SOURCE USED : java:global/default-datasource [exec] RESULT : DATASOURCE NOT ACCESSIBLE [exec] ---------------------------------------------------------------------------------------------------------- [exec] [exec] Mode : stateful_ejb [exec] Data-Source name : java:module/env/HelloEJB_DataSource [exec] ---------------------------------------------------------------------------------------------------------- [exec] REQUESTED DATA SOURCE : java:module/env/HelloEJB_DataSource [exec] ACTUAL DATA SOURCE USED : java:module/env/HelloEJB_DataSource [exec] RESULT : DATASOURCE ACCESSIBLE [exec] ---------------------------------------------------------------------------------------------------------- [exec] [exec] Mode : stateless_ejb [exec] Data-Source name : java:module/env/HelloEJB_DataSource [exec] ---------------------------------------------------------------------------------------------------------- [exec] REQUESTED DATA SOURCE : java:module/env/HelloEJB_DataSource [exec] ACTUAL DATA SOURCE USED : java:module/env/HelloEJB_DataSource [exec] RESULT : DATASOURCE ACCESSIBLE [exec] ----------------------------------------------------------------------------------------------------------
在这里,RESULT一行表明名为java:module/env/HelloEJB_DataSource的数据源只可用于定义在ejb-jar.xml文件中EJB组件,也就是说,只可用于HelloEJB和HelloStatefulEJB。
可以注意到,示例应用做了获取资源访问的查找,不过,它也可以以如下方式来注入数据源:
@Stateless
public class HelloEJB implements Hello {
@Resource(lookup = "java:app/env/HelloEJB_DataSource")
private DataSource app;
...
...
}
另外请注意,除了前面提到的那些之外,示例应用还定义了许多的数据源,要运行应用使用这些额外的数据源的话,在示例目录下找到build.xml文件,把注释了的target=”runclient”调用的注释符号去掉。
更进一步的阅读资料
如需了解更多关于Java EE 6中的DataSource资源的定义,请参阅Java EE 6规范中的EE.5.17一节“DataSource Resource Definition”。
关于作者
Jagadish Ramu是GlassFish应用服务器团队中的一名工程师,他的工作领域是JDBC、连接池以及连接器,从2005年中期开始其加入了Sun的连接器团队,Jagadish拥有印度Pilani的博拉理工学院(BITS)的M.Tech学位。