首页
会员中心
到顶部
到尾部
计算机

JDBC与JSP研究

时间:2020/10/14 13:43:36  作者:  来源:  查看:0  评论:0
内容摘要: select hitCount from counters where page like '<%=request.getRequestURI()%>' </sql:query> <%-- process only th...

select hitCount from counters where page like '<%=request.getRequestURI()%>'

</sql:query>

<%-- process only the first row of the query --%>

<sql:resultSet id="rset2" loop="false">

<sql:getColumn position="1"/>

</sql:resultSet>

</sql:statement>

times. The page counter is implemented using the Jakarta Project's

DBTags tag library, calling JDBC indirectly.</P>

<%-- close a database connection --%>

<sql:closeConnection conn="conn1"/>

</BODY>

</HTML>

我不了解你,但我有一点失望。那看起来比上面那个scriplet例子好要不清晰,并且我不知道是否有HTML设计者会因它而高兴,等等。那里出错了呢?毕竟,我们遵守了人们的建议:我们除去了scriplet,代替为定制标签。

JDBC与JSP研究

发展定制标签库是相当直接的,但是它确实要耗费一些时间和精力。我经常推介标签作者首先用scriptlets建立标签行为的圆型,接下来才把这些scriptlets转换为标签。一个可选的方法是用Allaire的JRun Server Tags(JST),它能使每个你的原始标签库作为一个JSP页面。JST可以在运行时可以转换这些页面为标签,因此,JST技术很明显地服务于客户端页面。尽管Allaire警告说“建立JST的目的是可移植技术,以便所有J2EE社团的成员都能平衡他们的利益”,但JST现在仅仅在JRun中可用。时间将会证明JST是否会成为开发标记更通用的工具。与此同时,我们发现scriptlet为开发标记的业务逻辑奠定了良好的基础;逻辑经调试后,我们把它迁移成为标记处理程序类。

他们没有告诉你关于标签库的是:标签设计是语言设

计。大多数标签库都是在约定的时间内,由程序员写给程序员的;这些标签的语义连接给其它的程序员。此外,还记得模型和描述的分离吗?DBTags并不是很好的支持这种分离。Sql:getColumn标签和jsp:getProperty事件很相似。它将DBTags结果直接发到输出流中。这使得很难在描述想要的表的输出流时不用DBTags。最后,注意在Listing 3和Listing 4的例子中的逻辑的不同。DBTags执行来自JDBC的更新语句的标签表示形式。只有查询才可以被找回。这意味着我们知道有多少行被更新语句所更新。因此,我们转换Update和insert语句;我们常常尝试插入一个新的记录,强迫它忽略任何错误,并在接下来执行更新。

公平地看待DBTags标签库,他并不是一个对程序员有害的标签库。除开对更新量的不足,这代码提供了一个相当优秀的JDBC计划。潜藏的问题人仍然存在;标签提供的功能少于直接转换JDBC包。不同于隐藏某些异常处理,标签库不真的提供任何从scriptlets的提取。他确实不帮助从功能中分离描述。

因此,真正的问题不在于是使用scriptlet还是使用标记;这种问题不是分离功能和表示的问题的原因,而是结果。解决方案是要以适当的说明级别上为表示页面的作者提供更高级别的功能。认为标记优于scriptlet的原因在于:根据定义,scriptlet 是编程,而标记可以表示高级概念。

2.5 对表示页面隐藏JDBC

将JDBC同JSP技术集成时,我们希望对表示作者尽可能多的隐藏该集成。在我们显现数据库概念的地方,我们希望在恰当的抽象级别上显现这些概念。这一方法引出了我们的下一个示例。

在清单5中的示例中,我们对表示页面隐藏了JDBC集成。(这个页面的 实际版本位于JavaServer Pages Developers Guide Web站点)

清单5 隐藏了JDBC的JSP页面

<jsp:directive.include file="/pagelets/hitCounter.jsp" />

<HTML>

<HEAD>

<TITLE>JDBC hidden example</TITLE>

</HEAD>

<BODY>

<P>This page has been hit

<jsp:getProperty name="hitCounter" property="int" />

times. The page counter is implemented indirectly: a JavaBeans component containing the

hit count is inserted into the environment and referenced within the page using

the JSP getProperty action. The JSP page doesn't have any exposure to JDBC.</P>

</BODY>

</HTML>

被包含的hitCounter.jsp文件负责设置环境。内容可以是scriptlet、标记或只是一个taglib伪指令;只要是为表示页面建立预期环境的都可以作为内容。如果您愿意,您可以把getProperty操作替换成定制标记;例如:

This page has been hit

<page:hitcounter />

times.

如前所述,这些点击计数器示例纯粹用作说明;每个页面都执行这样的数据库操作将会是不必要的开销。上面这个示例说明事实上您希望以何种方式显现点击计数器。通过把它隐藏在定制标记里,我们就把该实现彻底隐藏了起来。现在我们可以聚集点击计数运行时信息,并周期性的更新数据库(比如,在每次会话结束时)。甚至连存储方式(数据库或是别的)也对表示页面作者隐藏了。这正是我们在DevTech实现点击计数器的方式:我们利用bean类实现点击计数器模式行为。标记把该行为结合到了我们的页面里。

