JDBC控件是Beehive1.0中提供的访问JDBC数据源的Java控件。通过继承JDBC控件,我们根本无须关注访问数据库资源的通讯细节,只需要定义自己的业务方法,增加相应的注释来描述该继承子类,我们就可以非常轻松的实现企业应用中JDBC数据源的访问。
JDBC控件的所有注释在org.apache.beehive.controls.system.jdbc.JdbcControl接口中定义,Beehive编译器在编译时将检查这些注释是否使用正确。
本节中首先将通过一个简单的例子来演示如何通过继承JDBC控件来访问JDBC数据源,完成一个数据表的增加、删除、修改、查询等业务逻辑,随后将介绍如何使用JDBC控件访问JDBC数据源的更多技巧。
本文中所有例子的源代码可以通过下载资源区的链接下载。
本节中所有演示例子对应的JDBC数据源均为MySQL数据库。
第一个JDBC控件的例子
(本文来源于图老师网站,更多请访问http://m.tulaoshi.com/webkaifa/)接下来的这段例子将演示如何通过继承JDBC控件来完成JDBC数据源的访问,实现JDBC数据源中一个数据表中记录增加、删除、修改和查询的业务逻辑。
在演示例子中,为了保证演示的简洁,我们使用JDBC直接连接到数据库,而没有使用通常访问数据库所采用的DataSource技术。
建立数据表
在本地MySQL数据库的Demo数据库实例中按照如下DDL建立相应的数据表。
create table demo(
id int(5) primary key auto_increment,
name varchar(20) not null default '',
value varchar(20) not null default ''
);
建立表征Demo对象的JavaBean
创建新的应用目录,然后创建表征Demo对象的JavaBean
清单1 srcorgvivianjeehivecontrolsexamplescontrolseans
Demo.java
1. package org.vivianj.beehive.controls.examples.beans;
2.
3.
4. /**
5. * Demo 用于表征Beehive JDBC控件例子中的Demo对象
6. */
7. public class Demo implements java.io.Serializable{
8. private int id;
9.
10. private String name;
11.
12. private String value;
13.
14. public int getId() {
15. return id;
16. }
17.
18. public void setId(int id) {
19. this.id = id;
20. }
21.
22. public String getName() {
23. return name;
24. }
25.
26. public void setName(String name) {
27. this.name = name;
28. }
29.
30. public String getValue() {
31. return value;
32. }
33.
34. public void setValue(String value) {
35. this.value = value;
36. }
37.
38. }
继承JDBC控件,增加自己的业务方法
清单2 srcorgvivianjeehivecontrolsexamplescontrols
DemoMySQLControl.java
1. package org.vivianj.beehive.controls.examples.controls;
2.
3. import org.apache.beehive.controls.api.bean.ControlExtension;
4. import org.apache.beehive.controls.system.jdbc.JdbcControl;
5. import org.vivianj.beehive.controls.examples.beans.Demo;
6.
7. /**
8. * DemoMySQLControl 用于封装访问MySQL数据库中的Demo数据
9. 表的所有业务逻辑
10. * 包括新增、删除、修改、根据id查找对应的记录、查找所有Demo
11. 数据表中的记录、根据条件查找所有数据表中的记录
12. * 数据库访问时根据参数从DriverManager中获取数据库连接
13. */
14.
15. @ControlExtension
16. @JdbcControl.ConnectionDriver(
17. databaseDriverClass = "org.gjt.mm.mysql.Driver",
18. databaseURL = "jdbc:mysql://localhost/estore ",
19. userName = “root”, password = “root”)
20. public interface DemoMySQLControl extends JdbcControl {
21. /**
22. * 向数据表demo中增加新的记录
23. *
24. * @param demo
25. * 新增加的Demo对象
26. */
27. @SQL(statement = "insert into demo(name,value)
28. values({demo.name},{demo.value})")
29. public void createDemo(Demo demo);
30.
31. /**
32. * 修改数据表demo中demo.id对应记录的name和value信息
33. *
34. * @param demo
35. * 被修改的Demo对象
36. */
37. @SQL(statement = "update demo set name={demo.name},
38. value={demo.value} where id={demo.id}")
39. public void updateDemo(Demo demo);
40.
41. /**
42. * 删除数据表demo中demoId对应的记录
43. *
44. * @param demoId
45. * 被删除的Demo对象的id属性
46. */
47. @SQL(statement = "delete from demo where id={demoId}")
48. public void deleteDemo(int demoId);
49.
50. /**
51. * 根据demoId查找Demo数据库中对应的记录,返回对应的
52. Demo对象
53. *
54. * @param demoId
55. * 查找Demo对象的id属性
56. * @return id属性为demoId的记录
57. */
58. @SQL(statement = "select id,name,value from demo
59. where id={demoId}")
60. public Demo getDemoById(int demoId);
61.
62. /**
63. * 返回Demo数据表中所有记录集合
64. *
65. * @return Demo数据表中所有记录集合
66. */
67. @SQL(statement = "select id,name,value from demo")
68. public Demo[] getDemos();
69.
70. /**
71. *
72. * @param name
73. * @return
74. */
75. @SQL(statement = "select id,name,value from demo
76. where name like {sql: name}")
77. public Demo[] getDemosFilterByName(String name);
78. }
控件调用
我们可以编写DemoMySQLControl控件的测试类,然后通过如下方式来完成控件中业务方法的调用。
在测试类中采用声明式控件实例化方式来实例化DemoMySQLControl控件。
·@Control
DemoMySQLControl _control;
调用控件的相关业务方法来实现业务逻辑。
下面的代码段可以完成Demo对象的新增功能。
Demo demo = new Demo();
demo.setName(“name”);
demo.setValue(“value”);
_control.createDemo(demo);
实例分析
从上面的例子中我们可以看到,通过继承JDBC控件来完成JDBC数据源的访问是一件非常简单的事情,开发者需要完成的工作被简化为两项:编写表征访问对象的JavaBean和使用声明完成业务方法和逻辑的实现,这样的开发过程大大的简化了开发者的工作,极大地减少了开发者的代码编写量。
下面我们详细的分析一下继承JDBC控件的子类DemoMySQLControl:
15. @ControlExtension
在程序的第15行,我们使用@ControlExtension来注释将要创建的这个接口继承了另外的控件接口
16.@JdbcControl.ConnectionDriver(
17.databaseDriverClass = “org.gjt.mm.mysql.Driver”,
18.databaseURL = "jdbc:mysql://localhost/estore ",
19.userName = “root”, password = “king”)
在程序的第16~19行我们使用@JdbcControl.ConnectionDriver注释来为将要创建的接口指定一些需要传递的参数,这里主要是一些数据库访问时所需要的JDBC驱动类、JDBC访问URL、数据库访问用户名和密码等必要信息。
20.public interface DemoMySQLControl extends JdbcControl { 在程序的第20行使用extends关键字表示将要创建的接口DemoMySQLControl和JdbcControl接口之间的继承关系
27.@SQL(statement = "insert into demo(name,value)
28.values({demo.name},{demo.value})")
在程序的第27~28行使用@SQL关键字注释接下来定义的这个业务方法所需要执行的业务逻辑,其中的{}表示对传入参数的调用,{}包含的内容表示了需要处理的参数,这些参数的表示方法遵循面向对象的属性调用方式,JDBC控件的解析器会将这些代码转化为对应的getter方法调用。
29.public void createDemo(Demo demo);
在程序的第29行,采用常用的接口方法声明了该控件的一个访问接口。
后面的代码采用和27~29行相应的原理定义了其它需要实现的业务方法。需要注意的是,定义查询类方法时,如果只需要返回一条记录,目前通常的做法是返回一个Demo对象。如果返回的记录有超过一条的可能性,返回的是符合查询条件的Demo对象数组、List对象等,请参考《JDBC控件返回类型》部分的内容。
前面的内容中,我们已经通过继承JDBC控件完成了访问数据库资源的全部内容,现在你可以编写单元测试代码来测试控件是否符合要求了。请大家参考《控件入门》中的步骤完成JDBC控件的编译和单元测试工作。
到现在为止,我们已经知道了如何使用JDBC控件和注释来访问数据库资源的基本步骤和方法,但是还没有学习如何使用注释来让JDBC控件和业务内容的结合起来。这部分的内容分作两部分进行介绍:一是如何通过注释定制JDBC控件,提供访问数据库的信息(比如数据库访问方式、URL、用户名和密码等),一是如何确定每次业务方法执行的返回对象。
使用注释订制JDBC控件
本节中的内容将给大家介绍如何使用JDBC控件提供的注释根据不同的环境和要求来订制JDBC控件,完成JDBC数据源的访问和业务逻辑的封装。
JDBC控件支持的四个注释:ConnectionDataSource、ConnectionDriver、ConnectionOptions、SQL。四个注释中ConnectionDataSource、ConnectionDriver、ConnectionOptions是用于类级别的注释,用于描述JDBC控件继承类,SQL注释是方法级别的注释,用于描述JDBC继承类中的业务方法。
使用ConnectionDataSource注释可以指定JDBC控件访问数据库时根据采用DataSource方式获取数据库连接。使用ConnectionDriver注释可以指定JDBC控件访问数据库时使用指定的参数通过DriverManager获取数据库连接。ConnectionOptions注释描述了数据库访问时的一些扩展特性,比如数据库访问时是否使用READONLY优化(仍然可以进行UPDATE操作)等。
下面开始详细的介绍这些注释,并且通过提供简单的代码段来说明如何使用这些注释来配置JDBC控件的继承类。
ConnectionDriver注释
ConnectionDriver用于注释JDBC控件的继承类,控制JDBC控件在访问数据库的时候通过DriverManager获取数据库连接。ConnectionDriver注释支持5个参数:databaseDriverClass、databaseURL、userName、password、properties。
·databaseDriverClass
String类型,用于设定数据库驱动类的全名。使用ConnectionDriver注释时必须设置的参数。
比如访问MySQL数据库,可以使用databaseDriverClass= “org.gjt.mm.mysql.Driver”,访问Oracle数据库的时候可以使用databaseDriverClass= “oracle.jdbc.driver.OracleDriver”
·databaseURL
String类型,用于设定数据库访问的JDBC URL,使用ConnectionDriver注释时必须设置的参数。
比如访问MySQL可以使用databaseURL=“ jdbc:mysql://localhost/estore”,访问Oracle数据库时可以使用databaseURL=“ jdbc:oracle:thin:@localhost:1521:www”
·userName
String类型,设置访问数据库时使用的用户名,可选参数。
·password
String类型,设置访问数据库时使用的密码,可选参数。
·properties
String类型,设置访问数据库时的一些其它参数,可以一次性传递多个参数,两个参数之间用逗号隔开。
在《第一个JDBC控件的例子》中,我们提供的代码已经演示了如何使用ConnectionDriver注释JDBC控件,请参见 清单2 。
ConnectionDriver注释的JDBC控件运行时,必须保证databaseDriverClass中指定的数据库驱动类位于classpath中,否则控件运行时将抛出违例。
ConnectionDataSource注释
ConnectionDataSource注释用于指定JDBC控件在访问数据库时通过DataSource获取数据库连接,它有两个属性:jndiName和jndiContextFactory。
·jndiName
String类型,用于设置DataSource在目标容器内的jndiName属性
·jndiContextFactory
Class类型,用于设置数据库访问时可以获取jndi上下文环境的类,被传入的参数类必须继承org.apache.beehive.controls.system.jdbc.JdbcControl. JndiContextFactory类,并且覆盖JndiContextFactory类的getContext()方法,getContext()将获取访问目标容器的上下文环境(Context)。
下面的章节中我们将看到如何通过JDBC控件和ConnectionDataSource注释访问Tomcat服务器,获取jndiName为jndi/samplesDataSource的DataSource资源,该DataSource连接到MySQL数据库的Demo实例,Web应用部署在Tomcat环境中。
修改Tomct配置文件,创建DataSource资源
·如何创建Tomcat环境下的DataSource资源请参考Tomcat的帮助文档。
修改Web应用的配置文件web.xml,增加如下内容,增加对资源的引用
Datasource example
jndi/samplesDataSource
javax.sql.DataSource
Container
·创建访问Tomcat服务器Jndi上下文的类JndiContextFactory
JndiContextFactory类继承自JdbcControl.JndiContextFactory父类,覆盖父类中的抽象方法----getContext(),清单3 中显示了JndiContextFactory类定义和实现的完整代码。
清单3 src orgvivianjeehivecontrolsexamplescontrols
jndicontextJndiContextFactory.java
1. package org.vivianj.beehive.controls.examples.controls.jndicontext;
2.
3. import java.util.Hashtable;
4.
5. import javax.naming.Context;
6. import javax.naming.InitialContext;
7.
8. import org.apache.beehive.controls.system.jdbc.JdbcControl;
9.
10. /**
11. * JndiContextFactory 用于从Tomcat服务器中获取访问JNDI内容的上下文环境
12. */
13. public class JndiContextFactory extends JdbcControl.JndiContextFactory {
14. /**
15. * 获得本地Tomcat服务器中访问JNDI内容的上下文环境
16. *
17. */
18. public Context getContext() {
19.
20. Context ctx = null;
21.
22. try {
23. ctx = (Context)(new InitialContext().lookup("java:/comp/env"));
24. } catch (Exception e) {
25.
26. }
27.
28. return ctx;
29. }
30. }
我们可以使用ConnectionDataSource注释和新创建的JndiContextFactory类来注释JDBC控件了。
下面的代码片断就是一个简单的例子,使用这段代码注释的JDBC控件,调用任何业务方法时,将通过本地Tomcat服务器上、JNDI名为“jndi/ samplesDataSource”的DataSource资源获取数据库连接。
@JdbcControl.ConnectionDataSource(
jndiName = "jndi/samplesDataSource",
jndiContextFactory=JndiContextFactory.class)
ConnectionOptions注释
ConnectionOptions可以用于定义访问数据库时的一些扩展特性,可以选择性的和ConnectionDataSource或者ConnectionDriver一起使用。ConnectionOptions支持三个参数:readOnly、resultSetHoldability、typeMappers。
·readOnly
boolean类型,如果设置为true,数据库连接将优化为ReadOnly的访问方式,但是仍然可以进行更新和删除操作。默认为false。
·resultSetHoldability
org.apache.beehive.controls.system.jdbc.JdbcControl.HoldabilityType类型,可以选择为HOLD_CURSORS_OVER_COMMIT或者CLOSE_CURSORS_AT_COMMIT,用于设置ResultSet指针的关闭策略,默认值是CLOSE_CURSORS_AT_COMMIT,表示在每次commit之后关闭ResulSet对象指针。
·typeMappers
org.apache.beehive.controls.system.jdbc.JdbcControl.TypeMapper数组类型。设置SQL自定义类型和Java类型之间的映射。TypeMappers类中指定的Java类型必须实现java.sql.SQLData接口
SQL注释
SQL注释用于描述JDBC控件继承类中定义的方法,主要设置被注释方法调用时需要执行的SQL语句和其它功能。我们可以回头看一看清单2,里面的每一个业务方法之前均有@SQL注释。
SQL注释支持很多属性设置,包括statement、arrayMaxLength等,其中最重要的就是statemet,本节中我们讨论statement属性的设置,其他属性的设置方法请大家参考Beehive的帮助文档,在大多数的情况下最好不要设置这些属性,因为里面可能用到JDBC3.0规范中的新特性,而现在很多数据库驱动并不支持这些特性。
statement内容描述了我们需要完成的业务逻辑,在statement中我们可以使用变量的方式访问被注释方法中传递进来的参数,JDBC控件的解析器在运行时保证这些参数的正确传递。
statement编写规则
SQL注释的statement属性提供了被注释方法调用时执行的SQL语句的内容,SQL语言中可以使用JdbcControl提供的特殊语法直接访问Java对象或者它的属性。编写statement必须满足以下条件。
·statement中的sql语句编写方式和要求参考目标数据库的sql语句编写方式和要求。
·statement中使用{…}声明变量来获取业务方法调用时所传递参数或者它的属性。
·statement中变量声明的第一个字符和最后一个字符不能是空格,也就是说不能出现{ a}或者{a }这样的情况,否则可能执行结果和您期望的会有很大的出入
·{}中间的内容是大小写敏感的。如果传入的参数名为a,而{}中引用为A,他们之间将无法匹配。
·{}中间的内容可以是参数名(传入的参数是Java基本数据类型时适用),或者是参数对象的某个成员变量(传入的参数是Java类对象时)。
·{}中引用Java对象的字段时,必须保证该字段符合以下条件之一,否则Java控件调用时将会返回一个违例:
* 该字段被public关键字声明为公共字段,可以使用 对象名.字段名 直接访问
* 该字段被private关键字声明为私有字段,但是提供了公共的getter方法,可以通过 对象名.get属性名 方式访问。
* 该Java对象继承了java.util.Map接口,可以通过 对象名.get(“属性名”)方式访问
statement中引用业务方法中传递的参数时必须保证这些参数和对应的数据表的字段类型保持一致,不能出现数据表的字段是NUMBER类型,而对应的参数却是String类型。
当业务方法传递的日期或者时间类型的参数需要在statement中被引用时,请保证这些传递的日期和时间使用的是java.sql包中的对应类,比如我们应该使用java.sql.Date而不要使用java.util.Date来传递日期类型的变量给statement
statement中不能出现单引号。如果确实需要使用单引号,必须采用特别的方法解决。
statement创建实例
·不从业务方法获取参数
下面的业务方法声明可以从demo表中取出所有所有记录,返回一个Demo对象的数组。
@SQL(statement=“select * from demo” )
Public Demo[] getDemos();
·从业务方法中获取基本类型参数
下面的statement中,我们使用{demoId}变量从getDemoById()方法调用中获取传入的参数demoId。
@SQL(statement=“select * from demo where id={demoId}” )
Public Demo[] getDemoById(int demoId);
·从业务方法中获取Java对象类型参数
下面的statement中,我们使用{demo.name}变量表示从createDemo ()方法调用中获取传入的Java类对象demo的name属性的内容。
@SQL(statement = “insert into demo(name,value)
values({demo.name},{demo.value})”)
public void createDemo(Demo demo);
·从业务方法中获取完整SQL语句
有些时候,SQL语句可能需要动态生成,这时候statement默认的方式无法帮开发者完成这个过程,而是需要在Java代码中完成SQL语句的组装后传递给业务方法,这种情况下我们可以使用{sql: …}的方式向statement中传递一个完整的sql语句。
@SQL(statement=“{sql: totalsql}” )
Public Demo[] getDemos(String totalsql);
·从业务方法中获取SQL语句片断
statement还支持某一段SQL语句从业务方法的参数中获取,比如下面的这个例子中,只有最后的{sql: name}