??xml version="1.0" encoding="utf-8" standalone="yes"?>
This article discusses
some of the important changes in the JDBC specification that either
improve the design or facilitate better performance. The article does
not enlist or survey every single change incorporated as a part of Java Specification Request 221, the JDBC 4.0 initiative.
After reading this article, you should be ready to leverage the new features in your next set of applications.
Annotations and the generic DataSet
The new API defines a set of
All
A
The data class or the user-defined class, which is a parameter type of the
Listing 1 illustrates
code snippets for a simple example to show how the new API is used to
create and run SQL queries, define result set data using a user-defined
class, and bind the returned result set to the user-defined
specifications.
Listing 1. Employee user-defined type and employeeQueries
Exception-handling enhancements
JDBC 4.0 has enhanced
exception-handling capability and alleviates some of the mentioned
problems. The key changes are as follows:
The
Figure 1 illustrates the subclasses of
Figure 1. SQL exception classes: Transient and non-transient
Support for chained
exceptions are now included. New constructors add extra parameters to
capture possible causes for the exception. Multiple
The
Listing 2 depicts the usage of the new for-each-loop construct:
Listing 2. For each loop
SQL/XML
JDBC 4.0 now defines
Invoking the
The
The
Connections and Statements
Sometimes database
connections are unusable though they may not necessarily be closed and
garbage collected. In such situations, the database appears slow and
unresponsive. In most of these circumstances, reinitializing the
connections is perhaps the only way to resolve the problem. When using
the JDBC API prior to version 4.0, there is no way to distinguish
between a stale connection and a closed connection. The new API adds an
Also, database
connections are often shared among clients, and sometimes some clients
tend to use more resources than others, which can lead to
starvation-like situations. The
RowId
A
The
Both the
Listing 3 shows how
Listing 3. Get RowId
The
The
Both the
The facility to set or update the
Leverage nonstandard vendor implemented resources
This
The
As an example, when
using Oracle, Oracle JDBC drivers provide update batching extensions
that are better performing and more efficient as compared to the
standard JDBC batch-updating mechanisms. For earlier JDBC versions,
this implies using the Oracle-specific definitions, such as
Service provider mechanism for driver loading
Listing 4. Class.forName
With JDBC 4.0, if the
JDBC driver vendors package their drivers as services, defined under
the server provider mechanism definitions as per the JAR specification,
the
Conclusion
ANT安装、配|?/span>
内容摘要Q?br />ant是一个基于JAVA的自动化脚本引擎Q脚本格式ؓXML。除了做JAVA~译相关d外,ANTq可以通过插g实现很多应用的调用?/p>
d1Qusage 打印本脚本的帮助信息Q缺省) 注:我看到很多项目的ant脚本中的命名基本上都是一致的Q比如:~译一般叫build或者compileQ打包一般叫jar或warQ生成文档一般命名ؓjavadoc或javadocsQ执行全部Q务all。在每个d的中QANT会根据配|调用一些外部应用ƈ配以相应参数执行。虽然ANT可调用的外部应用U类非常丰富Q但其实最常用的就2Q?个:比如javac
javadoc jar{?
q样执行ant 后,如果不指定配|文件ant会缺省找build.xmlq个配置文gQƈҎ配置文g执行dQ缺省的d讄可以指向最常用的Q务,比如Q?
buildQ或指向打印帮助信息QusageQ告诉用h那些脚本选项可以使用?/p>
最好的学习q程是看懂那些open
source目中的build.xml脚本Q然后根据自q需要简化成一个更单的QANT和APACHE上很多非常工E派的项目:单易用,而且适应性非常强Q因些项目的建立往往来源于开发h员日常最直接的需求?br />以下是的一个WebLucene应用的例子:修改自JDOM的build.xmlQ?/p>
<project default="usage" basedir="."> <!-- ===================================================================
--> <echo message="----------- ${Name} ${version} [${year}]
------------"/> <property name="debug" value="off"/> <property name="src.dir" value="./src/WEB-INF/src"/> <property name="build.src"
value="./src/WEB-INF/build"/> <path id="classpath"> <filter token="year" value="${year}"/> <!-- ===================================================================
--> <!-- ===================================================================
--> <!-- copy src files --> <!-- ===================================================================
--> <!-- ===================================================================
--> <!-- ===================================================================
--> <!-- ===================================================================
--> ~省dQusage 打印帮助文档Q告诉有那些d选项Q可用的有build, jar, javadoc和clean. 初始化环境变量:init 1
除了使用却缺省的property讄了JAVA源\径和输出路径外,引用了一个外部的build.properties文g中的讄Q?br /><property
file="${basedir}/build.properties"
/> 2 CLASSPATH讄Q用了其中的: 文g复制Qprepare-src <!-- copy src files --> ~译dQbuild 打包dQjar 生成JAVADOC文档d: javadoc 清空临时~译文gQclean TODOQ?br />更多d/扩展Q(样例Q?/p>
试dQJUnit试 参考资料: Jakarta ANT: Trackback:
http://tb.blog.csdn.net/TrackBack.aspx?PostId=707995
转蝲自:http://www.javaworld.com/javaworld/jw-05-2006/jw-
0501-jdbc
.html
Design and performance improvements with JDBC 4.0
Effectively utilize JDBC'S features to get desired results with less code
Summary
By Shashank Tiwari
Java Database Connectivity (JDBC) 4.0 is ready for release by mid 2006
as a part of Java Standard Edition 6.0. How can you leverage the new
specification to improve the design and performance of database access
and interactions in Java applications? This article discusses the new
features of JDBC 4.0, illustrates its solutions to some existing
problems, and presents its improvements in design and performance
through examples. (2,100 words; May 1, 2006)
ava Database Connectivity (JDBC),
which has existed from the first public version of the core Java
language, has evolved significantly over the last 10 years. In its
current version, 4.0, which will be packaged with Java Standard Edition
6.0 (Java SE is Sun's new name for J2SE), it shows significant
improvements in design and provides a richer API, with focus on ease of
development and improvement in productivity.
I assume you are already aware of annotations and generics, which were
introduced in Java with J2SE 5.0. JDBC 4.0 introduces annotations and
the generic DataSet
.
This change aims to simplify execution of SQL queries (in scenarios
that return a single result set) and SQL DML (data manipulation
language) statements (that return either a row count or nothing).
Query
and DataSet
interfaces. The Query
interface defines a set of methods decorated with the JDBC annotations.
These decorated methods describe the SQL select and update statements,
and specify how the result set should be bound to a DataSet
. The DataSet
interface is a parameterized type, as defined by generics. The DataSet
interface provides a type-safe definition for the result set data.
Query
interfaces inherit from the BaseQuery
interface. A concrete implementation of the interface can be instantiated using either the Connection.createQueryObject()
or DataSource.createQueryObject()
methods and passing a Query
interface type as its parameter.
DataSet
interface inherits from java.util.List
. A data class describing the columns of the result set data, returned by an annotated method of the Query
interface, is its parameter type. A DataSet
can be manipulated and operated upon both in a connected and disconnected mode. Thus, the DataSet
is implemented either as a ResultSet
or a CachedRowSet
, depending on its operating mode: connected or disconnected. DataSet
, being a sub-interface of the java.util.List
, allows access of its data rows with the Iterator pattern, using the java.util.Iterator
interface.
DataSet
interface, can be specified in two ways: as a structure or as a
JavaBeans object. Either method achieves the goal of binding result set
data columns to user-defined class definitions, but the JavaBeans
component model is more elegant and facilitates object definition reuse
within other frameworks that support the JavaBeans model.
pubic class Employee {
private int employeeId;
private String firstName;
private String lastName;
public int getEmployeeId() {
return employeeId;
}
public setEmployeeId(int employeeId) {
this.employeeId = employeeId;
}
public String getFirstName() {
return firstName;
}
public setFirstName(String firstName) {
this.firstName = firstName;
}
pubic String lastName() {
return lastName;
}
public setLastName(String lastName) {
this.lastName = lastName;
}
}
interface EmployeeQueries extends BaseQuery {
@Select (sql="SELECT employeeId, firstName, lastName FROM employee")
DataSet<Employee> getAllEmployees ();
@Update (sql="delete from employee")
int deleteAllEmployees ();
}
Connection con = ...
EmployeeQueries empQueries = con.createQueryObject (EmployeeQueries.class);
DataSet<Employee> empData = empQueries.getAllEmployees ();
The exception-handling functionality in the JDBC API prior to version 4.0 is limited and often insufficient. SQLException
is thrown for all types of errors. There is no classification of
exceptions, and no hierarchy defines them. The only way to get some
meaningful information is to retrieve and analyze the SQLState
value. SQLState
values and their corresponding meanings change from datasource to
datasource; hence, getting to the root of the problem and efficiently
handling exceptions proves to be a tedious task.
SQLException
into transient and non-transient typesIterable
interface SQLTransientException
is thrown where a previously failed operation may succeed on retrial. The SQLNonTransientException
is thrown where retrial will not lead to a successful operation unless the cause of the SQLException
is corrected.
SQLTransientException
and SQLNonTransientException
.
SQLException
s could be iterated over in a loop, and getCause()
could be called to determine the exception's possible cause. The getCause()
method can return non-SQLException
s if they are the underlying cause of the exceptions.
SQLException
class now implements the Iterable
interface and supports the J2SE 5.0 for each loop for easier and more elegant looping.
catch(SQLException ex) {
for(Throwable t : ex) {
System.out.println("exception:" + t);
}
}
A large amount of data now exists in the XML format. Databases have
extended support for the XML data type by defining a standard XML type
in the SQL 2003 specification. Most database vendors have an
implementation of the XML data type in their new releases. With the
inclusion of such a type, an XML dataset or document could be one of
the fields or column values in a row of a database table. Prior to JDBC
4.0, perhaps the best way to manipulate such data within the JDBC
framework is to use proprietary extensions from the driver vendors or
access it as a CLOB
type.
SQLXML
as the Java data type that maps the database SQL XML type. The API
supports processing of an XML type as a string or as a StAX stream.
Streaming API for XML, which for Java has been adopted via JSR 173, is
based on the Iterator pattern, as opposed to the Simple API for XML
Processing (SAX), which is based on the Observer pattern.
Connection
object's createSQLXML()
method can create a SQLXML
object. This is an empty object, so the data can be attached to it by either using the setString()
method or by associating an XML stream using the createXMLStreamWriter()
method with the object. Similarly, XML data can be retrieved from a SQLXML
object using getString()
or associating an XML stream using createXMLStreamReader()
with the object.
ResultSet
, the PreparedStatement
, and the CallableStatement
interfaces have getSQLXML()
methods for retrieving a SQLXML
data type. PreparedStatement
and CallableStatement
also have setSQLXML()
methods to add SQLXML
objects as parameters.
SQLXML
resources can be released by calling their free()
methods, which might prove pertinent where the objects are valid in long-running transactions. DatabaseMetaData
's getTypeInfo()
method can be called on a datasource to check if the database supports the SQLXML
data type, since this method returns all the data types it supports.
The Connection
interface definitions have been enhanced to analyze connection state and usage to facilitate efficiency.
isValid()
method to the Connection
interface to query if the connection is still valid.
Connection
interface defines a setClientInfo()
method to define client-specific properties, which could be utilized to
analyze and monitor resource utilization by the clients.
The RowId
in many databases is a unique way to identify a row in a table. Queries using RowId
in the search criteria are often the fastest way to retrieve data,
especially true in the case of the Oracle and DB2 databases. Since java.sql.RowId
is now a built-in type in Java, you could utilize the performance benefits associated with its usage. RowId
s
are most useful in identifying unique and specific rows when duplicate
data exists and some rows are identical. However, it is important to
understand that RowId
s often are unique only for a table and not for the entire database; they may change and are not supported by all databases. RowId
s are typically not portable across datasources and thus should be used with caution when working with multiple datasources.
RowId
is valid for the lifetime defined by the datasource and as long as the row is not deleted. The DatabaseMetadata.getRowIdLifetime()
method is called to determine the RowId
's lifetime. The return type is an enumeration type as summarized in the table below.
RowIdLifetime
enum type
Definition
ROWID_UNSUPPORTED
Datasource does not support
RowId
type
ROWID_VALID_OTHER
Implementation-dependent lifetime
ROWID_VALID_TRANSACTION
Lifetime is at least the containing transaction
ROWID_VALID_SESSION
Lifetime is at least the containing session
ROWID_VALID_FOREVER
Unlimited lifetime
ROWID_VALID_TRANSACTION
, ROWID_VALID_SESSION
, and ROWID_VALID_FOREVER
definitions are true as long as the row is not deleted. It is important to understand that a new RowId
is assigned if a row is deleted and reinserted, which sometimes could
happen transparently at the datasource. As an example, in Oracle, if
the "enable row movement" clause is set on a partitioned table and an
update of the partition key causes the row to move from one partition
to another, the RowId
will change. Even without the "enable row movement" flag with the "alter table table_name" move, the RowId
could change.
ResultSet
and CallableStatement
interfaces have been updated to include a method called getRowID()
, which returns a javax.sql.RowId type
.
RowId
could be retrieved from a ResultSet
and from a CallableStatement
.
//The method signatures to retrieve RowId from a ResultSet is as follows:
RowId getRowId (int columnIndex)
RowId getRowId (String columnName)
...
Statement stmt = con.createStatement ();
ResultSet rs = stmt. ExecuteQuery (?;
while (rs.next ()) {
...
java.sql.RowId rid = rs.getRowId (1);
...
}
//The method signatures to retrieve RowId from a CallableStatement is as follows:
RowId getRowId (int parameterIndex)
RowId getRowId (String parameterName)
Connection con;
...
CallableStatement cstmt = con.prepareCall (?;
...
cstmt.registerOutParameter (2, Types.ROWID);
...
cstmt.executeUpdate ();
...
java.sql.RowId rid = cstmt.getRowId (2);RowId
can be used to refer uniquely to a row and thus can be used to retrieve
the rows or update the row data. When fetching or updating using RowId
references, it is important to know the validity of the RowId
's
lifetime to assure consistent results. It is also advisable to
simultaneously use another reference, such as the primary key, to avoid
inconsistent results in circumstances where the RowId
could change transparently.
RowId
values can also be set or updated. In the case of an updateable ResultSet
, the updateRowId()
method could be used to update the RowId
for a particular row in a table.
PreparedStatement
and the CallableStatement
interfaces support a setRowId()
method, with different signatures, to set the RowId
as a parameter value. This value could be used to refer to data rows or to update the RowId
value for a particular row in a table.
RowId
provides the flexibility to control the unique row identifiers and
could be used to make such identifiers unique across the tables used.
Perhaps, portability of RowId
across supporting
datasources could also be achieved by explicitly setting consistent
values across them. However, because system-generated RowId
s are often efficient, and transparent tasks could alter RowIds
, they are best used by an application as a read-only attribute.
The new JDBC API defines a java.sql.Wrapper
interface. This interface provides the ability to access
datasource-vendor-specific resources by retrieving the delegate
instance using the corresponding wrapped proxy instance.
Wrapper
interface has 17 sub-interfaces as per the current specification and includes Connection
, ResultSet
, Statement
, CallableStatement
, PreparedStatement
, DataSource
, DatabaseMetaData
, and ResultSetMetaData
,
among others in the list. This is an excellent design as it facilitates
datasource-vedor-specific resource implementation at almost all stages
of the query-creation and result-set-retrieval lifecycles.
unwrap()
method returns the object that implements the given interface to allow access to vendor-specific methods. The isWrapperFor()
method returns a Boolean value. It returns true if it implements the
interface, or if it directly or indirectly is a wrapper for the object.
OraclePreparedStatement
,
in the code. This compromises code portability. With the new API, many
such efficient implementations can be wrapped and exposed within the
standard JDBC definitions.
In a nonmanaged or standalone program scenario, prior to JDBC 4.0, you
would have to load the JDBC driver class explicitly by invoking the Class.forName
method, as shown in Listing 4:
Class.forName ("com.driverprovider.jdbc.jdbcDriverImpl");
DriverManager
code would implicitly load the driver
by searching for it in the classpath. The benefit of this mechanism is
that the developer does not need to know about the specific driver
class and can write a little less code when using JDBC. Also, since the
driver class name is no longer in the code, a name change would not
require recompilations. If multiple drivers are specified in the
classpath, then DriverManger
will try and establish a connection using the first driver it encounters in the classpath and iterate further if required.
In this article, I have discussed some of the new and improved features
of JDBC 4.0. Many of these new features enhance a developer's
productivity and facilitate development. Also, the specification does
not eradicate the possible use of extra JDBC frameworks to provide
templating facilities and advanced exception-handling capabilities.
However, there is some criticism as well. Some believe that annotations
effectively lead to hard-coding in code, which causes problems in code
maintainability.
]]>
ANT的基本概念:
ANT的安装:解包Q设|\?
ANT的用:最好的学习只不q是一个简单实用的例子h…?
ANT的基本概念:Java的Makefile
当一个代码项目大了以后,每次重新~译Q打包,试{都会变得非常复杂而且重复Q因此c语言中有make脚本来帮助这些工作的扚w完成。在Java
中应用是q_无关性的Q当然不会用q_相关的make脚本来完成这些批处理d了,ANT本n是q样一个流E脚本引擎,用于自动化调用程序完成项目的~译Q打包,试{。除了基于JAVA是^台无关的外,脚本的格式是ZXML的,比make脚本来说q要好维护一些?/p>
每个ant脚本Q缺省叫build.xmlQ中讄了一pdd(target)Q比如对于一个一般的目可能需要有以下d?/p>
d2Qclean <-- init 清空初始化环?
d3Qjavadoc
<-- build <-- init 生成JAVADOC
d4Qjar <-- build <-- init 生成JAR
d5Qall <-- jar + javadoc <-- build <-- init 完成以上所有Q务:jar javadoc
而多个Q务之间往往又包含了一定了依赖关系Q比如把整个应用打包d(jar)的这个依赖于~译d(build)Q而编译Q务又依赖于整个环境初始化d(init){?/p>
ANT的安?br />解包后在pȝ可执行\径中加入指向ant的bin的\径就可以了,比如可以在GNU/Linux上把以下配置加入/etc/profile中:
export
ANT_HOME=/home/ant
export JAVA_HOME=/usr/java/j2sdk1.4.1
export
PATH=$PATH:$JAVA_HOME/bin:$ANT_HOME/bin
ANT的?/p>
<!-- Initialization target -->
<!--
===================================================================
-->
<target name="init">
<tstamp/>
<property
file="${basedir}/build.properties" />
<property name="Name"
value="ProjectFullName"/>
<property name="name"
value="project_name"/>
<property name="version"
value="0.2"/>
<property name="year" value="2003"/>
<property name="optimize"
value="on"/>
<property name="deprecation" value="on"/>
<property
name="lib.dir" value="./src/WEB-INF/lib"/>
<property name="packages"
value="com.chedong.*,org.apache.lucene.*"/>
<property name="build.dest"
value="./src/WEB-INF/classes"/>
<property name="build.javadocs"
value="./src/doc"/>
<pathelement
path="${jsdk_jar}"/>
<fileset dir="${lib.dir}">
<include
name="**/*.jar"/>
</fileset>
</path>
<filter token="version"
value="${version}"/>
<filter token="date"
value="${TODAY}"/>
<filter token="log" value="true"/>
<filter
token="verbose" value="true"/>
</target>
<!-- Help on usage -->
<!--
===================================================================
-->
<target name="usage" depends="init">
<echo
message="${Name} Build file"/>
<echo
message="-------------------------------------------------------------"/>
<echo
message=""/>
<echo message=" available targets are:"/>
<echo
message=""/>
<echo message=" jar --> generates the ${name}.jar
file"/>
<echo message=" build --> compiles the source
code"/>
<echo message=" javadoc --> generates the API
documentation"/>
<echo message=" clean --> cleans up the
directory"/>
<echo message=""/>
<echo message=" Please rename
build.properties.default to build.properties"/>
<echo message=" and
edit build.properties to specify JSDK 2.3 classpath."/>
<echo
message=""/>
<echo message=" See the comments inside the build.xml file
for more details."/>
<echo
message="-------------------------------------------------------------"/>
<echo
message=""/>
<echo message=""/>
</target>
<!-- Prepares the source code -->
<!--
===================================================================
-->
<target name="prepare-src" depends="init">
<!-- create
directories -->
<mkdir dir="${build.src}"/>
<mkdir
dir="${build.dest}"/>
<copy
todir="${build.src}">
<fileset
dir="${src.dir}"/>
</copy>
</target>
<!-- Compiles the source directory -->
<!--
===================================================================
-->
<target name="build" depends="prepare-src">
<javac
srcdir="${build.src}"
destdir="${build.dest}"
debug="${debug}"
optimize="${optimize}">
<classpath
refid="classpath"/>
</javac>
</target>
<!-- Creates the class package -->
<!--
===================================================================
-->
<target name="jar" depends="build">
<jar
jarfile="${lib.dir}/${name}.jar"
basedir="${build.dest}"
includes="**"/>
</target>
<!-- Creates the API documentation -->
<!--
===================================================================
-->
<target name="javadoc" depends="build">
<mkdir
dir="${build.javadocs}"/>
<javadoc
packagenames="${packages}"
sourcepath="${build.src}"
destdir="${build.javadocs}"
author="true"
version="true"
use="true"
splitindex="true"
windowtitle="${Name}
API"
doctitle="${Name}">
<classpath
refid="classpath"/>
</javadoc>
</target>
<!-- Clean targets -->
<!--
===================================================================
-->
<target name="clean" depends="init">
<delete
dir="${build.src}"/>
<delete dir="${build.dest}/org"/>
<delete
dir="${build.dest}/com"/>
<delete>
<fileset
dir="${build.dest}"
includes="**/*.class"/>
</delete>
</target>
</project>
<!--
End of file -->
所有Q务都Z一些基本环境变量的讄初始化完成,是后l其他Q务的基础Q在环境初始化过E中Q有2Ҏ较可以方便设|:
q样大部分简单配|用户只要会看懂build.properties可以了Q毕竟XML比vkey
value的属性文件还是要可读性差一些。用build.properties也可以方便其他用户从~译的细节中解放出来?/p>
<path id="classpath">
<pathelement
path="${jsdk_jar}"/>
<fileset dir="${lib.dir}">
<include
name="**/*.jar"/>
</fileset>
</path>
则相当于讄了:CLASSPATH=/path/to/resin/lib/jsdk23.jar;
/path/to/project/lib/*.jar;
创徏临时SRC存放目录和输出目录?br /><!--
===================================================================
-->
<!-- Prepares the source code -->
<!--
===================================================================
-->
<target name="prepare-src" depends="init">
<!-- create
directories -->
<mkdir dir="${build.src}"/>
<mkdir
dir="${build.dest}"/>
<copy
todir="${build.src}">
<fileset
dir="${src.dir}"/>
</copy>
</target>
~译时的CLASSPATH环境通过一下方式找到引用一个path对象
<classpath
refid="classpath"/>
对应用打包生成项目所写名?jar文g
<!--
===================================================================
-->
<!-- Creates the class package -->
<!--
===================================================================
-->
<target name="jar" depends="build">
<jar
jarfile="${lib.dir}/${name}.jar"
basedir="${build.dest}"
includes="**"/>
</target>
<!--
===================================================================
-->
<!-- Creates the API documentation -->
<!--
===================================================================
-->
<target name="javadoc" depends="build">
<mkdir
dir="${build.javadocs}"/>
<javadoc
packagenames="${packages}"
sourcepath="${build.src}"
destdir="${build.javadocs}"
author="true"
version="true"
use="true"
splitindex="true"
windowtitle="${Name}
API"
doctitle="${Name}">
<classpath
refid="classpath"/>
</javadoc>
</target>
<!--
===================================================================
-->
<!-- Clean targets -->
<!--
===================================================================
-->
<target name="clean" depends="init">
<delete
dir="${build.src}"/>
<delete dir="${build.dest}/org"/>
<delete
dir="${build.dest}/com"/>
<delete>
<fileset
dir="${build.dest}"
includes="**/*.class"/>
</delete>
</target>
代码风格查Q务:CheckStyleQJalopy{?
邮g警报dQ可以把以上q些d的输告发送到制定的用户列表中Q这个Q务可以设|每天自动运行?
http://ant.apache.org
]]>
Statement stmt = con.createStatement();
ResultSet rs =
stmt.executeQuery("SELECT * FROM user WHERE
username='aa'");
stmt.executeUpdate("UPDATE user SET lastdatetime=now() where
username='aa'");
q是一个用L录时Q经常用到的代码Q先是根据用户名aa查找该用L详细信息Q然后再更新该用L最后登录时_lastdatetimeQ。这q个里面Q我们用了两个sql语句Q这个是我一直用的方法,但是如果用JDBC2.0l我们提供的便利Q我们只要写一条sql够了,其他的都交给jdbcQ看下面的代码:
Statement stmt2 =
con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);
ResultSet
rs2 = stmt.executeQuery("SELECT * FROM user WHERE
username='aa'");
rs2.next();
rs2.updateDate("lastdatetime", new
Date(Calendar.getInstance().getTimeInMillis()));
rs2.updateRow();
q里面最主要的特征就是ResultSet.TYPE_FORWARD_ONLY和ResultSet.CONCUR_UPDATABLEQ通过初始化Statement时传不同的参敎ͼ可以对ResultSetq行不用的错作限制。con.createStatement的时候,有三U可以掉用的函数Q?/font>
1、createStatement();
2、createStatement(int resultSetType, int
resultSetConcurrency)
3、createStatement(int resultSetType, int
resultSetConcurrency, int resultSetHoldability)
其中resultSetType可选值是Q?br /> 1、ResultSet.TYPE_FORWARD_ONLY
在ResultSet中只能先前移动游标,
2、ResultSet.TYPE_SCROLL_INSENSITIVE
在ResultSet中可以随心所Ʋ的先前向后Ud游标Q?br /> 3、ResultSet.TYPE_SCROLL_SENSITIVE
在ResultSet中可以随心所Ʋ的先前向后Ud游标Q同时ResultSet的值有所改变的时候,他可以得到改变后的最新的?br />其中resultSetConcurrency可选值是Q?br />
1、ResultSet.CONCUR_READ_ONLY 在ResultSet中的数据记录是只ȝQ可以修?br />
2、ResultSet.CONCUR_UPDATABLE
在ResultSet中的数据记录可以L修改Q然后更C数据?br />其中resultSetHoldability可选值是Q?br />
1、ResultSet.HOLD_CURSORS_OVER_COMMIT 表示修改提交?不关闭ResultSet的游?br />
2、ResultSet.CLOSE_CURSORS_AT_COMMIT 表示修改提交?关闭ResultSet的游?/font>
对于查询操作W一U初始化ҎcreateStatement()Q相当于W二U方法的createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY)Q第三种Ҏ的createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, ResultSet.CLOSE_CURSORS_AT_COMMIT)
下面写一Ddemo的代码,我把一些特征函数都用出来,但是只是用来查考和说明名灵zL的?/font>
Statement stmt2 =
con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);
ResultSet
rs2 = stmt.executeQuery("SELECT * FROM
user");
rs2.next();
rs2.updateDate("lastdatetime", new
Date(Calendar.getInstance().getTimeInMillis()));
rs2.updateRow();
rs2.afterLast();
while(rs2.previous()){
/**....*/ }
rs.beforeFirst();
while(rs2.next()){ /**....*/
}
rs.last();
rs.first();
rs.absolute(5); //游标Ud到第5?br /> rs.absolute(-1);
//游标Ud到最后一?br /> rs.relative(-5); //游标向上Ud5?br /> rs.relative(2);
//游标向下Ud2?br /> rs.deleteRow(); //删除当前?br /> rs.last();
//游标Ud到最?br /> rs.updateString("summary", "This is ...");
//讄更新的字D?br /> rs.cancelRowUpdates(); //取消刚才输入的更?br /> rs.getRow();
//得到当前行号
rs.moveToInsertRow(); //游标Ud到要新增的那条记录上
rs.updateInt("id",
1);
rs.updateString(2, "my name");
rs.insertRow(); //插入新记?/font>
JDBC2.0提供的还有一个功能就是数据库的批量操?/font>Q?/font>
con.setAutoCommit(false);
Statement stmt3 =
con.createStatement();
stmt3.addBatch("insert
.....");
stmt3.addBatch("insert .....");
int[] rows =
stmt3.executeBatch();
con.commit();
但是有一点要注意Qstmt3.executeBatch()他不会自动给你回滚数据操作,当你?条update语句的时候,如果W三条发生错误,那么无法自动回滚前两条update语句的媄响,所以一定要自己手工q行事务理?/font>
在您的事务中使用 Savepoint
JDBC3.0中最令h兴奋的附加特点就?Savepoint
了。有时候需要的是对事务多一点的控制Q而不是在当前的事务中单地Ҏ一个改变进行回滚。在JDBC3.0下,您就可以通过 Savepoint
获得q种控制。Savepoint 接口允许您将事务分割为各个逻辑断点Q以控制有多事务需要回滚。看下面的代码:
conn.setAutoCommit(false);
conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
Statement stmt = conn.createStatement();
int rows = stmt.executeUpdate(
"INSERT INTO authors (first_name, last_name) valueS(′Lewis? ′Carroll?");
Savepoint svpt = conn.setSavepoint("NewAuthor");
try{
rows =
stmt.executeUpdate( "UPDATE authors set type = ′fiction?WHERE last_name =
′Carroll?);
}catch(Exception e){
conn.rollback(svpt);
rows =
stmt.executeUpdate( " update .......... other sql ");
}
conn.commit();
上面代码昄Q当UPDATE authorsp|的时候,pȝ事务回滚UPDATE authors的sql的媄响,而INSERT INTO authors的sql仍然有效
索自动生的关键?/font>
Z解决对获取自动生的或自动增加的关键字的值的需求,JDBC
3.0现在获取这U值变得很L。要定M所产生的关键字的|只要单地在语句的 execute()
Ҏ中指定一个可选的标记QStatement.RETURN_GENERATED_KEYS和Statement.NO_GENERATED_KEYS。在执行q条语句后,所产生的关键字的值就会通过?
Statement 的实例方?getGeneratedKeys() 来检?ResultSet 而获得。ResultSet
包含了每个所产生的关键字的列。看下面代码Q?/font>
Statement stmt = conn.createStatement();
stmt.executeUpdate("INSERT INTO authors (first_name, last_name) valueS
(′George? ′Orwell?", Statement.RETURN_GENERATED_KEYS);
ResultSet rs =
stmt.getGeneratedKeys();
if ( rs.next() ) {
int key = rs.getInt();
}
参考资料: http://java.sun.com/j2se/1.5.0/docs/api/java/sql/package-summary.html
作者:朱骐
朱骐Q男Q江苏南京hQ讲师,主要研究方向Q信息系l与集成技术。您可以通过qizhu003@msn.com和作者取得联pR?/p>
JMS面向Web的应用与面向桌面的应用相比,有特D的用户环境要求Q同一个消息必能被若q未知的用户消费Q因此在消息接收方必L"接收而不认"的提交机Ӟ本文以CWNF校务pȝ为实现案例,讨论面向Web的JMS应用pȝ消息提交原理及采用的关键技术?/p>
消息传递是一U在软glg或应用之间进行分布式通信的松散耦合ҎQ与各种紧密耦合通信技术(如CORBA、Java RMI、COM/DCOMQ相比,不同之处在于Q①消息pȝ是一U对{实施,通信双方x息的发送者和接受者都是该pȝ中的客户端,彼此不呈C/S关系Q? ②通信双方的工作是异步的;③基于消息格式一_通信双方只需一个中介来存储q管理消息就可以实现通信Q而紧密耦合技术则需要知道远E方法在本地的接口? 因自w特点,消息传递技术在企业中和企业间有较广泛的应用需求?/p>
JMSQJava Message ServiceQ是J2EE企业q_的Java消息服务Q目前主J2EE产品的JMS都实C存储功能QJMS客户端通过JMS API创徏Q彼此间通过目的圎ͼDestinationQ对象进行通信Q可是JMS消息pȝ多见于桌面应用,而Web应用鲜见Q本文以W者开发的CWNF 校务pȝ为案例,讨论面向Web的JMS应用pȝ的实现原理及采用的关键技术?/p>
JMS应用pȝ?个部分:①JMS提供者(JMS ProviderQ,是一个逻辑数据存储体,q提供管理工具和控制Ҏ;②JMS客户端,是用Java语言~写的发送或接收消息的组件或应用Q③消息Q是 JMS客户端间被传递的承蝲信息的对象;④被理对象Q是pȝ理员ؓ客户端预|的JMS对象Q包括目的地对象和连接工厂对象,其中目的地对象是客户端间 的消息中介。这4个部分通过JNDI相关联:理员通过理工具把目的地对象和连接工厂对象绑定到一个JNDI API命名I间中,JMS客户端就可以在命名空间中查找q些对象Qƈ通过JMS提供者徏立与q些对象的逻辑q接Q从而彼此之间实现通信Q图1Q。JMS? ?U消息传递域Q点到点、发?订阅Q与之相对应的消息目的地对象也有2U:队列、主题?/p>
通常Q无论是消息发送方q是接收方,桌面应用都不容许消息丢失或重复,JMS消息提交机制是基于这个要求的Q它们从不同斚w保证该要求的实现Q①? 接收Ҏ制消息的认。通过认保证一个接收者对一个消息只消费一ơ,在非事务性的会话中,消息认方式取决于create×××SessionҎW二 个参数的|在事务性会话中Q无论由Bean理事务q是由Bean容器理事务Q消息确认都由Bean容器自动完成。②在发送方指定消息的提交模式和? 存期。提交模式有两种QPERSISTENT(E_存储)和NON_PERSISTENT(非稳定存?Q稳定存储保证在故障情况下消息不会丢失;生存? 军_一个消息在存储中介中的存在寿命QJMS提供者会自动摧毁到期的消息。③创徏持久定阅的接收方。在发布/订阅pȝ中,持久订阅者可以接收到在订阅者关 闭阶D|息发送方发布的消息?/p>
但是Web应用pȝ在消息接收方有WebҎ的用L境要求:①若q个用户q一个JMS客户端组Ӟ因此消息应向一个消息接收者提交而不需? 认,h容器自动认功能的Bean是无法实现这一要求的;在一个组件内如果把会话设|成事务性的Q而这个组件的容器又不h事务理能力Q则q个lg? 能做?接收而不认"Q在Web应用pȝ中只有ServletlgW合q一要求。②JMS客户端的消息接收者经常关闭,Z接收在关闭期间发送来的消 息,消息接收者必定是Z主题的持久定阅者,所以面向Web的JMS应用pȝ必定采用发布/订阅消息传递域?/p>
CWNF是一个面向Web的JMS校务pȝQ用于校园发布通知及征求意见等校务工作Q通知分ؓ2c:普通通知和征求意见性通知?/p>
该系l用户分?c,用户不同Q处理模型也不同Q基本情况如下:①发布用P拥有通知发布权,向主题发布通知Q②|名用户Q查阅通知Q也可发表对征求意见性通知的反馈意见;③匿名用P只查阅通知?/p>
pȝ中的数据因此?c:通知、反馈。接收方接收的数据将形成一个XML文档对象Q以便发往Web览器显C;Zq样的要求,考察下面2个问题:①系l中各方之间的数据关p,②各Ҏ据的形式?/p>
主要的数据关pL3个:①通知发送方与通知接收方的数据关系Q②反馈发送方与反馈接收方的数据关p,③通知接收方与反馈接收方的数据关系。(如图 2Q在发送方Q数据(通知或反馈)是一件一件的发送,在接收方Q数据(通知或反馈)则是ҎӞ是对应发送方数据的集合,因此在发送方没有必要把数据直? 加工成XML文档对象形式Q只要生成能构成XML文档对象的元素对象即可;而通知接收方与反馈接收方的数据关系则是Q每一条征求意见性通知都有相关的一? 反馈集合?/p>
pȝ的数据流模型如下Q?br>
①通知发送方Q表单数据→XML元素Q通知Q→主题(存储)
②通知接收方:主题(存储)→XML元素Q通知Q→XML文档Q通知Q→XSL昄Q含表单Q?br>
③通知接收方到反馈接收? XSL昄Q含表单Q→主题Q存储)
④反馈接收方Q主?存储)→XML元素Q反馈)→XML文档Q反馈)→XSL昄Q含表单Q?br>
⑤反馈发送方Q表单数据→XML元素Q反馈)→主?存储)
pȝlg模型如图3Q主题CWNFTopic是消息传递中介,NoticerServletlg向发布用户发送表单,q从表单接收数据Q然后生? XML元素对象Q该元素对象和其它一些数据被作ؓ参数调用PublisherBeanlgҎQ向主题发送以该元素对象ؓ消息体的消息Q? ReaderServletlg处理|名用户和匿名用h阅通知的业务,它从表单获得用户查阅什么方面通知的有关信息后Q便使用receiveҎ限时 dC主题接收消息q对消息q行{选,把筛选出的若q消息的消息体取出,然后加工成XML文档对象Q根元素是通知集)Q最后输出? FeedbackerPubServlet用于反馈发送方的业务处理,功能与NoticerServlet怼Q? FeedbackerSubServlet用于反馈接收方的业务处理Q功能与ReaderServlet怼QPublisherBeanlg? NoticerServletlg和FeedbackerPubServletlg调用Q用于发送消息,容器理发送事务,h很高的可靠性?/p>
JDOM是一个开放源代码的纯Java树式APIQ用于分析、徏立、处理和序列化XML文档。在数据模型中QXML元素和XML文档都由JDOM API建立Q在发送方Q通过用户提交的表单取得名/值对若干Q这些数据经qJDOMҎ处理生成XML元素对象Q元素对象被作ؓ消息的消息体发往主题? 储;在接收方Q持久订阅者接收到若干XML元素对象后,l箋通过JDOMҎ建立XML文档对象。且XML文档向Web览器输Z依赖于JDOM? XMLOutputte对象ҎQ?/p>
XSL是可扩展的样式单语言Q通知集的XML文档和反馈集的XML文档都有相关的XSL文档军_光面显C,如通知集XML文档的XSL样式定义如下Q?/p>
用户的一些处理工作需要注?d后才能进行,因此注册/d的获准信息必能在有关Servletlg之间传递。ServletContext 对象可设|和d属性,使不同Servlet之间怺通信Q在pȝ中被用于有关lg对用戯n份的验证?/p>
每一条征求意见性通知都有一个相兌的反馈集合,兌可通过讄消息属性实现。JMS消息Q包括通知cL息)都有pȝUJMSMessageID? 性,其值是唯一的,可用于表征每一条征求意见性通知Q因此对M反馈消息也可以设|一个应用属性(CWNF中是FeedbackSNQ,让它取与之相? 联的征求意见性通知的JMSMessageID属性倹{这样就建立了两者间的数据关联?/p>
因此数据模?③通知接收方到反馈接收? XSL昄Q含表单Q→主题Q存储)"的实现流E如下:用户在页面上选择一条征求意见性通知后,该通知的JMSMessageID属性值将被传递给 FeedbackerSubServletlgQ该lg用这个属性值去匚w从主题取出的反馈消息的FeedbackSN属性,从而筛选出相关联的反馈 消息?/p>
那么一条征求意见性通知的JMSMessageID属性值又如何传递给FeedbackerSubServletlg呢?通过 ServletContext对象只能传递可预知信息QCWNF的做法是Q由XSL为每一条征求意见性通知讄一个独立的表单Qƈ把该通知? JMSMessageID属性值写在表单的TEXTAREA元素框内Q这L户在表单上选择一条征求意见性通知后,该通知的JMSMessageID属? 值就随表单一h交给FeedbackerSubServletlg。XSL有关代码如下Q?/p>
JMS应用pȝ与数据库pȝ有相似性,从数据方面看QJMS消息体的数据cd支持文本和对象,所以JMS更灵z,与XML集成应用的空间更大;但从 理上看QJMS Provider向管理员提供的管理功能远q低于DBMS提供的管理功能,因此在面向Web的应用中QJMS宜作Z流量、管理员参与度较低的信息pȝ 解决Ҏ?br>
转蝲自:http://gceclub.sun.com.cn/yuanchuang/2004_Q4/jms.html
作者:刘学?
1 Java技术与Java虚拟?/b>
说vJavaQh们首先想到的是Java~程语言Q然而事实上QJava是一U技术,它由四方面组? Java~程语言、JavacL件格式、Java虚拟机和Java应用E序接口(Java API)。它们的关系如下图所C:
? Java四个斚w的关p?/p>
q行期环境代表着Javaq_Q开发h员编写Java代码(.java文g)Q然后将之编译成字节?.class 文g)。最后字节码被装入内存,一旦字节码q入虚拟机,它就会被解释器解释执行,或者是被即时代码发生器有选择的{换成机器码执行。从上图也可以看? Javaq_由Java虚拟机和Java应用E序接口搭徏QJava语言则是q入q个q_的通道Q用Java语言~写q编译的E序可以q行在这个^C? q个q_的结构如下图所C:
在Javaq_的结构中, 可以看出QJava虚拟?JVM) 处在核心的位|,是程序与底层操作pȝ和硬件无关的关键。它的下ҎUL接口Q移植接口由两部分组成:适配器和Java操作pȝ, 其中依赖于^台的部分UCؓ适配器;JVM 通过UL接口在具体的q_和操作系l上实现Q在JVM 的上ҎJava的基本类库和扩展cd以及它们的APIQ?利用Java API~写的应用程?application) 和小E序(Java applet) 可以在Q何Javaq_上运行而无需考虑底层q_, 是因ؓ有Java虚拟?JVM)实现了程序与操作pȝ的分,从而实CJava 的^台无x?
那么到底什么是Java虚拟?JVM)呢?通常我们谈论JVMӞ我们的意思可能是Q?
对JVM规范的的抽象说明是一些概늚集合Q它们已l在书《The Java Virtual Machine Specification》(《Java虚拟范》)中被详细地描qCQ对JVM的具体实现要么是软gQ要么是软g和硬件的l合Q它已经被许多生产厂 商所实现Qƈ存在于多U^C上;q行JavaE序的Q务由JVM的运行期实例单个承担。在本文中我们所讨论的Java虚拟?JVM)主要针对W三U情 况而言。它可以被看成一个想象中的机器,在实际的计算Z通过软g模拟来实玎ͼ有自己想象中的硬Ӟ如处理器、堆栈、寄存器{,q有自己相应的指令系l?/p>
JVM在它的生存周期中有一个明的dQ那是q行JavaE序Q因此当JavaE序启动的时候,׃生JVM的一个实例;当程序运行结束的时候,该实例也跟着消失了。下面我们从JVM的体pȝ构和它的q行q程q两个方面来对它q行比较深入的研I?
2 Java虚拟机的体系l构
刚才已经提到QJVM可以׃同的厂商来实现。由于厂商的不同必然DJVM在实C的一些不同,然而JVMq是可以实现跨^台的Ҏ,q就要归功于设计JVM时的体系l构了?/p>
我们知道Q一个JVM实例的行Z光是它自q事,q涉及到它的子系l、存储区域、数据类型和指oq些部分Q它们描 qCJVM的一个抽象的内部体系l构Q其目的不光规定实现JVM时它内部的体pȝ构,更重要的是提供了一U方式,用于严格定义实现时的外部行ؓ。每? JVM都有两种机制Q一个是装蝲h合适名U的c?cL是接?Q叫做类装蝲子系l;另外的一个负责执行包含在已装载的cL接口中的指oQ叫做运行引擎? 每个JVM又包括方法区、堆、Java栈、程序计数器和本地方法栈q五个部分,q几个部分和c装载机制与q行引擎机制一L成的体系l构图ؓQ?/p>
? JVM的体pȝ?/p>
JVM的每个实例都有一个它自己的方法域和一个堆Q运行于JVM内的所有的U程都共享这些区域;当虚拟机装蝲cL? 的时候,它解析其中的二进制数据所包含的类信息Qƈ把它们放到方法域中;当程序运行的时候,JVM把程序初始化的所有对象置于堆上;而每个线E创建的? 候,都会拥有自己的程序计数器和Java栈,其中E序计数器中的值指向下一条即被执行的指令,U程的Java栈则存储U程调用JavaҎ的状态; 本地Ҏ调用的状态被存储在本地方法栈Q该Ҏ栈依赖于具体的实现?/p>
下面分别对这几个部分q行说明?/p>
执行引擎处于JVM的核心位|,在Java虚拟范中Q它的行为是由指令集所军_的。尽对于每条指令,规范很详 l地说明了当JVM执行字节码遇到指令时Q它的实现应该做什么,但对于怎么做却a之甚。Java虚拟机支持大U?48个字节码。每个字节码执行一U基? 的CPUq算,例如,把一个整数加到寄存器,子程序{Uȝ。Java指o集相当于JavaE序的汇~语a?/p>
Java指o集中的指令包含一个单字节的操作符,用于指定要执行的操作,q有0个或多个操作?提供操作所需的参数或数据。许多指令没有操作数,仅由一个单字节的操作符构成?/p>
虚拟机的内层循环的执行过E如?
do{
取一个操作符字节;
Ҏ操作W的值执行一个动?
}while(E序未结?
׃指opȝ的简单?使得虚拟机执行的q程十分?从而有利于提高执行的效率。指令中操作数的数量和大是由操作符军_的。如果操作数比一个字节大,那么它存储的序是高位字节优先。例?一?6位的参数存放时占用两个字?其gؓ:
W一个字?256+W二个字节字节码?
指o一般只是字节对齐的。指令tableswitch和lookup是例?在这两条指o内部要求强制?字节边界寚w?
对于本地Ҏ接口Q实现JVMq不要求一定要有它的支持,甚至可以完全没有。Sun公司实现Java本地? ?JNI)是出于可UL性的考虑Q当然我们也可以设计出其它的本地接口来代替Sun公司的JNI。但是这些设计与实现是比较复杂的事情Q需要确保垃圑֛ 收器不会那些正在被本地Ҏ调用的对象释放掉?
Java的堆是一个运行时数据?cȝ实例(对象)从中分配I间Q它的管理是由垃圑֛收来负责?不给E序员显式释攑֯象的能力。Java不规定具体用的垃圾回收法,可以Ҏpȝ的需求用各U各L法?
JavaҎZ传统语言中的~译后代码或是Unixq程中的正文D늱伹{它保存Ҏ代码(~译后的 java代码)和符可。在当前的Java实现?Ҏ代码不包括在垃圾回收堆中,但计划在来的版本中实现。每个类文g包含了一个JavacL一? Java界面的编译后的代码。可以说cL件是Java语言的执行代码文件。ؓ了保证类文g的^台无x?Java虚拟范中对类文g的格式也作了详细? 说明。其具体l节请参考Sun公司的Java虚拟范?
Java虚拟机的寄存器用于保存机器的q行状?与微处理器中的某些专用寄存器cM。Java虚拟机的寄存器有四种:
在上qCpȝ构图中,我们所说的是第一U,即程序计数器Q每个线E一旦被创徏拥有了自己的程序计数器。当U程执行JavaҎ的时候,它包含该U程正在被执行的指o的地址。但是若U程执行的是一个本地的ҎQ那么程序计数器的值就不会被定义?
Java虚拟机的栈有三个区域:局部变量区、运行环境区、操作数区?/p>
局部变量区
每个JavaҎ使用一个固定大的局部变量集。它们按照与vars寄存器的字偏U量来寻址。局部变量都?2? 的。长整数和双_ֺ点数占据了两个局部变量的I间,却按照第一个局部变量的索引来寻址?例如,一个具有烦引n的局部变?如果是一个双_ֺ点?? 么它实际占据了烦引n和n+1所代表的存储空?虚拟范ƈ不要求在局部变量中?4位的值是64位对齐的。虚拟机提供了把局部变量中的D载到操作? 栈的指o,也提供了把操作数栈中的值写入局部变量的指o?/p>
q行环境?/b>
在运行环境中包含的信息用于动态链?正常的方法返回以及异常捕捉?
动态链?/b>
q行环境包括Ҏ向当前类和当前方法的解释器符可的指?用于支持Ҏ代码的动态链接。方法的class 文g代码在引用要调用的方法和要访问的变量时用符受动态链接把W号形式的方法调用翻译成实际Ҏ调用,装蝲必要的类以解释还没有定义的符?q把变量 讉K译成与q些变量q行时的存储l构相应的偏Ud址。动态链接方法和变量使得Ҏ中用的其它cȝ变化不会影响到本E序的代码?
正常的方法返?/b>
如果当前Ҏ正常地结束了,在执行了一条具有正类型的q回指o?调用的方法会得到一个返回倹{执行环境在正常q回的情况下用于恢复调用者的寄存?q把调用者的E序计数器增加一个恰当的数?以蟩q已执行q的Ҏ调用指o,然后在调用者的执行环境中l执行下厅R?
异常捕捉
异常情况在Java中被UCError(错误)或Exception(异常),是Throwablecȝ子类,在程序中的原因是:①动态链接错,如无法找到所需的class文g。②q行旉,如对一个空指针的引用。程序用了throw语句?
当异常发生时,Java虚拟机采取如下措?
操作数栈?/b>
机器指o只从操作数栈中取操作?对它们进行操?q把l果q回到栈中。选择栈结构的原因?在只有少量寄存器或非 通用寄存器的机器(如Intel486)?也能够高效地模拟虚拟机的行ؓ。操作数栈是32位的。它用于l方法传递参?q从Ҏ接收l果,也用于支持操 作的参数,q保存操作的l果。例?iadd指o两个整数相加。相加的两个整数应该是操作数栈顶的两个字。这两个字是由先前的指o压进堆栈的。这两个? 数将从堆栈弹出、相?q把l果压回到操作数栈中?
每个原始数据cd都有专门的指令对它们q行必须的操作。每个操作数在栈中需要一个存储位|?除了long 和double?它们需要两个位|。操作数只能被适用于其cd的操作符所操作。例?压入两个intcd的数,如果把它们当作是一个longcd的数? 是非法的。在Sun的虚拟机实现?q个限制由字节码验证器强制实行。但?有少数操?操作Wdupe和swap),用于对运行时数据行操作时是不 考虑cd的?
本地Ҏ栈,当一个线E调用本地方法时Q它׃再受到虚拟机关于l构和安全限制方面的U束Q它既可以访? 虚拟机的q行期数据区Q也可以使用本地处理器以及Q何类型的栈。例如,本地栈是一个C语言的栈Q那么当CE序调用C函数Ӟ函数的参C某种序被压? 栈,l果则返回给调用函数。在实现Java虚拟机时Q本地方法接口用的是C语言的模型栈Q那么它的本地方法栈的调度与使用则完全与C语言的栈相同?/p>
3 Java虚拟机的q行q程
上面对虚拟机的各个部分进行了比较详细的说明,下面通过一个具体的例子来分析它的运行过E?/p>
虚拟机通过调用某个指定cȝҎmain启动Q传递给main一个字W串数组参数Q指定的类被装载,同时链接该类所使用的其它的cdQƈ且初始化它们。例如对于程序:
~译后在命o行模式下键入Q?java HelloApp run virtual machine
通过调用HelloApp的方法main来启动java虚拟机,传递给main一个包含三个字W串"run"?virtual"?machine"的数l。现在我们略q虚拟机在执行HelloApp时可能采取的步骤?
开始试图执行类HelloApp的mainҎQ发现该cdƈ没有被装载,也就是说虚拟机当前不包含该类的二 q制代表Q于是虚拟机使用ClassLoader试图Lq样的二q制代表。如果这个进E失败,则抛Z个异常。类被装载后同时在mainҎ被调用之 前,必须对类HelloApp与其它类型进行链接然后初始化。链接包含三个阶D:验,准备和解析。检验检查被装蝲的主cȝW号和语义,准备则创建类或接 口的静态域以及把这些域初始化ؓ标准的默认|解析负责查主cd其它cL接口的符号引用,在这一步它是可选的。类的初始化是对cM声明的静态初始化函数 和静态域的初始化构造方法的执行。一个类在初始化之前它的父类必须被初始化。整个过E如下:
?Q虚拟机的运行过E?/p>
4 l束?/b>
本文通过对JVM的体pȝ构的深入研究以及一个JavaE序执行时虚拟机的运行过E的详细分析Q意在剖析清楚Java虚拟机的机理?br>
转蝲自:http://gceclub.sun.com.cn/staticcontent/html/2004-04-09/jvm.html
作者简?/b>
盛戈歆,软g工程师,你可以通过shenggexin@topwaver.com与他联系?/p>
正文
Java中类的查找与装蝲出现的问题L会时不时出现在JavaE序员面前,qƈ不是什么丢脸的事情Q相信没有一? JavaE序员没遇到qClassNotException,因此不要人瞅见自׃犯这L错误而觉得不自然Q但是在如果出现? ClassNotFoundException后异常后一脸的茫然Q那我想你该了解一下java的类装蝲的体制了Q同时ؓ了进行下面的关于c装载器之间? 隔离性的讨论Q我们先单介l一下类装蝲的体pȝ构?/p>
1. Javac装载体pȝ?/b>
装蝲cȝq程非常单:查找cL在位|,q将扑ֈ的Javacȝ字节码装入内存,生成对应的Class对象? Java的类装蝲器专门用来实现这Lq程QJVMq不止有一个类装蝲器,事实上,如果你愿意的话,你可以让JVM拥有无数个类装蝲器,当然q除了测? JVM外,我想不出q有其他的用途。你应该已经发现Cq样一个问题,c装载器自n也是一个类Q它也需要被装蝲到内存中来,那么q些c装载器p来装? 呢,d有个根吧Q没错,实存在q样的根Q它是龙见首不见Bootstrap ClassLoader. Z么说它神龙见首不见尾呢,因ؓ你根本无法在Java代码中抓住哪怕是它的一点点的尾_管你能时时d体会到它的存在,因ؓjava的运行环境所需 要的所有类库,都由它来装蝲Q而它本n是C++写的E序Q可以独立运?可以说是JVM的运行v?伟大吧。在Bootstrap完成它的d后,会生? 一个AppClassLoader(实际上之前系l还会用扩展类装蝲器ExtClassLoaderQ它用于装蝲Javaq行环境扩展包中的类),q个 c装载器才是我们l常使用的,可以调用ClassLoader.getSystemClassLoader() 来获得,我们假定E序中没有用类装蝲器相x作设定或者自定义新的c装载器Q那么我们编写的所有javac通通会由它来装载,值得敬吧? AppClassLoader查找cȝ区域是耳熟能详的ClasspathQ也是初学者必跨q的门槛Q有没有灵光一闪的感觉Q我们按照它的类查找范围 l它取名为类路径c装载器。还是先前假定的情况Q当Java中出现新的类QAppClassLoader首先在类传递给它的父类c装载器Q也是 Extion ClassLoaderQ询问它是否能够装蝲该类Q如果能Q那AppClassLoader׃q这zMQ同样Extion ClassLoader在装载时Q也会先问问它的父类装蝲器。我们可以看出类装蝲器实际上是一个树状的l构图,每个c装载器有自q父亲Q类装蝲器在装蝲 cLQL先让自己的父c装载器装蝲(多么敬长辈),如果父类装蝲器无法装载该cLQ自己就会动手装载,如果它也装蝲不了Q那么对不vQ它会大喊一壎ͼ ExceptionQclass not found。有必要提一句,当由直接使用c\径装载器装蝲cd败抛出的是NoClassDefFoundException异常。如果用自定义的类装蝲 器loadClassҎ或者ClassLoader的findSystemClassҎ装蝲c,如果你不d意改变,那么抛出的是 ClassNotFoundException?/p>
我们短ȝ一下上面的讨论Q?/p>
1.JVMc装载器的体pȝ构可以看作是树状l构?/p>
2.父类装蝲器优先装载。在父类装蝲器装载失败的情况下再装蝲Q如果都装蝲p|则抛出ClassNotFoundException或者NoClassDefFoundError异常?/p>
那么我们的类在什么情况下被装载的呢?
2. cd何被装蝲在java2中,JVM是如何装载类的呢Q可以分ZU类型,一U是隐式的类装蝲Q一U式昑ּ的类装蝲?/p> 2.1 隐式的类装蝲
隐式的类装蝲是编码中最常用得方式:
A b = new A();
如果E序q行到这D代码时q没有Ac,那么JVM会请求装载当前类的类装器来装载类? 问题来了Q我把代码弄得复杂一点点Q但依旧没有M隑ֺQ请思考JVM得装载次序:
1 package test;
2 Public class A{
3 public void static main(String args[]){
4 B b Q?nbsp;new B();
5 }
6 }
7
8 class B{C c;}
9
10 class C{}
揭晓{案Q类装蝲的次序ؓA->BQ而类CҎ不会被JVM理会,先不要惊Ӟ仔细xQ这不正是我们最需? 得到的结果。我们仔l了解一下JVM装蝲序。当使用Java A命oq行AcLQJVM会首先要求类路径c装载器(AppClassLoader)装蝲Ac,但是q时只装载AQ不会装载A中出现的其他c?Bc?Q接 着它会调用A中的main函数Q直到运行语句b Q?new B()ӞJVM发现必须装蝲Bcȝ序才能l运行,于是c\径类装蝲器会去装载Bc,虽然我们可以看到B中有有Ccȝ声明Q但是ƈ不是实际的执行语句, 所以ƈ不去装蝲Cc,也就是说JVM按照q行时的有效执行语句Q来军_是否需要装载新c,从而装载尽可能的c,q一点和~译cL不相同的?/p>
2.2 昑ּ的类装蝲
使用昄的类装蝲Ҏ很多Q我们都装蝲ctest.AZ?/p>
使用ClasscȝforNameҎ。它可以指定装蝲器,也可以用装载当前类的装载器。例如:
Class.forName("test.A");
它的效果?br>Class.forName("test.A",true,this.getClass().getClassLoader());
是一L?br>
使用c\径类装蝲装蝲.
ClassLoader.getSystemClassLoader().loadClass("test.A");
使用当前q程上下文的使用的类装蝲器进行装载,q种装蝲cȝҎ常常被有着复杂c装载体pȝ构的pȝ所使用?/p>
Thread.currentThread().getContextClassLoader().loadClass("test.A")
使用自定义的c装载器装蝲c?/p>
MyClassLoaderl承了URLClassLoaderc,q是JDK核心包中的类装蝲器,在没有指定父c装载器的情况下Q类路径c装载器是它的父类装蝲器,MyClassLoaderq没有增加类的查找范_因此它和c\径装载器有相同的效果?/p>
我们已经知道Java的类装蝲器体pȝ构ؓ树状Q多个类装蝲器可以指定同一个类装蝲器作q父类Q每个子c装? 器就是树状结构的一个分支,当然它们又可以个有子c装载器c装载器Q类装蝲器也可以没有父类装蝲器,q时Bootstrapc装载器作为它的隐含父c, 实际上Bootstrapc装载器是所有类装蝲器的先Q也是树状结构的栏V这U树状体pȝ构,以及父类装蝲器优先的机制Qؓ我们~写自定义的c装载器? 供了便利Q同时可以让E序按照我们希望的方式进行类的装载。例如某个程序的c装载器体系l构囑֦下:
?Q某个程序的c装载器的结?/p>
解释一下上面的图,ClassLoaderA定义的类装蝲器,它的父类装蝲器ؓc\径装载器Q它有两个子c装? 器ClassLoaderAA和ClassLaderABQClassLoaderB为程序用的另外一个类装蝲器,它没有父c装载器Q但有一个子c装? 器ClassLoaderBB。你可能会说Q见|我的E序怎么会用这么复杂的c装载器l构。ؓ了进行下面的讨论Q暂且委屈一下?/p>
3. 奇怪的隔离?/b>
我们不难发现Q图2中的c装载器AA和ABQ? AB和BBQAA和B{等位于不同分支下,他们之间没有父子关系Q我不知道如何定义这U关p,姑且UC们位于不同分支下。两个位于不同分支的c装载器h 隔离性,q种隔离性得在分别使用它们装蝲同一个类Q也会在内存中出C个Classcȝ实例。因h隔离性的c装载器装蝲的类不会׃n内存I间Q 得用一个类装蝲器不可能完成的Q务变得可以轻而易举,例如cȝ静态变量可能同时拥有多个|虽然好像作用不大Q,因ؓq是被装蝲cȝ同一静态变量,? 们也被保存不同的内存空_又例如程序需要用某些包Q但又不希望被程序另外一些包所使用Q很单,~写自定义的c装载器。类装蝲器的q种隔离性在许多 大型的Y件应用和服务E序得到了很好的应用。下面是同一个类静态变量ؓ不同值的例子?/p>
my static field b is 1
my static field b is 2
E序的结果非常有意思,从编E者的角度Q我们甚臛_以把不在同一个分支的c装载器看作不同的java虚拟机,因ؓ? 们彼此觉察不到对方的存在。程序在使用h分支的类装蝲的体pȝ构时要非常小心,弄清楚每个类装蝲器的cL找范_量避免父类装蝲器和子类装蝲器的cL 找范围中有相同类名的c(包括包名和类名)Q下面这个例子就是用来说明这U情况可能带来的问题?/p>
假设有相同名字却不同版本的接?AQ?/p>
版本 1Q?br>package test;
Intefer Same{ public String getVersion(); }
版本 2Q?br>Package test;
Intefer Same{ public String getName(); }
接口A两个版本的实玎ͼ
版本1的实?br>package test;
public class Same1Impl implements Same {
public String getVersion(){ return "A version 1";}
}
版本2的实?br>public class Same 2Impl implements Same {
public String getName(){ return "A version 2";}
}
我们依然使用?的类装蝲器结构,首先版?的Same和Same的实现类Same1Impl打成? same1.jarQ将版本2的Same和Same的实现类Same1Impl打成包same2.jar。现在,做这L事情Q把same1.jar攑օ c装载器ClassLoaderA的类查找范围中,把same2.jar攑օc装器ClassLoaderAB的类查找范围中。当你兴冲冲的运行下面这? 看似正确的程序?/p>
实际上这个错误的是由父类载器优先装蝲的机刉成Q当c装载器ClassLoaderAB在装载Same2Impl cL发现必须装蝲接口test.SameQ于是按规定h父类装蝲器装载,父类装蝲器发C版本1的test.Same接口q兴冲冲的装载,但是却想不到 Same2Impl所希望的是版本2 的test.SameQ后面的事情可想而知了,异常被抛出?/p>
我们很难责怪Java中暂时ƈ没有提供区分版本的机Ӟ如果使用了比较复杂的c装载器体系l构Q在出现了某个包或者类的多个版本时Q应特别注意?/p>
掌握和灵z运用Java的类装蝲器的体系l构Q对E序的系l设计,E序的实玎ͼ已经E序的调试,都有相当大的帮助。希望以上的内容能够Ҏ有所帮助.
转蝲自:http://gceclub.sun.com.cn/yuanchuang/week-9/classloader.html