2.6 集成JavaBean组件

到目前为止,示例一直都相当简单,但大多数数据库操作都比这些简单的查询和更新要复杂。因此,既然我们已经讨论了把JDBC用于JSP页面的一些基本原则,就让我们用一个略微复杂些但肯定更加通用的一类应用来结束这篇文章。

这一部分的示例(下面清单9)将指出一种方法以支持Web站点上访问者提供的内容。换句话说,我们希望允许访问者读取与某一URI关联的数据库内容并撰写额外的内容。这样的内容在现在的Web站点上相当常见。同样的基本部分可用于构造:

· 评论页面,例如在Amazon.com找到的那些页面

· 链接页面

· 公告牌

· Wikiweb

通过由不同技术背景的设计人员编写,本例中JSP组件稍微再精细些的版本就可以实现看上去截然不同的Web页面。这些页面看上去的唯一的相同之处就是用于访问者撰写内容。

我们的annotation(注释)示例使用HTML表单。HTML表单用于JSP时,使用属性映射到表单域的bean变得方便了。这使setProperty标记可以变戏法:

清单6 映射到表单的Bean实例

<%-- setup a bean instance that matches our form --%>

<jsp:useBean id="instance-name" class="bean-class" ... />

<%-- set all bean properties that match a form field --%>

<jsp:setProperty name="instance-name" property="*" />

JDBC与JSP研究

映射bean和ResultSet

示例使用com.devtech.sql以使说明简洁。示例使用Java Reflection和Introspection,依靠列名及属性名提供JDBC数据和bean属性间的映射。您可以把DevTech包替换成自己的代码。

集成JavaBean组件是JSP技术设计方面的一个过人之处。不幸的是bean和JDBC之间的集成根本不是无缝的,因此我们针对DevTech的JDBC工作开发了一个包,它不仅提供了bean和JDBC的集成,而且还有必要的异常处理,从而使程序员不必应付这些细节。

annotation(注释)示例使用com.devtech.sql包中的两种查询和更新方法。这里用到的查询方法传递一个bean类、一个SQL查询和一个Object数组填充查询中的占位符。在这种情况下,仅有的占位符是给页面的URL的。结果是一个数据库游标对象,它必须是一种迭代器。

清单7 数据库游标对象

dataBase.queryCursor(AnnotationDBBean.class, new String[] { URL },

"select page, author, annotation, DATE_FORMAT(whenPosted, '%W %d%b%y %T')" +

" as whenPosted from annotations where page like ?");

这个查询方法的有趣之处在于指定类型的bean将会为您实例化,只要bean属性名同ResultSet中的列名相对应,属性值就会被自动设置。每次您用游标选择下一行时,bean的属性就会根据ResultSet被自动设置。

用到的这种特殊更新方法有一个bean实例、一个String数组及一条update语句作为参数。String数组值规定了用来填充更新中的占位符的预期bean属性。在这种情况下,page、author和annotation属性是根据bean选择的。

清单8 更新方法

int count = dataBase.update(annotationBean,

new String[] { "page", "author", "annotation" },

"insert into annotations(page, author, annotation) values(?, ?, ?)");

我们的JSP页面示例annotations.jsp如清单9中所示。高亮的部分指出两个可以用定制标记替换的scriptlet,如清单 10 中所示。一些为页面设计者提供帮助的JSP组件、把动态内容放到页面上去的getProperty操作以及标准HTML组成了页面的剩余部分。使用JSP注释的原因是因为JSP注释是私有的,不会出现在输出流中。

清单9 annotation(注释)的JSP页面

<jsp:directive.include file="/pagelets/annotate.jsp" />

<%--

By the time we arrive here, the annotation bean has been established, and if the

form is submitted, the contents will be posted to the database. The page

property is initialized. If the author is known during this session, that property

is also initialized.

Bean:"annotation"

Properties:String page;

String author;

String annotation;

String whenPosted;

Access to any bean property follows the format:

<jsp:getProperty name="annotation" property="property-name" />

-%>

<HTML>

<HEAD>

<TITLE>Comments for <jsp:getProperty name="annotation" property="page" /></TITLE>

</HEAD>

<BODY>

<p align="left"><font size="+1">

Comments for <i><jsp:getProperty name="annotation" property="page" /></i>

</font>.</p>

<CENTER><HR WIDTH="100%"></CENTER>

<!-- Annotation Submission Form -->

<FORM method="POST">

<TABLE>

<TR>

<TH align="left">Name:</TH>

<TD><INPUT type=text name=author size=50 maxlength=60

value="<jsp:getProperty name="annotation" property="author" />"> </TD>

</TR>

<TR>

<TH valign="top" align="left">Note:</TH>

<TD><TEXTAREA name=annotation cols=40 rows=5 wrap=virtual>

<jsp:getProperty name="annotation" property="annotation" /></TEXTAREA></TD>

</TR>

<TR>

<TD align="center" colspan="2"><INPUT type=submit value="Add Comment"></TD>

</TR>

</TABLE>

</FORM>

<!-- End of Annotation Submission Form -->

<!-- beginning of annotations -->

<%--

The following section iterates through all annotations in the database for the

requested page. To change the look of the page, just change anything in the

demarcated area.

--%>

<jsp:scriptlet>

Database.Cursor annotations = annotation.getCursor();

while (annotations.next(annotation) != null)

{

</jsp:scriptlet>

<%-- beginning of annotation change area --%>

<CENTER><HR WIDTH="100%"></CENTER>

From: <jsp:getProperty name="annotation" property="author" /></A>

at <jsp:getProperty name="annotation" property="whenPosted" /><BR>

<jsp:getProperty name="annotation" property="annotation" /><BR>

<%-- end of annotation change area --%>

<jsp:scriptlet>

}

annotations.close();

</jsp:scriptlet>

<!-- end of annotations -->

</BODY>

</HTML>

定制标记等价程序清楚,但不提供信息:

清单10 定制标记等价程序

<sql:results queryName="annotations" bean="annotation">

<CENTER><HR WIDTH="100%"></CENTER>

From: <jsp:getProperty name="annotation" property="author" /></A>

at <jsp:getProperty name="annotation" property="whenPosted" /><BR>

<jsp:getProperty name="annotation" property="annotation" /><BR>

</sql:results>

我们在这个示例中使用scriptlet只是要让您(一位程序员)知道正在发生的事情。如果用陈述性的标记替换scriptlet,那么页面设计者会很清楚,但您却一头雾水。

逻辑并不复杂。annotation.getCursor()调用能得到同服务器的连接、发出查询并在结果集建立一个数据库游标对象annotations。每次调用annotations.next()时,从结果集中取出一个新行,其中的值移到bean里,方法返回的正是这个bean的引用。所用的这个特殊的next()方法使用一个要植入的bean参数。虽然我们本可以使游标为每一行实例化一个新的bean,但反复使用一个bean更加高效。

请注意真正的查询及更新都没有在表示页面中出现。被包括的页面设置表示页面环境,也包括setProperty和update操作。这些操作独立于表示页面;只有注释bean的属性所包含的contract是重要的。这遵守了将表示与模式行为相分离的策略。页面设计者完全有能力更改表示是如何呈现的,但无从得知数据库是如何集成的。如果更改在数据库更新或查询时生效,就应当由JSP程序员负责。

2.7 总结

本文对结合使用Java Server Page、JavaBean和JDBC技术并通过关系数据库生成动态内容的进行了总结性介绍。我们从对于JSP程序员新手来说最显而易见的方法(scriptlet)开始。我们看到不加控制的使用scriptlet是如何使逻辑和表示纠缠在一起的,使两者都难以维护。我们还看到标记库并不一定会改善 MVC 分离,如果用编程术语来表示标记,则页面设计人员可能无法理解使用这样的标记的页面。最后,我们看了更为复杂的示例,这些示例说明了几种使数据库访问与内容表示分离的途径。

现在您应该对在对页面设计人员隐藏真实的数据库访问的同时如何把数据库内容集成到一个Web站点中有一些基本概念。还请注意,对您(一个程序员)而言,几乎不含信息的那些示例是最适合于页面设计人员的示例。当您为JSP解决方案制定计划时,千万要为页面设计人员设身处地地想一想。

译文原文出处:

1、 http://www-db.stanford.edu/~ullman/fcdb/oracle/or-jdbc.html

2、 http://www-128.ibm.com/developerworks/java/library/j-webdata/

,

JDBC与JSP研究

1JDBC入门

本文阐述了JDBC(数据库连接)API(应用程序接口)的基本内容。在这里,你将学到用基本的JDBC API去创建表单,插入数值,查询表单,检索结果,修改表单,创建动态语句,完成事务,捕捉异常和错误。

本文来自SUN公司关于JDBC基础内容的官方指南。

--概述

--建立连接

--第一个JDBC语句

--精制JDBC语句

--执行DDL语句

--执行查询语句

--结果集访问说明

--事务处理

--异常和错误处理

--简单的代码和编辑规则

1.1 概述

JDBC的访问级接口是提供了SQL数据库操作和更新操作的编程接口。他们通过带数据库接口的库程序允许在普通的程序中调用SQL。特别的,Java提供了很多非常简单的基于JDBC的接口。

这里有一条简单的方法可也看到在访问级发生了什么:你正在写一个简单的Java程序。在程序的某处,你需要和数据库交换数据。用标准的库程序,你就可也打开一个数据库的连接。接下来,用JDBC来发送SQL代码给数据库,并且加工要返回的结果。等到你结束工作,就关闭数据连接。

这样的方法需要和内嵌SQL的预编辑方法相比较。后者含有一个预编辑过程,在这个过程中内嵌的SQL语句被转化为主机语言代码(C/C++)。访问级的接口不要求预编辑过程就避免了内嵌SQL语句转化的过程。这样在增加轻便性的同时使客户—服务的关系更清晰。

1.2 建立连接

当然,最先要做的是在你的计算机上安装Java,JDBC和DBMS。由于我们想要连接Oracle数据库,我们也就需要一个支持它的驱动程序。幸运地是,我们有一个负责的主管他已经在Leland的机子上为我们做好了。

和前面说的一样,在一个数据库能被访问前,一个在程序(client)和数据库(server)之间的连接必须被打开。这包括两步:

1、 装载特定厂商的驱动程序

为什么需要这一步呢? 为了确保轻便和代码重用,API被尽可能地设计来不依赖于一个数据库的版本或厂家。由于不同DBMS拥有不同的行为,我们就需要告诉驱动管理器我们想用那种DBMS,以便它能调用正确的驱动程序。装载Oracle驱动程序可以用下面的代码:

Class.forName(“oracle.jdbc.driver.OracleDriver”)

2、 建立连接

当驱动装载完成就可以建立连接,你建立一个实际的连接可以用:

Connection  con=DriverManger.getConnection(“jdbc:oracle: thin:

@dbaprodl:1544:SHRI_PRD”,username,password );

现在,让我们看看这个语句是干什么的。第一个字符串是关于数据库的URL,它包含了协议(JDBC),厂商(oracle),驱动(thin),服务器(dbaprodl),端口号(1521),和一个服务实例(SHRI_PRD)。用户名和密码就是你过去输入SQLPLUS以进入你的帐户的用户名和密码。

这就是建立连接的步骤。最后一步返回来的是一个打开的连接,我们将用它来向数据库传递SQL语句。在上面的代码中,con是一个打开的连接,并且我们在后面还将用到它。注意:上面提到的价值在我们的环境中是用根据的。他们在别的环境中有不同的价值。

1.3 创建JDBC 语句

JDBC语句对象是用来发送你的SQL语句到DBMS的,不是与SQL语句相混淆的。JDBC语句是绑定在一个打开的连接上的,不只是一个简单的SQL语句。你能把JDBC语句对象想象成嵌在连接上的一个信息通道,它能传递一个或多个你的SQL语句(那些你要执行的)到DBMS。

一个活动的连接要求创建一个Statement对象。下面的小代码,就是用刚才的连接con,来为你实现Statement对象的:

Statement stmt = con.createStatement();

这样,一个Statement对象存在了,但是它没有向DBMS传递一个SQL语句。我们将在下一节学习它。

1.4 精制JDBC语句

有时,用精制的语句对象来向DBMS发送SQL语句能更方便或更有效。它区别于其超类Statement的主要特征是,它不像Statement,在它被创建时就被给与了一个正确的SQL语句。这个SQL语句接下来就以正确的方式发向DBMS,并且在那里得到编译。因此,精制的语句实际上是连接的一个信道,并汇编有关SQL声明.

和用普通的语句在每次用的时候都要编译一次相比,它提供的优势是,当你需要用相同的或相似的带不同参数的查询多次,语句就可以被DBMS编译和优化,而在不同参数时,不再要求编译。

PreparedStatements 也是用连接方法来创建的。下面的小代码展示了怎么创建一个有三个输入参数的带参数SQL语句:

PreparedStatements prepareUpdateprice =con. preparedStatements(

“UPDATE Sells SET price = ? where bar = ? AND beer = ?”);

在执行一个PreparedStatement前,我们需要提供参数的值。这可以调用定义在PreparedStatement类中的SET XXX方法来完成。最常用的方法是setInt,setFloat,setDouble,setString 等。你可以设置参数的值在执行准备的语句前。

继续上面的例子,我们将写:

prepareUpdatePrice.setInt(1, 3);

prepareUpdatePrice.setString(2, "Bar Of Foo");

prepareUpdatePrice.setString(3, "BudLite");

1.5 执行DDL语句

在JDBC中执行SQL语句用很多样式,他们依赖于SQL语句的目的。DDL(数据定义语言)语句,象表的创建和删除语句,还有更新表的内容的语句都是通过executeupdate方法执行的。注意到这些命令都改变数据库的状态,因此方法的名字包含”Update”。

下面的代码是executeupdate语句的例子:

Statement stmt con.createStatement();

stmt. Executeupdate(“CREATE TABLE Sells” + “(bar VARCHAR2(40),beer

VARCHAR2(40),price REAL)”);

stmt. Executeupdate(“INSERT INTO Sells ” + “VALUES (‘Bar Of

Foo’,’BudLite’,2.00)” );

String sqlString= “CREATE TABLE Bars ” + “name

VARCHAR2(40),address VARCHAR2(80),license INT”);

Stmt. Executeupdate(sqlString);

由于SQL语句不太适合在一行上显示,我们就把它打断成两个字符串,并且用加号连接起来以便它被编译。特别注意“INSERT INTO Sells”和“VALUES”之间的间隔。也注意我们更愿意重用相同的Statement而不是重新创建一个。

在执行executeUpdate时常常是调用DDL语句,返回的结果一般是0,在数据修改语句执行时将返回一个大于或等于0的值,这个数据表明了其间的关系。

当使用PreparedStatement时,我们将执行这样的一个语句,它首先要设定参数的值(象上面所见的那样),接着调用executeUpdate.

int n = prepareUpdatePrice.executeUpdate();

1.6 执行SQL语句

作为和前一节的语句相对的语句,一个查询希望得到一组记录来作为结果,并且不改变数据库的状态。自然地,这里就有相应的方法叫做executeQuery,它返回一个ResultSet对象作为其结果。

String bar,beer;

float price ;

ResultSet rs = stmt.executeQuery(“SELECT * FROM Sells”);

While( rs.next() ){

Bar = rs.getString(“bar”);

Beer = rs.getString(“beer”);

Price = rs.getFloat(“price”);

System.out.println(bar + “sells” + beer + “for” + price + “Dollars.”);

}

查询得到的结果集包含在一个变量rs中,rs是ResultSet的一个实例。这样的组对我们没有太大的作用,除非我们能访问每一行和每行中的各个属性。ResultSeta给我们提供了一个能让我们依次访问每行的游标。游标仅仅是第一行的集。每次调用next方法使它移动到下一行,如果下一行存在就返回真,如果没有剩余行就返回错。

我们可以用适当类型的getXXX方法去检索行的各个属性。 在前面的例子中,我们用了getDtring和getFloat两个方法来访问各列的值。注意到我们用想得到的值得列的列名作为方法的参数。同时也注意,VARCHAR2类型的bar,beer被转换为Java String,REAL转换为Java float.

同样地,我们可以指定列号来代替列名,也能得到相同的结果。这样,有关的语句就应该是:

Bar = rs.getString(1);

Beer = rs.getString(2);

Price = rs.getFloat(3);

当使用PreparedStatement工作时,我们应该执行一个设置好参数的查询,并且用它调用executeQuery方法。

ResultSet rs = prepareUpdatePrice.executeQuery();

1.7 结果集访问说明

JDBC提供了一系列的方法来找出你在处理结果集的那个地方,方法包括:getRow,isFirst,isBeforeFirst,isLast,isAfterLast.

他们提供可以任意访问结果集中各行的游标。默认的,游标仅仅只能向前并且是只读的。在为Connection创建Statement时,你可以改变ResultSet的类型,使它成为更方便查找和更新的模型:

Statement stmt = con.createStartment(

ResultSet.TYPE_FORWARD_ONLY,ResultSet.CONCUR_READ_ONLY);

ResultSet rs = stmt.executeQuery(“SELECT * FROM Sells”);

不同的类型选项有: TYPE_FORWARD_ONLY、TYPE_SCROLL_INSENSITIVE、和TYPE_SCROLL_SENSITIVE。你可以利用CONCUR_READ_ONLY和CONCUR_UPDATABLE选项来选择是用只读的还是可更新的游标。用默认的游标,你可以利用rs.next()来卷动游标。用可卷动的游标你可设置更多的选项:

rs.absolute(3);          // 移动到第三tuple

rs.previous();           //向后移动一个tuple(tuple 2)

rs.relative(2);          //向后移动两个tuples(tuple 4)

rs. relative(-3);        //往回移动两个tuples(tuple 1)

这里还有很多可卷动游标特征的细节。可卷动游标,虽然在某些应用中很有用,但是它非常复杂,因此在使用时要小心和克制。更多关于游标细节的操作技术可以在New Features in the JDBC 2.0 API上找到。

1.8 事务处理

JDBC允许多个SQL语句嵌入到一个单一的事务中。因而,我们用JDBC事务可以确保ACID(原子性、一致性、独立性、完整性)属性。

事务控制是通过连接对象完成的。当连接建立时,它就默认是自动提交型。这意味着每个SQL语句都被认为是一个单独得事务,并且只要它自身执行完成就马上提交。(这样并不完全精确,并且在大多数情况下,我们可能被它蒙蔽而不知道这些细节。)

我们能为连接关闭自动提交,用:

con.setAutoCommit(false);

也可以再打开,用:

con. setAutoCommit(ture);

一旦自动提交关闭,没有SQL语句能被提交(那不是说,数据库将永远得不到更新),直到你明确地调用commit()方法来告诉它提交。

Con.commit();

在提交之前的任何地方,我们都可也调用rollback()来卷回事务,并恢复数据到上一次提交的地方(在尝试更新之前)。下面是个融合了上面观点的例子。

con.setAutoCommit(false);

Statement stmt = con.createStatement();

Stmt.executeUpdate(“INSERT INTO Sells VALUES(‘bar of

foo’,’budlite’,’1.00’)”);

con.rollback();

Stmt.executeUpdate(“INSERT INTO Sells VALUES(‘bar of

joe’,’Miller’,’2.00’)”);

con.commit();

con.setAutoCommit(true);

让我们浏览这个例子理解几个方法的用法和作用。我们首先看看自动提交关闭时,接下来的几条语句需要看成一个整体。我们尝试在Sells表中插入记录(‘bar of foo’,’budlite’,’1.00’))。然而,这个操作最终并没完成(committed).当我们调用rollback,我们取消了插入。注意,Sells表现在仍和我们尝试插入之前一样。接着我们尝试插入其他记录,并且这次,我们提交了事务。现在Sells表就受到了永久的影响,并且多了一条新的记录。最后,我们重新开启了自动提交。

我们同样可以按需要地设定事务的独立性。例如,我们可以设定事务独立性为TRANSACTION_READ_COMMITTED,它不允许访问数据直到数据提交完毕,同时也不允许读脏数据。在连接的接口中提供了5个独立性级别。默认下,独立性级别是串行化 。JDBC允许查看数据库设定的独立性级别(用连接的getTransactionIsolation方法),也可以设定适当的级别(用连接的setTransactionIsolation方法)。

rollback常常和Java异常处理结合在一起来恢复不可预知的错误。这样的结合提供了简单而优秀的处理数据完整性的机制。我们将在下一节学习JDBC的异常处理。

1.9 错误与异常处理

事实上,在软件项目中错误是常常发生的。经常,数据库程序是一个危险的应用,并且温和地捕捉和处理错误是势在必行的。程序应该能恢复并使数据库保持一致性的状态。Rollback与Java异常处理机制协同工作是达到这一要求的一条清晰的道路。

客户端(程序)访问服务器(数据库)需要是否有错误从服务器返回。JDBC通过两个不同级别的错误情况(SQLException和SQLWarning)来获取以上信息。SQLExceptions是Java异常,假如不处理,会终止应用程序。SQLWarnings是SQLException的子类,但它们描述不致命的错误或不预期的情况等,可以忽略。

在Java中,预计要抛出异常或警告的语句被包含在try语句块中。如果在try语句块中的语句抛出了异常或警告,那么,在相应的catch语句中将被捕获。每个catch语句都被指明准备捕获那个异常。

下面是一个捕捉异常,使用错误情况来卷回事务的例子:

try{

con.setAutoCommit(false);

stmt.executeUpdate(“CREATE TABLE Sells (bar VARCHAR2(40),”+”beer VARHAR2(40),price REAL)”);

stmt.executeUpdate("INSERT INTO Sells VALUES " +

"('Bar Of Foo', 'BudLite', 2.00)") ;

con.commit() ;

con.setAutoCommit(true) ;

}catch(SQLException ex) {

System.err.println("SQLException: " + ex.getMessage()) ;

con.rollback() ;

con.setAutoCommit(true) ;

}

既然这样,一个异常肯定被抛出,因为错误的书写导致了beer被定义为VARHAR2。由于在DBMS中没有这样的类型,一个SQLException将被抛出。这样输出就将是:

Message:   ORA-00902:invalid datatype

还有就是,如果你的数据类型正确,但万一你的你的数据库超过了空间限制并不能再创建一个新的表。SQLWarning可以从Connection对象、Statement对象和ResultSet对象中找回来。每一个仅仅存放着最近的SQLWarning。因此,只要你用你的Statement对象执行其他的语句,那么以前的警告就将被丢弃。下面的小代码举例说明了怎么使用SQLWarning:

ResultSet rs = stmt.executeQuery("SELECT bar FROM Sells") ;

SQLWarning warn = stmt.getWarnings() ;

if (warn != null)

System.out.println("Message: " + warn.getMessage()) ;

SQLWarning warning = rs.getWarnings() ;

if (warning != null)

warning = warning.getNextWarning() ;

if (warning != null)

System.out.println("Message: " + warn.getMessage()) ;

SQLWarnings(相对于SQLExceptions)其实是相当难得,最多的就是数据连接切断的警告。后面将要指出的是在读或写数据库是这里存在的一个问题。

1.11简单的代码和编辑规则

有望地,到现在你已经可以熟悉地用JDBC写复杂的代码了。这里有一个简单的例子融合了上面多有的观点。

我们还有一些简单的为教育的目的由Craig Jurney编写的代码。你可以免费地使用这些代码来作为一个准则,或者在将来写代码是用作筐架,但是你要在你引用了代码的应用上做一个说明。

SQLBuilder.java - Creation of a Relation  关系的创建

SQLLoader.java - Insertion of Tuples     插入记录

SQLRunner.java - Processes Queries     查询过程

SQLUpdater.java - Updating Tuples      更新记录

SQLBatchUpdater.java - Batch Updating   批更新

SQLUtil.java - JDBC Utility Functions    JDBC的有用函数

不要忘记用source /usr/class/cs145/all.env,它将正确的设置你的classpath。增加你的全局的classpath的简单命令:

elaine19:~$ javac SQLBuilder.java

elaine19:~$ java SQLBuilder

替换成:

elaine19:~$ javac SQLBuilder.java

elaine19:~$ java –classpath

/usr/pubsw/apps/oracle/8.1.5/jdbc/lib/classes111.zip: SQLBuilder

在java的用户名和密码文件中有固定的用户名和密码。他们必须被改成你自己的用户名和密码,这样你才能访问数据库。

这篇文章最初由Nathan Folkert在2000年春写给Jennifer Widom教授的CS145班,在2000年秋,它又被Mayank Bawa出租给Jeff Ullman教授的CS145班。Jim Zhuang在2005年夏作了一点小改动。同时感谢Matt Laue的排版改正。

2利用JSP和JDBC技术访问动态的WEB数据

这篇文章讨论用JSP和JDBC技术来整合WEB站点上的静态的、动态的和数据库的内容。为了简单和举例的需要,这里的JSP页面用简短的脚本来揭示JSP开发者根本的JDBC观点而不是把它们隐藏在传统的标签下。作者介绍了一种关键的设计途径就是整合JavaBeans组件和JDBC,就好像JSP技术已经运用了bean和HTTP一样。他也提供代码来执行这种整合。

建立在Java Servlet技术上, JSP技术是生成动态内容的Java 服务器端的核心。一种动态内容的来源是关系数据库。为了管理来自在线社团的电子商务事务,WEB站点用关系数据库来存储各种信息:目录条款,图像,文本,注册用户的数据等等。这篇文章讨论利用JDBC的JSP与关系数据库的应用。JDBC的意思就是通过它,Java程序可以操作关系数据库。

为了能更好的学习这篇文章,你应该熟悉JDBC和SQL。

2.1 JDBC基础

JDBC是连接Java和SQL数据库的桥梁。首要的JDBC对象描述数据库的连接且语句的执行要依赖这个连接。和关系数据库一起使用的两种基本的语句是查询和更新。作为先决条件,你首先需要用java.sql.DriverManger类建立一个数据库连接。连接的建立要花费很长的时间,因此,在强事务环境中,比如一个WEB服务器,只要可能你就想要重用连接。这样的重用称为connection pooling。

如果你的JDBC技术已经生疏了,在例子Listing 1中代码片段说明了怎样建立数据库连接,用连接创建statement对象,发送SQL查询,处理结果,以及释放JDBC资源。

Listing 1.Simple JDBC code

Connection connection = DriverManager.getConnection(URL, user, password);

Statement statement = connection.createStatement();

ResultSet results = statement.executeQuery(sqlQuery);

while (results.next())

{

... process query results ...

logSQLWarnings(results.getWarnings());

}

results.close();

statement.close();

connection.close();

在现实生活中,JDBC代码没有这么简单;异常和警告情况需要处理。例子Listing 2 是相同的JDBC例子但是增加了JDBC异常和警告的处理。在这个例子中,异常和警告是简单和笨拙的,在异常情况下,我们终止操作。可是,dinally{}子句确保了资源的释放。

在这里仅仅暗示实际的处理结果。我们将在本文的后面找到更详细的内容。如果我们正在执行一个数据库更新而不是查询,我们就应该用下面的代码来替换while循环的内容:

int count = statement.executeUpdate(sqlUpdate);

JDBC与JSP研究

除了executeQuery()和executeUpdate()之外,类Statement支持一般的execute()方法。这就允许编辑一般的SQL代码,尽管处理的结果复杂很多。

executeUpdate方法返回更新语句所影响的行数。如果这些代码看起来不熟悉,你也许想花更多的时间来回顾一些关于JDBC的指南信息,在资源章节可以找到。

2.2 在JSP页面中使用JDBC

为了来自数据库的动态内容,我们应该怎样结合JDBC和JSP技术。

一般的规则是,好的JSP实践经验建议我们把表示模式和行为模式分离开来。这类似于在OO编程中的模型-视图-控制器(MVC)范例。这样分离的一个原因是在程序员创建的基于JSP技术的应用可能包含了模型和控制器组件,然而,视图组件是在页面设计器里编辑的。在JSP应用体系结构下,视图的角色决定了它的职责是描述,并只在JSP页面中被处理。控制器的角色是响应请求,往往扮演着servlet的角色,并且多数的JSP开发者渐渐地对在控制器角色里使用JSP页面的优越性达成了共识。模型的角色是对应用实体的行为进行建模,它典型的表现在JavaBeans组件上。

除了决定数据库在MVC范例中对应的位置之外,你有好几种在JSP页面中整合JDBC技术的选择。例如,你可以用脚本插入JDBC,用库标签插入,或把它隐藏在定制的标签或其它类中。

我们下面将看到几个不同途径的例子并讨论它们的使用方法。

2.3 一个JSP scriptlet的例子

一个新的JSP程序员很可能做的第一件事就是写一个scriptlet来访问JDBC。或许它在一些地方象在Listing 3中的那个在页面中用JDBC实现"hit counter"的例子。(这页的一个生动的版本是在JSP开发者指南的WEB站点上。)

Listing 3  JSP page using JDBC in a scriptlet

<jsp:directive.page import="java.sql.*" />

<jsp:scriptlet>

Class.forName("org.gjt.mm.mysql.Driver");

Connection connection =

DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "", "");

Statement statement = connection.createStatement();

int changed =

statement.executeUpdate("update counters set hitCount = hitCount + 1 " +

"where page like '" + request.getRequestURI() + "'");

if (changed == 0) statement.executeUpdate("insert counters(page) values('" +

request.getRequestURI() + "')");

ResultSet rs =

statement.executeQuery("select hitCount from counters where page like '" +

request.getRequestURI() + "'");

rs.next();

int hitCount = rs.getInt(1);

statement.close();

connection.close();

</jsp:scriptlet>

<HTML>

<HEAD>

<TITLE>JDBC scriptlet example</TITLE>

</HEAD>

<BODY>

<P>This page has been hit

<jsp:expression>hitCount</jsp:expression>

times. The page counter is implemented by a JSP scriptlet calling

the JDBC package directly.</P>

</BODY>

</HTML>

这页包含了一个scriptlet(前面突出的部分),它连接数据库,创建statement,以及尝试更新由页面URI键入的纪录。如果更新不影响任何行,这个例子中假定没有这样的行并新增一行。最后,这个scriptlet查询数据库当前命中的数并赋值给一个本地变量。更进一步,在这个JSP页面的描述部分,突出的JSP表达式被用来传递上面的值。

JDBC与JSP研究

注意:你不可能像这样来执行一个hit counter .为每次请求改变数据库的代价是必不可少的。无论如何,这个hit counter都提供了一个查询和更新数据库的简单的例子,这说明我们可以用各种手段把你的JSP与JDBC页整合起来.

尽管功能正确,但这个JSP页面仍有几个问题。首先这个scriptlet并不是一个无程序页面设计师会先看到的。坦白地,甚至使程序员也不想在页面中看到这样的东西。第二,页面缺少了异常处理,那可是真实的JDBC代码必不可少的部分。第三,hit counter的执行是逐字逐句地植入的,因此任何改动都需要传播到包含hit counter 的每个JSP页面。

因此,我们该如何装配这个JSP页面。一个再三提到的方法是用库标签来除去scriptlet。在我们的下一个例子中,将看到这种可选择的解决方案。

2.4 一个用DBTags的库标签例子

一个初学JSP的程序员最先听到的事,经常是来自朋友和专家的关于不要使用scriptlet的建议。相反,他们建议你用定制的标签。定制的标签是一种手段,通过它可扩张JSP平台:定制XML风格的标签,依靠代码库,实现渴望的功能。我们将在下一个例子中,它们工作得多么良好。

Jakarta 库标签工程是Jakarta工程的一个子工程,正式实施的范围是Java Servlet和JSP 技术。

一个由Jakarta 库标签工程赞助发展的包就是DBTags定制标签库。(从前叫做JDBC标签库)。在Listing 4中的例子实现了和Listing 3中相同的hit counter,仅仅用定制标签代替了scriptlet

Listing 4. JSP page using DBTags

<HTML>

<HEAD>

<TITLE>Jakarta DBTags example</TITLE>

</HEAD>

<BODY>

<%@ taglib uri="http://jakarta.apache.org/taglibs/dbtags" prefix="sql" %>

<%-- open a database connection --%>

<sql:connection id="conn1">

<sql:url>jdbc:mysql://localhost/test</sql:url>

<sql:driver>org.gjt.mm.mysql.Driver</sql:driver>

</sql:connection>

<%-- insert a row into the database --%>

<sql:statement id="stmt1" conn="conn1">

<%-- set the SQL query --%>

<sql:query>

insert counters(page,hitCount) values('<%=request.getRequestURI()%>', 0)

</sql:query>

<%-- the insert may fail, but the page will continue --%>

<sql:execute ignoreErrors="true"/>

</sql:statement>

<%-- update the hit counter --%>

<sql:statement id="stmt1" conn="conn1">

<%-- set the SQL query --%>

<sql:query>

update counters set hitCount = hitCount + 1 where page like '<%=request.getRequestURI()%>'

</sql:query>

<%-- execute the query --%>

<sql:execute/>

</sql:statement>

<P>This page has been hit

<%-- query the hit counter --%>

<sql:statement id="stmt1" conn="conn1">

<sql:query>

  

相关评论
广告联系QQ:45157718 点击这里给我发消息 电话:13516821613 杭州余杭东港路118号雷恩国际科技创新园  网站技术支持:黄菊华互联网工作室 浙ICP备06056032号