﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-Cool eye-随笔分类-Java</title><link>http://www.blogjava.net/joeyeezhang/category/7217.html</link><description /><language>zh-cn</language><lastBuildDate>Sat, 23 Jul 2011 14:13:45 GMT</lastBuildDate><pubDate>Sat, 23 Jul 2011 14:13:45 GMT</pubDate><ttl>60</ttl><item><title>21种代码的“坏味道”</title><link>http://www.blogjava.net/joeyeezhang/archive/2007/01/16/94259.html</link><dc:creator>joeyeezhang</dc:creator><author>joeyeezhang</author><pubDate>Tue, 16 Jan 2007 09:48:00 GMT</pubDate><guid>http://www.blogjava.net/joeyeezhang/archive/2007/01/16/94259.html</guid><wfw:comment>http://www.blogjava.net/joeyeezhang/comments/94259.html</wfw:comment><comments>http://www.blogjava.net/joeyeezhang/archive/2007/01/16/94259.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/joeyeezhang/comments/commentRss/94259.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/joeyeezhang/services/trackbacks/94259.html</trackback:ping><description><![CDATA[<span id="ArticleContent1_ArticleContent1_lblContent">&nbsp;
<p>1．Duplicated Code<br />代码重复几乎是最常见的异味了。他也是Refactoring 的主要目标之一。代码重复往<br />往来自于copy-and-paste 的编程风格。与他相对应OAOO 是一个好系统的重要标志<br />（请参见我的duplicated code 一文：<a href="http://www.erptao.org/download.php?op=viewsdownload&amp;sid=6">http://www.erptao.org/download.php?op=viewsdownload&amp;sid=6</a>）。</p><p><br />2．Long method<br />它是传统结构化的&#8220;遗毒&#8220;。一个方法应当具有自我独立的意图，不要把几个意图<br />放在一起，我的《大类和长方法》一文中有详细描述。</p><p><br />3．Large Class<br />大类就是你把太多的责任交给了一个类。这里的规则是One Class One Responsibility。</p><p>4．Divergent Change<br />一个类里面的内容变化率不同。某些状态一个小时变一次，某些则几个月一年才变<br />一次；某些状态因为这方面的原因发生变化，而另一些则因为其他方面的原因变一次。<br />面向对象的抽象就是把相对不变的和相对变化相隔离。把问题变化的一方面和另一<br />方面相隔离。这使得这些相对不变的可以重用。问题变化的每个方面都可以单独重用。<br />这种相异变化的共存使得重用非常困难。</p><p><br />5．Shotgun Surgery<br />这正好和上面相反。对系统一个地方的改变涉及到其他许多地方的相关改变。这些<br />变化率和变化内容相似的状态和行为通常应当放在同一个类中。</p><p><br />6．Feature Envy<br />对象的目的就是封装状态以及与这些状态紧密相关的行为。如果一个类的方法频繁<br />用get 方法存取其他类的状态进行计算，那么你要考虑把行为移到涉及状态数目最多的<br />那个类。</p><p><br />7．Data Clumps<br />某些数据通常像孩子一样成群玩耍：一起出现在很多类的成员变量中，一起出现在<br />许多方法的参数中&#8230;..，这些数据或许应该自己独立形成对象。</p><p><br />8．Primitive Obsession<br />面向对象的新手通常习惯使用几个原始类型的数据来表示一个概念。譬如对于范围，<br />他们会使用两个数字。对于Money，他们会用一个浮点数来表示。因为你没有使用对象<br />来表达问题中存在的概念，这使得代码变的难以理解，解决问题的难度大大增加。<br />好的习惯是扩充语言所能提供原始类型，用小对象来表示范围、金额、转化率、邮<br />政编码等等。</p><p><br />9．Switch Statement<br />基于常量的开关语句是OO 的大敌，你应当把他变为子类、state 或strategy。</p><p><br />10． Parallel Inheritance Hierarchies<br />并行的继承层次是shotgun surgery 的特殊情况。因为当你改变一个层次中的某一个<br />类时，你必须同时改变另外一个层次的并行子类。</p><p><br />11． Lazy Class<br />一个干活不多的类。类的维护需要额外的开销，如果一个类承担了太少的责任，应<br />当消除它。</p><p><br />12． Speculative Generality<br />一个类实现了从未用到的功能和通用性。通常这样的类或方法唯一的用户是test<br />case。不要犹豫，删除它。</p><p><br />13． Temporary Field<br />一个对象的属性可能只在某些情况下才有意义。这样的代码将难以理解。专门建立<br />一个对象来持有这样的孤儿属性，把只和他相关的行为移到该类。最常见的是一个特定<br />的算法需要某些只有该算法才有用的变量。</p><p><br />14． Message Chain<br />消息链发生于当一个客户向一个对象要求另一个对象，然后客户又向这另一对象要<br />求另一个对象，再向这另一个对象要求另一个对象，如此如此。这时，你需要隐藏分派。</p><p><br />15． Middle Man<br />对象的基本特性之一就是封装，而你经常会通过分派去实现封装。但是这一步不能走得太远，如果你发现一个类接口的一大半方法都在做分派，你可能需要移去这个中间<br />人。</p><p><br />16． Inappropriate Intimacy<br />某些类相互之间太亲密，它们花费了太多的时间去砖研别人的私有部分。对人类而<br />言，我们也许不应该太假正经，但我们应当让自己的类严格遵守禁欲主义。</p><p><br />17． Alternative Classes with Different Interfaces<br />做相同事情的方法有不同的函数signature，一致把它们往类层次上移，直至协议一<br />致。</p><p><br />18． Incomplete Library Class<br />要建立一个好的类库非常困难。我们大量的程序工作都基于类库实现。然而，如此<br />广泛而又相异的目标对库构建者提出了苛刻的要求。库构建者也不是万能的。有时候我<br />们会发现库类无法实现我们需要的功能。而直接对库类的修改有非常困难。这时候就需<br />要用各种手段进行Refactoring。</p><p><br />19． Data Class<br />对象包括状态和行为。如果一个类只有状态没有行为，那么肯定有什么地方出问题<br />了。</p><p><br />20． Refused Bequest<br />超类传下来很多行为和状态，而子类只是用了其中的很小一部分。这通常意味着你<br />的类层次有问题。</p><p><br />21． Comments<br />经常觉得要写很多注释表示你的代码难以理解。如果这种感觉太多，表示你需要<br />Refactoring。</p></span> <a href="http://www.imaiyi.com/">卖艺网提供</a><img src ="http://www.blogjava.net/joeyeezhang/aggbug/94259.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/joeyeezhang/" target="_blank">joeyeezhang</a> 2007-01-16 17:48 <a href="http://www.blogjava.net/joeyeezhang/archive/2007/01/16/94259.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java线程总结 </title><link>http://www.blogjava.net/joeyeezhang/archive/2006/12/26/90183.html</link><dc:creator>joeyeezhang</dc:creator><author>joeyeezhang</author><pubDate>Tue, 26 Dec 2006 14:45:00 GMT</pubDate><guid>http://www.blogjava.net/joeyeezhang/archive/2006/12/26/90183.html</guid><wfw:comment>http://www.blogjava.net/joeyeezhang/comments/90183.html</wfw:comment><comments>http://www.blogjava.net/joeyeezhang/archive/2006/12/26/90183.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/joeyeezhang/comments/commentRss/90183.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/joeyeezhang/services/trackbacks/90183.html</trackback:ping><description><![CDATA[
		<p>首先要理解线程首先需要了解一些基本的东西，我们现在所使用的大多数操作系统都属于多任务，分时操作系统。正是由于这种操作系统的出现才有了多线程这个概念。我们使用的windows,linux就属于此列。什么是分时操作系统呢，通俗一点与就是可以同一时间执行多个程序的操作系统，在自己的电脑上面，你是不是一边听歌，一边聊天还一边看网页呢？但实际上，并不上cpu在同时执行这些程序，cpu只是将时间切割为时间片，然后将时间片分配给这些程序，获得时间片的程序开始执行，不等执行完毕，下个程序又获得时间片开始执行，这样多个程序轮流执行一段时间，由于现在cpu的高速计算能力，给人的感觉就像是多个程序在同时执行一样。<br />一般可以在同一时间内执行多个程序的操作系统都有进程的概念.一个进程就是一个执行中的程序,而每一个进程都有自己独立的一块内存空间,一组系统资源.在进程概念中,每一个进程的内部数据和状态都是完全独立的.因此可以想像创建并执行一个进程的系统开像是比较大的，所以线程出现了。在java中，程序通过流控制来执行程序流,程序中单个顺序的流控制称为线程,多线程则指的是在单个程序中可以同时运行多个不同的线程,执行不同的任务.多线程意味着一个程序的多行语句可以看上去几乎在同一时间内同时运行.（你可以将前面一句话的程序换成进程，进程是程序的一次执行过程,是系统运行程序的基本单位）<br /><br />线程与进程相似,是一段完成某个特定功能的代码,是程序中单个顺序的流控制;但与进程不同的是,同类的多个线程是共享一块内存空间和一组系统资源,而线程本身的数据通常只有微处理器的寄存器数据,以及一个供程序执行时使用的堆栈.所以系统在产生一个线程,或者在各个线程之间切换时,负担要比进程小的多,正因如此,线程也被称为轻负荷进程(light-weight process).一个进程中可以包含多个线程.<br /><br />多任务是指在一个系统中可以同时运行多个程序,即有多个独立运行的任务,每个任务对应一个进程，同进程一样,一个线程也有从创建,运行到消亡的过程,称为线程的生命周期.用线程的状态(state)表明线程处在生命周期的哪个阶段.线程有创建,可运行,运行中,阻塞,死亡五中状态.通过线程的控制与调度可使线程在这几种状态间转化每个程序至少自动拥有一个线程,称为主线程.当程序加载到内存时,启动主线程.<br /><br />[线程的运行机制以及调度模型] <br />java中多线程就是一个类或一个程序执行或管理多个线程执行任务的能力，每个线程可以独立于其他线程而独立运行，当然也可以和其他线程协同运行，一个类控制着它的所有线程，可以决定哪个线程得到优先级，哪个线程可以访问其他类的资源，哪个线程开始执行，哪个保持休眠状态。<br />下面是线程的机制图：<br /><img onmouseover="javascript:imgShowTip(this);" style="DISPLAY: inline" onclick="javascript:imgClick(this);" alt="image" src="http://blog.matrix.org.cn/resources/Kaizen/Thread.jpg" onload="javascript:imgLoad(this);" border="0" resized="0" /><br /><br />线程的状态表示线程正在进行的活动以及在此时间段内所能完成的任务.线程有创建,可运行,运行中,阻塞,死亡五中状态.一个具有生命的线程,总是处于这五种状态之一：<br /><strong>1.创建状态</strong><br />使用new运算符创建一个线程后,该线程仅仅是一个空对象,系统没有分配资源,称该线程处于创建状态(new thread)<br /><strong>2.可运行状态</strong><br />使用start()方法启动一个线程后,系统为该线程分配了除CPU外的所需资源,使该线程处于可运行状态(Runnable)<br /><strong>3.运行中状态</strong><br />Java运行系统通过调度选中一个Runnable的线程,使其占有CPU并转为运行中状态(Running).此时,系统真正执行线程的run()方法.<br /><strong>4.阻塞状态</strong><br />一个正在运行的线程因某种原因不能继续运行时,进入阻塞状态(Blocked)<br /><strong>5.死亡状态</strong><br />线程结束后是死亡状态(Dead)<br /><br />同一时刻如果有多个线程处于可运行状态,则他们需要排队等待CPU资源.此时每个线程自动获得一个线程的优先级(priority),优先级的高低反映线程的重要或紧急程度.可运行状态的线程按优先级排队,线程调度依据优先级基础上的"先到先服务"原则.<br />线程调度管理器负责线程排队和CPU在线程间的分配,并由线程调度算法进行调度.当线程调度管理器选种某个线程时,该线程获得CPU资源而进入运行状态.<br /><br />线程调度是先占式调度,即如果在当前线程执行过程中一个更高优先级的线程进入可运行状态,则这个线程立即被调度执行.先占式调度分为:独占式和分时方式.<br />独占方式下,当前执行线程将一直执行下去,直 到执行完毕或由于某种原因主动放弃CPU,或CPU被一个更高优先级的线程抢占<br />分时方式下,当前运行线程获得一个时间片,时间到时,即使没有执行完也要让出CPU,进入可运行状态,等待下一个时间片的调度.系统选中其他可运行状态的线程执行<br />分时方式的系统使每个线程工作若干步,实现多线程同时运行<br /><br />另外请注意下面的线程调度规则（如果有不理解，不急，往下看）：<br />①如果两个或是两个以上的线程都修改一个对象，那么把执行修改的方法定义为被同步的（Synchronized）,如果对象更新影响到只读方法，那么只度方法也应该定义为同步的<br />②如果一个线程必须等待一个对象状态发生变化，那么它应该在对象内部等待，而不是在外部等待，它可以调用一个被同步的方法，并让这个方法调用wait()<br />③每当一个方法改变某个对象的状态的时候，它应该调用notifyAll()方法，这给等待队列的线程提供机会来看一看执行环境是否已发生改变<br />④记住wait(),notify(),notifyAll()方法属于Object类，而不是Thread类，仔细检查看是否每次执行wait()方法都有相应的notify()或notifyAll()方法，且它们作用与相同的对象 在java中每个类都有一个主线程，要执行一个程序，那么这个类当中一定要有main方法，这个man方法也就是java class中的主线程。你可以自己创建线程，有两种方法，一是继承Thread类，或是实现Runnable接口。一般情况下，最好避免继承，因为java中是单根继承，如果你选用继承，那么你的类就失去了弹性，当然也不能全然否定继承Thread,该方法编写简单,可以直接操作线程,适用于单重继承情况。至于选用那一种，具体情况具体分析。<br /><br /><br /><strong>eg.继承Thread</strong><br /></p>
		<pre class="overflow">public class MyThread_1 extends Thread<br />{<br />public void run()<br />{<br />//some code <br />}<br />}</pre>
		<p>
				<br />
				<br />
				<strong>eg.实现Runnable接口</strong>
				<br />
		</p>
		<pre class="overflow">public class MyThread_2 implements Runnable<br />{<br />public void run()<br />{<br />//some code <br />}<br />}</pre>
		<p>
				<br />
				<br />
				<br />当使用继承创建线程，这样启动线程：<br /></p>
		<pre class="overflow">new MyThread_1().start()</pre>
		<p>
				<br />
				<br />当使用实现接口创建线程，这样启动线程：<br /></p>
		<pre class="overflow">new Thread(new MyThread_2()).start()</pre>
		<p>
				<br />
				<br />注意，其实是创建一个线程实例，并以实现了Runnable接口的类为参数传入这个实例，当执行这个线程的时候，MyThread_2中run里面的代码将被执行。<br />下面是完成的例子：<br /><br /></p>
		<pre class="overflow">public class MyThread implements Runnable<br />{ <br /><br />public void run()<br />{ <br />System.out.println("My Name is "+Thread.currentThread().getName()); <br />} <br />public static void main(String[] args)<br />{<br />new Thread(new MyThread()).start(); <br />}<br />} </pre>
		<p>
				<br />
				<br />
				<br />执行后将打印出：<br /><span style="COLOR: blue">My Name is Thread-0</span><br /><br />你也可以创建多个线程，像下面这样<br /></p>
		<pre class="overflow">new Thread(new MyThread()).start();<br />new Thread(new MyThread()).start();<br />new Thread(new MyThread()).start();</pre>
		<p>
				<br />
				<br />
				<br />那么会打印出：<br /><span style="COLOR: blue">My Name is Thread-0<br />My Name is Thread-1<br />My Name is Thread-2</span><br /><br />看了上面的结果，你可能会认为线程的执行顺序是依次执行的，但是那只是一般情况，千万不要用以为是线程的执行机制；影响线程执行顺序的因素有几点：首先看看前面提到的优先级别<br /><br /><br /></p>
		<pre class="overflow">public class MyThread implements Runnable<br />{ <br /><br />public void run()<br />{ <br />System.out.println("My Name is "+Thread.currentThread().getName()); <br />} <br />public static void main(String[] args)<br />{<br />Thread t1=new Thread(new MyThread());<br />Thread t2=new Thread(new MyThread());<br />Thread t3=new Thread(new MyThread());<br />t2.setPriority(Thread.MAX_PRIORITY);//赋予最高优先级<br />t1.start();<br />t2.start();<br />t3.start();<br />}<br />} </pre>
		<p>
				<br />
				<br />再看看结果：<br /><span style="COLOR: blue">My Name is Thread-1<br />My Name is Thread-0<br />My Name is Thread-2</span><br /><br /><br />线程的优先级分为10级，分别用1到10的整数代表，默认情况是5。上面的t2.setPriority(Thread.MAX_PRIORITY)等价与t2.setPriority(10）<br />然后是线程程序本身的设计，比如使用sleep,yield,join，wait等方法（详情请看JDKDocument)<br /><br /></p>
		<pre class="overflow">public class MyThread implements Runnable<br />{ <br />public void run()<br />{ <br />try<br />{<br />int sleepTime=(int)(Math.random()*100);//产生随机数字，<br />Thread.currentThread().sleep(sleepTime);//让其休眠一定时间，时间又上面sleepTime决定<br />//public static void sleep(long millis)throw InterruptedException （API）<br />System.out.println(Thread.currentThread().getName()+" 睡了 "+sleepTime);<br />}catch(InterruptedException ie)//由于线程在休眠可能被中断，所以调用sleep方法的时候需要捕捉异常<br />{<br />ie.printStackTrace();<br />} <br />} <br />public static void main(String[] args)<br />{<br />Thread t1=new Thread(new MyThread());<br />Thread t2=new Thread(new MyThread());<br />Thread t3=new Thread(new MyThread());<br />t1.start();<br />t2.start();<br />t3.start();<br />}<br />}</pre>
		<p>
				<br />
				<br />执行后观察其输出：<br /><br /><span style="COLOR: blue">Thread-0 睡了 11<br />Thread-2 睡了 48<br />Thread-1 睡了 69</span><br /><br /><br /><br />上面的执行结果是随机的，再执行很可能出现不同的结果。由于上面我在run中添加了休眠语句，当线程休眠的时候就会让出cpu，cpu将会选择执行处于runnable状态中的其他线程，当然也可能出现这种情况，休眠的Thread立即进入了runnable状态，cpu再次执行它。<br />[线程组概念]<br />线程是可以被组织的，java中存在线程组的概念，每个线程都是一个线程组的成员,线程组把多个线程集成为一个对象,通过线程组可以同时对其中的多个线程进行操作,如启动一个线程组的所有线程等.Java的线程组由java.lang包中的Thread——Group类实现.<br />ThreadGroup类用来管理一组线程,包括:线程的数目,线程间的关系,线程正在执行的操作,以及线程将要启动或终止时间等.线程组还可以包含线程组.在Java的应用程序中,最高层的线程组是名位main的线程组,在main中还可以加入线程或线程组,在mian的子线程组中也可以加入线程和线程组,形成线程组和线程之间的树状继承关系。像上面创建的线程都是属于main这个线程组的。<br />借用上面的例子，main里面可以这样写：<br /><br /></p>
		<pre class="overflow">public static void main(String[] args)<br />{<br />/***************************************<br />ThreadGroup(String name) <br />ThreadGroup(ThreadGroup parent, String name) <br />***********************************/<br />ThreadGroup group1=new ThreadGroup("group1");<br />ThreadGroup group2=new ThreadGroup(group1,"group2");<br />Thread t1=new Thread(group2,new MyThread());<br />Thread t2=new Thread(group2,new MyThread());<br />Thread t3=new Thread(group2,new MyThread());<br />t1.start();<br />t2.start();<br />t3.start();<br />}</pre>
		<p>
				<br />
				<br />
				<br />线程组的嵌套，t1,t2,t3被加入group2,group2加入group1。<br />另外一个比较多就是关于线程同步方面的，试想这样一种情况，你有一笔存款在银行，你在一家银行为你的账户存款，而你的妻子在另一家银行从这个账户提款，现在你有1000块在你的账户里面。你存入了1000，但是由于另一方也在对这笔存款进行操作，人家开始执行的时候只看到账户里面原来的1000元，当你的妻子提款1000元后，你妻子所在的银行就认为你的账户里面没有钱了，而你所在的银行却认为你还有2000元。<br />看看下面的例子：<br /><br /></p>
		<pre class="overflow">class BlankSaving //储蓄账户<br />{<br />private static int money=10000;<br />public void add(int i)<br />{<br />money=money+i;<br />System.out.println("Husband 向银行存入了 [￥"+i+"]");<br />}<br />public void get(int i)<br />{<br />money=money-i;<br />System.out.println("Wife 向银行取走了 [￥"+i+"]");<br />if(money&lt;0)<br />System.out.println("余额不足！"); <br />}<br />public int showMoney()<br />{<br />return money;<br />}<br />} <br /><br /><br />class Operater implements Runnable<br />{<br />String name;<br />BlankSaving bs;<br />public Operater(BlankSaving b,String s)<br />{<br />name=s;<br />bs=b;<br /><br /><br /><br />}<br />public static void oper(String name,BlankSaving bs)<br />{<br /><br /><br /><br />if(name.equals("husband"))<br />{<br />try<br />{<br />for(int i=0;i&lt;10;i++)<br />{<br />Thread.currentThread().sleep((int)(Math.random()*300));<br />bs.add(1000);<br />}<br />}catch(InterruptedException e){}<br />}else<br />{<br />try<br />{<br /><br /><br /><br />for(int i=0;i&lt;10;i++)<br />{<br />Thread.currentThread().sleep((int)(Math.random()*300));<br />bs.get(1000);<br />}<br />}catch(InterruptedException e){}<br />}<br />}<br />public void run()<br />{<br />oper(name,bs);<br />} <br />}<br />public class BankTest <br />{<br />public static void main(String[] args)throws InterruptedException<br />{<br />BlankSaving bs=new BlankSaving();<br />Operater o1=new Operater(bs,"husband");<br />Operater o2=new Operater(bs,"wife");<br />Thread t1=new Thread(o1);<br />Thread t2=new Thread(o2);<br />t1.start();<br />t2.start();<br />Thread.currentThread().sleep(500);<br />}<br /><br /><br /><br />}</pre>
		<p>
				<br />
				<br />
				<br />下面是其中一次的执行结果：<br /><br /><br /><br /><span style="COLOR: blue">---------first--------------<br />Husband 向银行存入了 [￥1000]<br />Wife 向银行取走了 [￥1000]<br />Wife 向银行取走了 [￥1000]<br />Husband 向银行存入了 [￥1000]<br />Wife 向银行取走了 [￥1000]<br />Husband 向银行存入了 [￥1000]<br />Wife 向银行取走了 [￥1000]<br />Husband 向银行存入了 [￥1000]<br />Wife 向银行取走了 [￥1000]<br />Husband 向银行存入了 [￥1000]<br />Husband 向银行存入了 [￥1000]<br />Wife 向银行取走了 [￥1000]<br />Husband 向银行存入了 [￥1000]<br />Husband 向银行存入了 [￥1000]<br />Wife 向银行取走了 [￥1000]<br />Wife 向银行取走了 [￥1000]<br />Husband 向银行存入了 [￥1000]<br />Wife 向银行取走了 [￥1000]<br />Wife 向银行取走了 [￥1000]<br />Husband 向银行存入了 [￥1000]</span><br /><br />看到了吗，这可不是正确的需求，在husband还没有结束操作的时候，wife就插了进来，这样很可能导致意外的结果。解决办法很简单，就是将对数据进行操作方法声明为synchronized,当方法被该关键字声明后，也就意味着，如果这个数据被加锁，只有一个对象得到这个数据的锁的时候该对象才能对这个数据进行操作。也就是当你存款的时候，这笔账户在其他地方是不能进行操作的，只有你存款完毕，银行管理人员将账户解锁，其他人才能对这个账户进行操作。<br />修改public static void oper(String name,BlankSaving bs)为public static void oper(String name,BlankSaving bs)，再看看结果:<br /><br /><span style="COLOR: blue">Husband 向银行存入了 [￥1000]<br />Husband 向银行存入了 [￥1000]<br />Husband 向银行存入了 [￥1000]<br />Husband 向银行存入了 [￥1000]<br />Husband 向银行存入了 [￥1000]<br />Husband 向银行存入了 [￥1000]<br />Husband 向银行存入了 [￥1000]<br />Husband 向银行存入了 [￥1000]<br />Husband 向银行存入了 [￥1000]<br />Husband 向银行存入了 [￥1000]<br />Wife 向银行取走了 [￥1000]<br />Wife 向银行取走了 [￥1000]<br />Wife 向银行取走了 [￥1000]<br />Wife 向银行取走了 [￥1000]<br />Wife 向银行取走了 [￥1000]<br />Wife 向银行取走了 [￥1000]<br />Wife 向银行取走了 [￥1000]<br />Wife 向银行取走了 [￥1000]<br />Wife 向银行取走了 [￥1000]<br />Wife 向银行取走了 [￥1000]</span><br /><br /><br /><br />当丈夫完成操作后，妻子才开始执行操作，这样的话，对共享对象的操作就不会有问题了。<br />[wait and notify]<br />你可以利用这两个方法很好的控制线程的执行流程，当线程调用wait方法后，线程将被挂起，直到被另一线程唤醒（notify）或则是如果wait方法指定有时间得话，在没有被唤醒的情况下，指定时间时间过后也将自动被唤醒。但是要注意一定，被唤醒并不是指马上执行，而是从组塞状态变为可运行状态，其是否运行还要看cpu的调度。<br />事例代码：<br /><br /></p>
		<pre class="overflow">class MyThread_1 extends Thread<br />{<br />Object lock;<br />public MyThread_1(Object o)<br />{<br />lock=o;<br />}<br />public void run()<br />{<br />try<br />{<br />synchronized(lock)<br />{<br />System.out.println("Enter Thread_1 and wait");<br />lock.wait();<br />System.out.println("be notified");<br />}<br />}catch(InterruptedException e){}<br />}<br />}<br />class MyThread_2 extends Thread<br />{<br />Object lock;<br />public MyThread_2(Object o)<br />{<br />lock=o;<br />}<br />public void run()<br />{<br />synchronized(lock)<br />{<br />System.out.println("Enter Thread_2 and notify");<br />lock.notify();<br />}<br />}<br />}<br />public class MyThread<br />{ <br />public static void main(String[] args)<br />{<br />int[] in=new int[0];//notice<br />MyThread_1 t1=new MyThread_1(in);<br />MyThread_2 t2=new MyThread_2(in);<br />t1.start();<br />t2.start();<br />}<br />}</pre>
		<p>执行结果如下：<br /><span style="COLOR: blue">Enter Thread_1 and wait<br />Enter Thread_2 and notify<br />Thread_1 be notified</span><br /><br />可能你注意到了在使用wait and notify方法得时候我使用了synchronized块来包装这两个方法，这是由于调用这两个方法的时候线程必须获得锁，也就是上面代码中的lock[]，如果你不用synchronized包装这两个方法的得话，又或则锁不一是同一把，比如在MyThread_2中synchronized(lock)改为synchronized(this),那么执行这个程序的时候将会抛出java.lang.IllegalMonitorStateException执行期异常。另外wait and notify方法是Object中的，并不在Thread这个类中。最后你可能注意到了这点：int[] in=new int[0];为什么不是创建new Object而是一个0长度的数组，那是因为在java中创建一个0长度的数组来充当锁更加高效。<br /><br />Thread作为java中一重要组成部分，当然还有很多地方需要更深刻的认识，上面只是对Thread的一些常识和易错问题做了一个简要的总结，若要真正的掌握java的线程，还需要自己多做总结 </p>
<img src ="http://www.blogjava.net/joeyeezhang/aggbug/90183.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/joeyeezhang/" target="_blank">joeyeezhang</a> 2006-12-26 22:45 <a href="http://www.blogjava.net/joeyeezhang/archive/2006/12/26/90183.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java中如何正确使用字体编码</title><link>http://www.blogjava.net/joeyeezhang/archive/2006/12/21/89362.html</link><dc:creator>joeyeezhang</dc:creator><author>joeyeezhang</author><pubDate>Thu, 21 Dec 2006 13:44:00 GMT</pubDate><guid>http://www.blogjava.net/joeyeezhang/archive/2006/12/21/89362.html</guid><wfw:comment>http://www.blogjava.net/joeyeezhang/comments/89362.html</wfw:comment><comments>http://www.blogjava.net/joeyeezhang/archive/2006/12/21/89362.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/joeyeezhang/comments/commentRss/89362.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/joeyeezhang/services/trackbacks/89362.html</trackback:ping><description><![CDATA[
		<p class="a14">在Java编程中，中文字体编码难倒了不少程序员，如果抓住了影响Java中文显示的几个关键因素，问题将迎刃而解。 
</p>
		<p class="a14">Java是目前最流行的面向对象的编程语言之一，Java支持UTF-8、ISO-8 
</p>
		<table style="MARGIN: 10px 7px 3px 4px" cellspacing="0" cellpadding="0" align="left" border="0">
				<tbody>
						<tr>
								<td>
										<script language="JavaScript1.1" src="http://ad.ccw.com.cn/adshow.asp?positionID=38&amp;js=1&amp;innerJs=1">
										</script>
										<object codebase="http://active.macromedia.com/flash2/cabs/swflash.cab#version=4,0,0,0" height="300" width="360" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000">
												<param name="_cx" value="9525" />
												<param name="_cy" value="7938" />
												<param name="FlashVars" value="" />
												<param name="Movie" value="http://imgs.ccw.com.cn/200606/IBM_060613_tuiguang_soft_360_300.swf" />
												<param name="Src" value="http://imgs.ccw.com.cn/200606/IBM_060613_tuiguang_soft_360_300.swf" />
												<param name="WMode" value="Window" />
												<param name="Play" value="-1" />
												<param name="Loop" value="-1" />
												<param name="Quality" value="High" />
												<param name="SAlign" value="" />
												<param name="Menu" value="-1" />
												<param name="Base" value="" />
												<param name="AllowScriptAccess" value="" />
												<param name="Scale" value="ShowAll" />
												<param name="DeviceFont" value="0" />
												<param name="EmbedMovie" value="0" />
												<param name="BGColor" value="" />
												<param name="SWRemote" value="" />
												<param name="MovieData" value="" />
												<param name="SeamlessTabbing" value="1" />
												<param name="Profile" value="0" />
												<param name="ProfileAddress" value="" />
												<param name="ProfilePort" value="0" />
												<param name="AllowNetworking" value="all" />
												<param name="AllowFullScreen" value="false" />
												<embed src="http://imgs.ccw.com.cn/200606/IBM_060613_tuiguang_soft_360_300.swf" quality="high" pluginspage="http://www.macromedia.com/shockwave/download/index.cgi?P1_Prod_Version=ShockwaveFlash" type="application/x-shockwave-flash" width="360" height="300">
												</embed>
										</object>
								</td>
						</tr>
				</tbody>
		</table>859-1、GBK等各种字体编码，可笔者发现Java中字体编码的问题仍难倒了不少程序员，网上虽然也有不少关于在Java中如何正确显示中文的文章，但都不够全面，笔者特意总结如下。 
<p class="a14">影响Java中字体编码正确显示的有几个因素: 1)数据库的连接方式; 2)网页中使用的字体编码; 3)数据库里存放数据的字体编码; 4)Java的缺省字体编码。如果在编程中遇到不能正确显示中文时，要先弄清楚以上几项所使用的字体编码，再分析找出原因，即可解决问题。 
</p><p class="a14">众所周知，JSP是Java的一种，和网页有关，而网页也有自己的中文编码系统，所以JSP处理中文要比纯Java的类文件更为麻烦。本文的测试数据库是MySQL3.2，数据库连接驱动是用org.gjt.mm.mysql.Driver，这里主要讨论UTF-8和GBK的显示( GB2312是GBK的一个子集，Java中可以使用GBK来代替GB系列)。我们先来研究JSP中字体编码问题， 下面第一到第六点是针对JSP的(因为从数据库里读出中文数据与写入中文数据有所区别，咱们分别说明，前三点是从读取数据库到显示在网页，后三点是从网页输入数据到存入数据库)，第七到第九点针对纯Java的类文件。 以下rs表示ResultSet的一个实例，是执行Select语句之后产生的数据集。 
</p><p class="a14">一、数据库连接方式使用UTF-8 
</p><p class="a14">在连接数据库的驱动后面加上这句参数useUnicode=true&amp;characterEncoding= 
</p><p class="a14">UTF-8，例如jdbc:mysql://lo<a href="http://topic.ccw.com.cn/corpCenter/249.html" target="_blank"><font color="#0000cc"><b>ca</b></font></a>lhost/DBVF?autoReconnect=true&amp;useUnicode= 
</p><p class="a14">true&amp;characterEncoding=UTF-8，从数据库里读出中文显示在使用GBK的JSP的网页里，如果数据库里存放的字体编码是UTF-8，在JSP中使用 str=new String(rs.getBytes(1),"UTF-8")或者str=rs.getString(1)，可以正确显示中文。如果数据库里存放的是GBK数据，那么JSP中也要使用str=new String(rs.getBytes(1),"GBK")来显示正确的中文。值得注意的是如果页面使用UTF-8，数据库里存放的是UTF-8，也可以用str=new String(rs.getBytes(1),"GBK")正确显示中文。如果网页是UTF-8，而数据库里存放的是GBK，无法直接显示中文，需要2步转换, str=new String(rs.getBytes(1),"GBK"); 再str=new String(str.getBytes("UTF-8"),"GBK")，才可以正确显示中文。 
</p><p class="a14">二、数据库连接方式使用GBK 
</p><p class="a14">在连接数据库的驱动后面加上这句参数useUnicode=true&amp;characterEncoding= 
</p><p class="a14">GBK,例如jdbc:mysql://localhost/DBVF?autoReconnect=true&amp;UseUnicode=true&amp; 
</p><p class="a14">characterEncoding=GBK，从数据库里读出中文，显示在使用GBK的JSP的网页里，如果数据库里存放的字体编码是UTF-8，在JSP中一定要使用 str=new String(rs.getBytes(1),"UTF-8")，才正确显示中文。如果数据库里存放的是GBK数据，那么JSP中也要使用str=new String(rs.getBytes(1),"GBK") 或者直接使用str=rs.getString(1)，即可显示正确的中文。 如果网页是UTF-8，而数据库里存放的是GBK，只能用str=new String(rs.getString(1).getBytes("UTF-8"),"GBK")的方法来显示中文; 如果网页是UTF-8，而数据库里存放的是UTF-8，可用str=new String(rs.getBytes(1),"GBK") 或者rs.getString(1)方法来显示中文。 
</p><p class="a14">三、使用缺省数据库连接方式 
</p><p class="a14">连接数据库的驱动后面没有这句参数useUnicode=&amp;characterEncoding=，例如jdbc:mysql://localhost/DBVF?autoReconnect=true，没有参数useUnicode=true&amp;characterEncoding，表示使用默认的ISO-8895-1编码。 
</p><p class="a14">1. 从数据库里读出中文，显示在GBK的网页里。如果数据库里存放的字体编码是UTF-8，在JSP网页中一定要使用语句 str=new String(rs.getBytes(1),"UTF-8") 或者str= new String(rs.getString(1).getBytes("ISO-8859-1"),"UTF-8")，才可正确显示中文。如果数据库里存放的是GBK数据，那么JSP中也要使用str=new String(rs.getBytes(1),"GBK")或str=new String(rs.getString(1).getBytes("ISO-8859-1"),"GBK") 显示正确的中文。 
</p><p class="a14">2. 如果网页是UTF-8,不能直接正确显示GBK，需要2步转换，str=new String(rs.getBytes(1),"GBK")，再str=new String(str.getBytes("UTF-8"),"GBK") 才可以正确显示中文。如果数据库里存的是UTF-8，直接用str=new String(rs.getBytes(1),"GBK")或者str=new String(rs.getString(1).getBytes("ISO-8859-1"),"GBK")就可以显示中文了。 
</p><p class="a14">以上是读取数据库里中文正确显示在网页上，下面三点是如何正确存入数据库。 
</p><p class="a14">四、数据库连接方式使用UTF-8编码 
</p><p class="a14">JSP中要把网页输入的中文存入数据库，通常有一个提交(Submit)的过程，是用str=request.getParameter("username")，然后执行update或者insert语句来存入数据库。如何赋值给str很重要，而且这里中文输入与网页所使用的字体编码有关。 
</p><p class="a14">1、 网页使用UTF-8，使用str= new String(request.getParameter("username").getBytes("ISO-8859-1"),"UTF-8")或者str= new String(request.getParameter("username").getBytes(),"UTF-8")，都可以使得存到数据库里的数据是UTF-8编码。 
</p><p class="a14">2. 网页使用GBK，使用str= new String(request.getParameter("username").getBytes(),"GBK")，那么存入数据库的是UTF-8编码。 
</p><p class="a14">3. 值得注意的是使用UTF-8的数据库连接方式不能存得GBK。 
</p><p class="a14">五、数据库连接方式使用GBK编码 
</p><p class="a14">1. 输入使用GBK网页，存到数据库里是GBK的方法: str= new String(request.getParameter("username").getBytes("ISO-8859-1"),"GBK") 或者str= new String(request.getParameter("username").getBytes(),"GBK")。 
</p><p class="a14">2. 网页使用GBK，想存入UTF-8到数据库里，要分2步: 先str=new String(request.getParameter("username").getBytes(),"GBK")，再str=new String(str.getBytes("UTF-8"),"GBK")即可。 
</p><p class="a14">3. 网页使用UTF-8，而且使用str= new String(request.getParameter("username").getBytes("ISO-8859-1"),"GBK") 或者str= new String(request.getParameter("username").getBytes(),"UTF-8")，那么存到数据库里的数据是UTF-8编码。 
</p><p class="a14">4. 网页使用UTF-8，而且使用str= new String(request.getParameter("username").getBytes("ISO-8859-1"),"UTF-8")，那么存到数据库里的数据是GBK编码。 
</p><p class="a14">六、数据库连接方式使用缺省，即不使用参数useUnicode和characterEncoding 
</p><p class="a14">1. 网页使用GBK，如果使用str= request.getParameter("username")或者str= new String(request.getParameter("username").getBytes())，那么在数据库里的数据是GBK码。网页使用UTF-8 和使用str= request.getParameter("username")，则存入数据库是UTF-8编码。 
</p><p class="a14">2. 如果使用str= new String(request.getParameter("username").getBytes("ISO-8859-1"))，那么根据网页提供的字体编码而存到数据库里，比如是UTF-8的网页，那么存到数据库中就是UTF-8编码，如果使用GBK网页，那么存到数据库里的字就是GBK编码。 
</p><p class="a14">3. 如果使用str= new String(request.getParameter("username").getBytes("UTF-8"),"UTF-8")这一种组合能存到正确的数据外，其他存到数据库里的数据则都是乱码或者错误码。在这个UTF-8组合的特例中，网页使用的是GBK，则存放到数据库里就是GBK，网页使用UTF-8，那么存到数据库里的就是UTF-8。 
</p><p class="a14">4. 网页是GBK的要存得UTF-8，一定需要2步: company=new String(request.getParameter("company").getBytes(),"GBK")和company=new String(company.getBytes("UTF-8"))。 
</p><p class="a14">5. 网页是UTF-8的，不能存得GBK在数据库里，一句话，改变数据库连接方式不能存得GBK码。 
</p><p class="a14">以上所有的都是基于JSP网页和数据库交换数据，下面讨论一下纯JAVA编程下的字体编码转换。 
</p><p class="a14">七、数据库连接方式使用UTF-8编码 
</p><p class="a14">1. 数据库里的中文是UTF-8，可以转换为GBK,但不能把GBK存入数据库。 
</p><p class="a14">2. 数据库是GBK，如果转换为UTF-8，使用content=new String(rs.getBytes(2),"GBK")直接将content存入数据库就可为UTF-8。 
</p><p class="a14">八、数据库连接方式使用GBK编码 
</p><p class="a14">1. 数据库里的中文是UTF-8，如果转换为GBK，使用content= new String(rs.getString(2).getBytes(),"UTF-8"),再直接使用update或者insert语句插入到数据库，即存得GBK。如果使用content= new String(rs.getString(2).getBytes(),"GBK")或者content= new String(rs.getString(2).getBytes())，再存入数据库即存得还是UTF-8编码。 
</p><p class="a14">2. 数据库里的中文是GBK，如果转换为UTF-8，使用content= new String(rs.getString(2).getBytes("UTF-8"))或者content= new String(rs.getString(2).getBytes("UTF-8"),"GBK")，再直接使用update或者insert语句插入到数据库,即存得UTF-8。 
</p><p class="a14">3. 如果某个String是GBK，要转换为UTF-8，也是使用content= new String(GBKstr.getBytes("UTF-8"))或者content= new String(GBKstr.getBytes("UTF-8"),"GBK"); 如果某个String是UTF-8，要转换为GBK，应该使用new String(UTFstr.getBytes("GBK"),"UTF-8")。 
</p><p class="a14">九、数据库连接方式使用缺省,即不跟参数 
</p><p class="a14">1. str2=new String(GBKstr.getBytes("UTF-8"),"ISO-8859-1")，可以将数据库里的GBK编码转换为UTF-8。 
</p><p class="a14">2. 读取UTF-8然后存入UTF-8，则用str1=new String(UTFstr.getBytes(),"ISO-8859-1")或者str1=new String(UTFstr.getBytes("GBK"),"ISO-8859-1")。 
</p><p class="a14">3. 不能实现数据库里的UTF-8转换为GBK。 
</p><p class="a14"><font color="#5e0000">如果采用UTF-8的数据库连接方式或者缺省数据连接方式，那么无法将UTF-8转为GBK；而GBK的数据库连接方式可以实现UTF-8和GBK的相互转换。建议大家采用GBK的数据连接方式。</font></p><img src ="http://www.blogjava.net/joeyeezhang/aggbug/89362.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/joeyeezhang/" target="_blank">joeyeezhang</a> 2006-12-21 21:44 <a href="http://www.blogjava.net/joeyeezhang/archive/2006/12/21/89362.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>事务服务浅析 </title><link>http://www.blogjava.net/joeyeezhang/archive/2006/10/23/76691.html</link><dc:creator>joeyeezhang</dc:creator><author>joeyeezhang</author><pubDate>Mon, 23 Oct 2006 01:42:00 GMT</pubDate><guid>http://www.blogjava.net/joeyeezhang/archive/2006/10/23/76691.html</guid><wfw:comment>http://www.blogjava.net/joeyeezhang/comments/76691.html</wfw:comment><comments>http://www.blogjava.net/joeyeezhang/archive/2006/10/23/76691.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/joeyeezhang/comments/commentRss/76691.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/joeyeezhang/services/trackbacks/76691.html</trackback:ping><description><![CDATA[　事务是一个非常重要的编程概念，使用事务，可以很简单地构造出可靠稳定的应用程序，本文以许多具体的例子介绍了事务服务的概念和事务服务的具体实现。<br />　　本文共分两部分：第一部分从事务服务整体描述的角度简要介绍了事务服务产生的动机、事务服务的应用和事务服务的功能，其中以具体的例子解释了相关概念和事务服务涉及到的一些术语；第二部分以J2EE中的事务服务为例对事务的实现作简要的介绍。 
<table id="table1" style="BORDER-RIGHT: 1px dotted; BORDER-TOP: 1px dotted; BORDER-LEFT: 1px dotted; BORDER-BOTTOM: 1px dotted; BACKGROUND-COLOR: #e0e0e0" cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr valign="top"><td width="100%"><a name="0"><span class="atitle2"><font size="2">第I部分 事务服务简述</font></span></a><br />　 
<p><a name="1"><span class="atitle3">1. 事务综述</span></a><br />　</p><p>事务是一个非常重要的编程概念，使用事务，可以很简单地构造出可靠稳定的应用程序，尤其对那些需要进行并发数据访问的应用程序，事务更是重要的多。事务的概念最初应用在那些用于商务操作的应用程序上，在这些应用中，事务用来保护中央数据库中的数据。随后，随着分布式计算的发展，事务在分布式计算领域中也得到了广泛的应用。现在，在分布式计算领域中，公认为事务是构造可靠性分布式应用程序的关键。</p><p><b xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">1.1事务产生的动机</b></p><p>1.1.1原子操作</p><p>考虑这样一个应用：用户把钱从一个银行账号转账至另一个银行账号，需要将资金从一个银行账号中取出，然后再存入另一个银行账号中。理想来说，这两次操作都应该成功。但是，如果有错误发生，则两次操作都应该失败，否则的话，操作之后其中一个账号中的金额将会是错误的，整个操作过程应该是原子性的，两个操作都是一个原子事务操作的一部分。</p><p>可以使用异常处理来处理上述问题，代码如下：</p><table id="table2" cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code>try{  //从账户1中取款}catch(Exception e){	//如果发生错误，则终止操作	return;}try {	//如果第一步没有发生错误，则将提取出的资金存入账户2	}catch(Exception e) {//如果发生错误，则终止这步操作，并且将从账户1中取出的资金再重新存回到账户1中return ;}</code></pre></td></tr></tbody></table><p>上面这种解决方法从存在着下面的问题：</p><ul xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><li>程序拖沓冗长 
</li><li>必须考虑到每一步中可能发生的每一个问题，并且要编写错误处理程序来考虑如何撤销所作的操作 
</li><li>如果执行的是比简单的取款、存款操作要复杂的多的程序，那么错误处理程序将会变得难以控制。 
</li><li>编写测试程序将会非常困难 </li></ul><p>因此，需要一种事务处理机制来保证这种原子性的操作。</p><p>1.1.2 网络故障或机器故障</p><p>为了在发生严重故障之后，能够保证系统的状态是一致的，所以需要一种恢复性的机制来保证这一点。</p><p>1.1.3 数据共享</p><p>需要一种机制来保证多用户并发访问数据的问题。</p><p>以上这些问题的解决方法，便是使用事务服务。</p><p><b xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">1.2 使用事务服务带来的好处</b></p><p>使用事务，便可以利用事务的四个重要属性：ACID。</p><ul xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><li>原子性( <b>atomic</b>)：事务中包含的各项操作必须全部成功执行或者全部不执行。任何一项操作失败，将导致整个事务失败，其他已经执行的任务所作的数据操作都将被撤销，只有所有的操作全部成功，整个事务才算是成功完成。 
</li><li>一致性( <b>consistent</b>)：保证了当事务结束后，系统状态是一致的。那么什么是一致的系统状态？例如，如果银行始终遵循着"银行账号必须保持正态平衡"的原则，那么银行系统的状态就是一致的。上面的转账例子中，在取钱的过程中，账户会出现负态平衡，在事务结束之后，系统又回到一致的状态。这样，系统的状态对于客户来说，始终是一致的。 
</li><li>隔离性( <b>isolated</b>)：使得并发执行的事务，彼此无法看到对方的中间状态。保证了并发执行的事务顺序执行，而不会导致系统状态不一致。 
</li><li>持久性( <b>durable</b>)：保证了事务完成后所作的改动都会被持久化，即使是发生灾难性的失败。可恢复性资源保存了一份事务日志，如果资源发生故障，可以通过日志来将数据重建起来。 </li></ul><p><a name="2"><span class="atitle3">2. 事务应用</span></a><br />　</p><p>事务服务支持的应用由下列实体组成：</p><ul xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><li>事务客户(TC,Transactional Client) 
</li><li>事务对象(TO,Transactional Objects) 
</li><li>可恢复对象(Recoverable Objects) 
</li><li>提供事务支持的服务器(Transactional Servers) 
</li><li>可恢复资源服务器(Recoverable Servers) </li></ul><p>下图展示了一个简单的事务应用，包含了基本的事务元素：</p><p><br /><img height="463" alt="" src="http://www-128.ibm.com/developerworks/cn/java/l-transation/part1/1.gif" width="553" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" /></p><ul xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><li>事务客户：事务客户是一个独立的程序，调用参与事务的多个事务对象。发起事务的程序被称为事务发起者(Transaction Originator)。 
</li><li>事务对象：指那些在事务范围内行为会被影响的对象。事务对象自身包含了对持久化数据的操作或者通过事务请求间接对持久化数据进行操作。事务服务并不要求所有的事务请求都具有事务性的行为。与事务对象相对的是非事务对象。 
</li><li>可恢复对象(Recoverable Objects)和资源对象(Resource Objects)：如果事务提交和事务的回滚将影响某个对象里面的数据，那么这个对象称为一个可恢复对象。一个对象可以是事务对象而不是可恢复对象，因为事务对象可以使用其他的可恢复对象。可恢复对象必须将自己注册为事务服务中的一个资源对象，才可以参与到事务中。 
</li><li>支持事务的服务器：例如应用服务器 
</li><li>可恢复服务器：例如数据库 </li></ul><p><a name="3"><span class="atitle3">3. 事务服务提供的功能</span></a><br />　</p><p>事务服务提供下列操作：</p><ul xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><li>控制事务的范围和持续时间 
</li><li>让多个对象参与到一个单独的原子性事务中 
</li><li>将对象内部状态的改变同事务相联系 
</li><li>协调事务完成 </li></ul><p><b xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">3.1事务模型</b></p><ul xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><li>平面型事务模型整个事务是一个整体，不可划分为子事务。 
</li><li>嵌套式事务模型嵌套有子事务，子事务中还可以嵌套有子事务，整个是一个树形结构。旅行计划问题： 
<ul><li>购买从美国波士顿到美国纽约的火车票 
</li><li>购买从美国纽约到英国伦敦的飞机票 
</li><li>购买从英国伦敦到法国巴黎的气艇票 
</li><li>发现没有飞往法国巴黎的气艇票 </li></ul>这对于平面型事务来说，只能有一个选择：事务失败。这样由于没有飞往巴黎的气艇票，将会取消所有的出行计划。但是在这里，完全可以采用其他的旅行方式(购买火车票或者飞机票)来代替气艇。因此需要一个更加健壮的事务模型来解决这个问题。可以将整个事务划分为若干个子事务，整个事务可以重新执行单个子事务来尝试完成事务，如果最终某个单个子事务无法完成，则整个事务失败。 
</li><li>其他事务模型 <br />锁链式模型、传奇式模型等。 </li></ul><p><b xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">3.2 事务的终止</b></p><p>通过发出提交请求或回滚请求来终止事务。通常，事务是由发起事务的客户(事务发起者)来终止的。</p><p><b xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">3.3事务的完整性</b></p><p>某些事务服务的实现为了保证事务的完整性，在事务服务接口的使用上施加了一些限制。这称为checked事务行为。例如，在事务的所有任务完成之前提交事务会导致数据不完整。事务服务的Checked实现会阻止事务的过早提交。其他事务服务的实现则完全依赖应用程序来提供事务的完整性保证，这称为unchecked事务行为。</p><p><b xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">3.4事务的上下文</b></p><p>事务上下文惟一标识了一个事务,保存了事务的当前状态。通过查询事务上下文，可以得知自身是否处于事务中，所在事务的状态以及其他一些有用的信息。</p><p>事务上下文可以作为事务操作调用的一部分隐式地传递给事务对象。事务服务也允许程序员将事务上下文作为事务请求的显示参数来传递。</p><p><a name="4"><span class="atitle3">4. 分布式事务</span></a><br />　</p><p>分布式事务是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。为了实现分布式事务，需要使用下面将介绍的两阶段提交协议。</p><ul xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><li>阶段一：开始向事务涉及到的全部资源发送提交前信息。此时，事务涉及到的资源还有最后一次机会来异常结束事务。如果任意一个资源决定异常结束事务，则整个事务取消，不会进行资源的更新。否则，事务将正常执行，除非发生灾难性的失败。为了防止会发生灾难性的失败，所有资源的更新都会写入到日志中。这些日志是永久性的，因此，这些日志会幸免遇难并且在失败之后可以重新对所有资源进行更新。 
</li><li>阶段二：只在阶段一没有异常结束的时候才会发生。此时，所有能被定位和单独控制的资源管理器都将开始执行真正的数据更新。 </li></ul><p>在分布式事务两阶段提交协议中，有一个主事务管理器负责充当分布式事务协调器的角色。事务协调器负责整个事务并使之与网络中的其他事务管理器协同工作。</p><p><br /><img height="415" alt="" src="http://www-128.ibm.com/developerworks/cn/java/l-transation/part1/2.gif" width="552" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" /></p><p>为了实现分布式事务，必须使用一种协议在分布式事务的各个参与者之间传递事务上下文信息，IIOP便是这种协议。这就要求不同开发商开发的事务参与者必须支持一种标准协议，才能实现分布式的事务。</p><p>以上从事务整体描述的角度简要介绍了事务服务产生的动机、事务服务的应用和事务服务的功能，下面以J2EE中的事务服务为例对事务的实现作简要的介绍。</p></td></tr></tbody></table><p>　</p><table id="table2" style="BORDER-RIGHT: 1px dotted; BORDER-TOP: 1px dotted; BORDER-LEFT: 1px dotted; BORDER-BOTTOM: 1px dotted; BACKGROUND-COLOR: #e0e0e0" cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr valign="top"><td width="100%"><a name="0"><span class="atitle2"><font size="2">第II部分 J2EE中的事务服务</font></span></a><br />　 
<p><a name="1"><span class="atitle3">简介</span></a><br />　</p><p>Java <sup xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">TM</sup>2 Platform, Enterprise Edition(J2EE)简化了分布式事务管理应用程序的编写。J2EE包括了两套规范，用来支持分布式的事务，一种是Java Transaction API(JTA)，另一种是Java Transaction Service(JTS)。JTA是一种高层的，与实现无关的，与协议无关的API，应用程序和应用服务器可以使用JTA来访问事务。JTS则规定了支持JTA的事务管理器的实现规范，在高层API之下实现了OMG Object Transaction Service(OTS) 1.1规范的Java映射。JTS使用Internet Inter-ORB Protocol(IIOP)来传播事务。作为J2EE平台实现的一部分，SUN实现了一个支持JTS的事务管理器，同时还实现了JTA。 </p><p>JTA和JTS让J2EE应用服务器完成事务管理，这样就使得组件开发人员摆脱了事务管理的负担。开发者只需在部署描述符中声明事务管理属性，便可以使得EJB组件参与到事务之中，由应用服务器来负责事务的管理。</p><ul xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><li>JTS规范定义了事务管理器的实现。JTS规范中定义的事务管理器在高层支持JTA接口规范，在底层则实现了OTS1.1(CORBA Object Transaction Service)的标准Java映射。OMG使用IDL(接口定义语言)定义了事务服务语言中性的实现，JTS则对这个IDL的事务服务实现作了标准的Java映射。JTS使用OTS接口实现了互操作和移植性。OTS接口定义了一组标准的机制，使得JTS事务管理器之间可以使用IIOP(Internet InterORB协议)来生成并传播事务上下文。 
</li><li>从事务管理器的角度出发，事务服务的具体实现是不需要暴露出来的，只需要定义高层接口，使得事务服务的用户可以驱动事务界限、资源获取、事务同步和事务恢复过程。JTA的目的是定义事务管理器所要求的本地Java接口，从而在企业级分布计算环境中支持事务管理。下图中的小半圆代表JTA规范。 </li></ul><p><br /><img height="452" alt="" src="http://www-128.ibm.com/developerworks/cn/java/l-transation/part2/3.gif" width="512" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" /></p><p><a name="2"><span class="atitle3">J2EE事务服务的层次关系</span></a><br />　</p><p>企业级Java中间件的分布式事务服务包括五层：事务管理器(Transaction Manager)、应用服务器(Application Server)、资源管理器(Resource Manager)、应用程序(Application Program)和通信资源管理器(Communication Resource Manager)。每一层都通过实现一组事务API和相关机制参与到分布式事务处理系统中。</p><ul xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><li>事务管理器：是一个系统级的组件，是事务服务的访问点。提供了一组服务和相关的管理机制，用于支持事务划分、事务资源管理、事务同步和事务上下文的传播。 
</li><li>应用服务器(或者称为TP monitor)提供了支持应用程序运行环境的基础设施，这个运行环境包括了事务状态管理。应用服务器的一个例子是EJB服务器。 
</li><li>资源管理器(通过资源适配器[Resource Adapter]，资源适配器类似于数据库连接)为应用程序提供了对资源的访问。资源管理器通过实现一组事务资源接口来参与到分布式事务中。事务管理器使用这组事务资源接口在处理事务联系、事务完成和事务恢复的相关工作。资源管理器的一个例子是关系数据库服务器。 
</li><li>基于组件的事务性应用运行在应用服务器环境中，需要依赖应用服务器通过事务属性声明设置所提供的事务管理支持。这种类型应用的典型例子是EJB。除此之外，一些独立的Java客户端程序需要使用应用服务器或事务管理器所提供的高层接口来控制事务界限。 
</li><li>通讯资源管理器(CRM)支持事务上下文的传播和事务服务的访问请求。JTA文档中没有规定通信的要求。请参考JTS规范[2]获得有关事务管理器之间互操作的详细信息。 </li></ul><p>从事务管理器的角度出发，事务服务的具体实现是不需要暴露出来的，只需要定义高层接口，使得事务服务的用户可以驱动事务界限、资源获取、事务同步和事务恢复过程。JTA的目的是定义事务管理器所要求的本地Java接口，从而在企业级分布计算环境中支持事务管理。下图中的小半圆代表JTA规范。</p><p><br /><img height="452" alt="" src="http://www-128.ibm.com/developerworks/cn/java/l-transation/part2/3.gif" width="512" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" /></p><p><a name="3"><span class="atitle3">JTS中规定的事务管理器的实现</span></a><br />　</p><p>本节从事务管理器实现者的角度描述了实现方面的要求。如下图，事务管理器必须实现JTA接口，用于支持应用服务器和资源管理器。不要求实现对JDBC 1.0 Driver和非JTA资源管理器支持。也不要求实现对各种CORBA应用实体的支持，如事务客户端(Transactional Client)、事务服务器(Transactional Server)和恢复服务器(Recoverable Server)。</p><p><br /><img height="434" alt="" src="http://www-128.ibm.com/developerworks/cn/java/l-transation/part2/4.gif" width="576" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" /></p><p><a name="4"><span class="atitle3">Java Transaction API</span></a><br />　</p><p>Java Transaction API由三部分组成：高层的应用事务划分接口(供事务客户使用)、高层的事务管理器接口(供应用服务器使用)和X/Open XA协议的标准Java映射(供事务性资源管理器使用)。</p><p><b xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">4.1 UserTransaction接口</b></p><p>Javax.transaction.UserTransaction接口使得应用程序能够编程控制事务边界。这个接口可以由Java客户端程序或者EJB来使用。</p><p>4.1.1在EJB Server中的UserTransaction支持</p><p>EJB中对事务的管理有两种类型：</p><ol xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><li>Bean自管理事务对于自管理事务的EJB，需要从EJB上下文中获取UserTransaction的接口引用，由自己负责完成事务的开始、提交或者回滚。 
<table id="table2" cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code>try {javax.transaction.UserTransaction userTran = ctx.getUserTransaction();userTran.begin();… //执行事务性的程序逻辑userTran.commit();}catch(Exception e) {	userTran.rollBack();	throw new Exception("……");}</code></pre></td></tr></tbody></table>EJB这样处理事务称为编程型事务。 
</li><li>由容器负责事务管理对于这样的EJB，只需在其部署描述符中指定所需的事务相关属性，便可由EJB容器代替EJB进行事务管理。这称为声明式事务。 </li></ol><p>4.1.2 事务客户端中的UserTransaction支持</p><p>Java客户端程序需要首先通过JNDI来获得UserTransaction对象的引用，然后使用该对象的方法完成事务的开始、提交或者回滚。</p><table id="table3" cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code>java.util.Properties env = …Context ctx = new InitialContext(env);Javax.transaction.UserTransaction userTran = (javax.transaction.UserTransaction)ctx.lookup("javax.transaction.UserTransaction");userTran.commit()try {	userTran.commit();}catch(Exception e) {	userTran.rollBack();	throw new Exception("……");}</code></pre></td></tr></tbody></table><p><b xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">4.2 TransactionManager接口</b></p><p>应用服务器使用javax.transaction.TransactionManager接口来代表受控的应用程序控制事务的边界。例如，EJB容器为事务性EJB组件管理事务状态。</p><p><b xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">4.3 Transaction接口</b></p><p>使用Transaction接口可以执行与目标对象相关联的事务操作。</p><p><b xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">4.4 XAResource接口</b></p><p>Javax.transaction.xa.XAResource接口是基于X/Open CAE规范(分布式事务处理：XA规范)工业标准XA接口的Java映射。 XAResource接口定义了分布式事务处理环境(DTP)中资源管理器和事务管理器之间是如何交互的。资源管理器的资源适配器实现了XAResource接口，将事务同事务资源联系起来，类似关系数据库的一个连接。</p><p><br /><img height="272" alt="" src="http://www-128.ibm.com/developerworks/cn/java/l-transation/part2/5.gif" width="575" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" /></p><p><b xmlns:dw="http://www.ibm.com/developerworks/" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">4.5 Xid接口</b></p><p>Javax.transaction.xa.Xid接口是X/Open事务标识符XID结构的Java映射。这个接口由事务管理器和资源管理器来使用，对于应用程序和应用服务器而言这个接口是不可见的。</p></td></tr></tbody></table><img src ="http://www.blogjava.net/joeyeezhang/aggbug/76691.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/joeyeezhang/" target="_blank">joeyeezhang</a> 2006-10-23 09:42 <a href="http://www.blogjava.net/joeyeezhang/archive/2006/10/23/76691.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>事务</title><link>http://www.blogjava.net/joeyeezhang/archive/2006/10/20/76412.html</link><dc:creator>joeyeezhang</dc:creator><author>joeyeezhang</author><pubDate>Fri, 20 Oct 2006 08:39:00 GMT</pubDate><guid>http://www.blogjava.net/joeyeezhang/archive/2006/10/20/76412.html</guid><wfw:comment>http://www.blogjava.net/joeyeezhang/comments/76412.html</wfw:comment><comments>http://www.blogjava.net/joeyeezhang/archive/2006/10/20/76412.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/joeyeezhang/comments/commentRss/76412.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/joeyeezhang/services/trackbacks/76412.html</trackback:ping><description><![CDATA[
		<h1 align="center">
				<strong>
						<font size="4">第14章 事务</font>
				</strong>
		</h1>
		<p>Dale Green著</p>
		<p>JSP WU 译</p>
		<p>一个典型的企业应用程序在一个或多个数据库中访问和存储信息。因为这些信息对于商业操作非常重要，它必须精确、实时、可靠。如果允许多个程序同时更新相同的数据，就会破坏数据的完整性。如果在一个商业交易处理过程中，部分数据被更新后系统崩溃也将破坏数据完整性。事务通过预防以上情况的发生确保数据的完整性。事务控制多个应用程序对数据库的并发操作。如果发生系统崩溃，事务确保恢复的数据崩溃前将保持一致。</p>
		<p>
				<strong>本章内容：</strong>
		</p>
		<p>什么是事务</p>
		<p>容器管理事务</p>
		<p>事务的属性</p>
		<p>回滚容器管理事务</p>
		<p>同步会话bean实例变量</p>
		<p>容器管理事务中不允许使用的方法</p>
		<p>Bean 管理事务</p>
		<p>JDBC事务</p>
		<p>JTA  事务</p>
		<p>非提交返回事务</p>
		<p>在Bean管理事务中不允许使用的方法</p>
		<p>企业Bean事务摘要</p>
		<p>事务超时</p>
		<p>隔离级别</p>
		<p>更新多个数据库</p>
		<p>Web 组件事务</p>
		<p> </p>
		<h2>
				<a name="#14-1">
				</a>
				<strong>
						<font size="3">一．什么是事务</font>
				</strong>
		</h2>
		<p>模拟一个商业交易，应用程序需要完成几个步骤。例如，一个财物应用程序，可能会将资金从经常性帐户（checking account）转到储蓄性账户（saving account），该交易的伪码表示如下：</p>
		<p>begin transaction </p>
		<p>debit checking account </p>
		<p>credit savings account</p>
		<p>update history log </p>
		<p>commit transaction</p>
		<p>三个步骤要么全部完成，要么一个都不做。否则数据完整性将被破坏。因为事务中的所有步骤被看作一个统一的整体，所以事务一般被定义为一个不可分割的工作单元。 </p>
		<p>结束事务有两种方法：提交或者回滚。当一个事务提交，数据修改被保存。如果事务中有一个步骤失败，事务就回滚，这个事务中的已经执行的动作被撤销。例如在上面的伪码中，如果在处理第二步的时候硬盘驱动器崩溃，事务的第一步将被撤销。尽管事务失败，数据的完整性不会被破坏，因为帐目仍然保持平衡。</p>
		<p>前面伪码中，begin和commit标明了事务的界限。当设计一个企业Bean的时候，你要决定怎样通过容器管理或bean管理事务来指定事务界限。</p>
		<h2>
				<a name="#14-2">
				</a>
				<strong>
						<font size="3">二．容器管理事务</font>
				</strong>
		</h2>
		<p>在容器管理事务的企业Bean中，EJB容器来设定事务界线。你能够在任何企业Bean中使用容器管理事务：会话Bean、实体Bean或者 Message-driven Bean。容器管理事务简化了开发，因为企业Bean不用编码来显式制定事务界限。代码不包括开始结束事务的语句。典型的，容器会在一个企业Bean的方法被调用前立即开始一个事务，在这个方法退出以前提交这个事务。每个方法都关联一个事务。在一个方法中不允许嵌套或多个的事务存在。容器管理事务不需要所有的方法都关联事务。当部署一个Bean时，通过设定部署描述符中的事务属性来决定方法是否关联事务和如何关联事务。</p>
		<p>
				<b>事务的属性</b>
		</p>
		<p>一个事务的属性控制了事务的使用范围。图 14-1说明了为什么控制事务的范围很重要。图中，method-A开始一个事务然后调用Bean-2中的method-B.它运行在method-A开始的事务中还是重新执行一个新的事务？结果要看method-B中的事务属性。</p>
		<p>
				<img height="215" src="http://www.leftworld.net/online/j2ee/14/image002.gif" width="540" border="0" />
		</p>
		<p>
				<strong>图 14-1 Transaction Scope</strong>
		</p>
		<p>一个事务属性可能有下面的属性之一：</p>
		<p>☆ Required </p>
		<p>☆ RequiresNew </p>
		<p>☆ Mandatory </p>
		<p>☆ NotSupported </p>
		<p>☆ Supports </p>
		<p>☆ Never </p>
		<h4>Required </h4>
		<p>如果客户端正在一个运行的事务中调用一个企业Bean的方法，这个方法就在这个客户端的事务中执行。如果客户端不关联一个事务，这个容器在运行该方法前开始一个新的事务。</p>
		<p>Required属性在许多事务环境中可以很好的工作，因此你可以把它作为一个默认值，至少可以在早期开发中使用。因为事务的属性是在部署描述符中声明的，在以后的任何时候修改它们都很容易。</p>
		<h4>RequiresNew </h4>
		<p>如果客户端在一个运行的事务中调用企业Bean的方法，容器的步骤是：</p>
		<p>1．挂起客户端的事务</p>
		<p>2．开始一个新的事务</p>
		<p>3．代理方法的调用</p>
		<p>4．方法完成后重新开始客户端的事务</p>
		<p>如果客户端不关联一个事务，容器运行这个方法以前同样开始一个新的事务。如果你想保证该方法在任何时候都在一个新事物中运行，使用RequiresNew属性。</p>
		<h4>Mandatory</h4>
		<p>如果客户端在一个运行的事务中调用企业Bean的方法，这个方法就在客户端的事务中执行。如果客户端不关联事务，容器就抛出TransactionRequiredException 异常。</p>
		<p>如果企业Bean的方法必须使用客户端的事务，那么就使用Mandatory属性。</p>
		<h4>NotSupported </h4>
		<p>如果客户端在一个运行的事务中调用企业Bean的方法，这个容器在调用该方法以前挂起客户端事务。方法执行完后，容器重新开始客户端的事务。</p>
		<p>如果客户端不关联事务，容器在方法运行以前不会开始一个新的事务。为不需要事务的方法使用NotSupported属性。因为事务包括整个过程，这个属性可以提高性能。</p>
		<p>
				<strong>Supports</strong>
		</p>
		<p>如果客户端在一个运行的事务中调用企业Bean的方法，这个方法在客户端的事务中执行，如果这个客户端不关联一个事务，容器运行该方法前也不会开始一个新的事务。因为该属性使方法的事务行为不确定，你应该谨慎使用Supports属性。</p>
		<p>
				<strong>Never </strong>
		</p>
		<p>如果客户端在一个运行的事务中调用企业Bean的方法，容器将抛出RemoteException异常。如果这个客户端不关联一个事务，容器运行该方法以前不会开始一个新的事务。</p>
		<p>
				<strong>Summary of Transaction Attributes（事务属性概要）</strong>
		</p>
		<p>表 14-1 列出了事务属性的影响。事务T1和T2都被容器控制。T1是调用企业Bean方法的客户端的事务环境。在大多数情况下，客户端是其它的企业Bean。T2是在方法执行以前容器启动的事务。在表 14-1中，“None”的意思是这个商业方法不在容器控制事务中执行。然而，该商业方法中的数据库操作可能在DBMS管理控制的事务中执行。</p>
		<p>
				<strong>Setting Transaction Attributes （设定事务属性）</strong>
		</p>
		<p>因为事务属性被保存在配置描述符中，他们会在J2EE应用程序开发的几个阶段被改变：创建企业Bean，应用程序装配和部署。然而， 当创建这个Bean的时候指定它的属性是企业Bean开发者的责任。只有将该组件装配到一个更大的应用程序时可以由开发者修改该属性，而不要期待J2EE应用程序部署者来指定该事务属性。</p>
		<table cellspacing="0" cellpadding="0" align="center" border="1">
				<tbody>
						<tr>
								<td colspan="3">
										<p>
												<b>表 14-1 </b>
												<b>事物属性和范围 </b>
										</p>
								</td>
						</tr>
						<tr>
								<td>
										<p>
												<b>事务属性</b>
										</p>
								</td>
								<td>
										<p>
												<b>客户端事务</b>
										</p>
								</td>
								<td>
										<p>
												<b>商业方法事务</b>
										</p>
								</td>
						</tr>
						<tr>
								<td rowspan="2">
										<p>Required</p>
								</td>
								<td>
										<p>None</p>
								</td>
								<td>
										<p>T2</p>
								</td>
						</tr>
						<tr>
								<td>
										<p>T1</p>
								</td>
								<td>
										<p>T1</p>
								</td>
						</tr>
						<tr>
								<td rowspan="2">
										<p>RequiresNew</p>
								</td>
								<td>
										<p>None</p>
								</td>
								<td>
										<p>T2</p>
								</td>
						</tr>
						<tr>
								<td>
										<p>T1</p>
								</td>
								<td>
										<p>T2</p>
								</td>
						</tr>
						<tr>
								<td rowspan="2">
										<p>Mandatory</p>
								</td>
								<td>
										<p>None</p>
								</td>
								<td>
										<p>error</p>
								</td>
						</tr>
						<tr>
								<td>
										<p>T1</p>
								</td>
								<td>
										<p>T1</p>
								</td>
						</tr>
						<tr>
								<td rowspan="2">
										<p>NotSupported</p>
								</td>
								<td>
										<p>None</p>
								</td>
								<td>
										<p>None</p>
								</td>
						</tr>
						<tr>
								<td>
										<p>T1</p>
								</td>
								<td>
										<p>None</p>
								</td>
						</tr>
						<tr>
								<td rowspan="2">
										<p>Supports</p>
								</td>
								<td>
										<p>None</p>
								</td>
								<td>
										<p>None</p>
								</td>
						</tr>
						<tr>
								<td>
										<p>T1</p>
								</td>
								<td>
										<p>T1</p>
								</td>
						</tr>
						<tr>
								<td rowspan="2">
										<p>Never</p>
								</td>
								<td>
										<p>None</p>
								</td>
								<td>
										<p>None</p>
								</td>
						</tr>
						<tr>
								<td>
										<p>T1</p>
								</td>
								<td>
										<p>Error</p>
								</td>
						</tr>
				</tbody>
		</table>
		<p>你可以为整个企业Bean或者单个方法指定事务属性。如果你为整个企业Bean和它某个方法各指定一个事务属性，为该方法指定的事务属性优先。当为单个方法指定事务属性时，不同类型企业Bean的要求也不同。会话Bean需要为商业方法定义属性属性，但create方法不需要定义事务属性。实体Bean需要为商业方法、create方法、remove方法和查找（finder）方法定义事务属性。Message-driven Bean需要为onMessage方法指定属性事务，而且只能是Required和NotSupported其中之一。</p>
		<p>
				<b>容器管理事务的回滚</b>
		</p>
		<p>　　在以下两中情况下，事务将回滚。第一，如果产生一个系统异常，容器将自动回滚该事务。第二，通过调用EJBContext接口SetRollbackOnly方法，Bean方法通知容器回滚该事务。如果Bean抛出一个应用异常，事务将不会自动回滚，但可以调用SetRollbackOnly回滚。对于一个系统和应用的异常，参考第5章的处理异常一节。</p>
		<p>下面这个例子的代码在j2eetorial/examples/src/bank目录下。在命令行窗口下进入j2eetutorial/examples目录执行ant bank命令编译这些代码，执行 ant create-bank-table命令创建要用到的表。一个BankApp.ear样本文件在j2eetutorial/examples/ears目录下。通过BnankEJB 实例的transferToSaving方法来说明setRollbackOnly方法的用法。如果余额检查出现负数，那么transferToSaving调用setRollBackOnly回滚事务并抛出一个应用程序异常（InsufficientBalanceException）。updateChecking和updateSaving 方法更新数据表。如果更新失败，这两个方法抛出SQLException异常而transgerToSaving方法抛出EJBException异常。因为EJBException是一个系统异常，它使容器事务自动回滚事务。TransferTuSaving 方法的代码如下：</p>
		<p>public void transferToSaving(double amount) throws</p>
		<p>   InsufficientBalanceException  {</p>
		<p>  checkingBalance -= amount;</p>
		<p>   savingBalance += amount;</p>
		<p>  try {</p>
		<p>      updateChecking(checkingBalance);</p>
		<p>      if (checkingBalance &lt; 0.00) {</p>
		<p>         context.setRollbackOnly();</p>
		<p>         throw new InsufficientBalanceException();</p>
		<p>      }</p>
		<p>      updateSaving(savingBalance);</p>
		<p>   } catch (SQLException ex) {</p>
		<p>       throw new EJBException </p>
		<p>          ("Transaction failed due to SQLException: " </p>
		<p>          + ex.getMessage());</p>
		<p>   }</p>
		<p>}</p>
		<p>当一个容器回滚一个事务，它总是会撤消事务中已执行的SQL语句造成的数据改动。然而，仅仅在实体Bean中容器回滚才会改变Bean的实例变量（与数据库状态有关的字段）。（这是因为容器会自动调用实体Bean的ejbLoad方法，该方法从数据库中读入实例变量的值。）当发生回滚，会话Bean必须显式重新设置所有被事务改动过的实例变量。重设会话Bean的实例变量最简单的方法是实现SessionSynchronization接口。</p>
		<p>
				<b>同步会话Bean的实例变量</b>
		</p>
		<p>SessionSynchronization接口是可选的，它允许你在Bean的实例变量和它们在数据库中的相应值之间保持同步。容器会在事务的几个主要阶段调用SessionSynchronization接口的对应方法—afterBegin、beforeCompletion和afterCompletion。</p>
		<p>AfterBegin方法通知Bean实例一个新的事务已经开始。容器在调用商业方法以前立即调用afterBegin方法。afterBegin方法是从数据库中读入实例变量值的最佳位置。例如，在BanBean类中，在afterBegin方法中从读入了CheckingBalance和savingBalance变量的值：</p>
		<p>  public void afterBegin() {</p>
		<p>  System.out.println("afterBegin()");</p>
		<p>   try {</p>
		<p>      checkingBalance = selectChecking();</p>
		<p>      savingBalance = selectSaving();</p>
		<p>   } catch (SQLException ex) {</p>
		<p>       throw new EJBException("afterBegin Exception: " +</p>
		<p>           ex.getMessage());</p>
		<p>   }</p>
		<p>}</p>
		<p>商业方法方法完成以后，容器调用beforeCompletion方法，不过仅仅是在事务提交以前。BeforeCompletion方法是会话Bean回滚事务的最后时机（通过调用setRollbackOnly方法）.如果会话Bean还没有实例变量的值更新数据库，就在beforCompletion方法里实现。</p>
		<p>afterCompletion方法指出事务已经完成。它只有一个布尔型的参数，true表示事务被正确提交false表示事务回滚。如果事务回滚，会话Bean可以在该方法中从数据库中重新读取它的实例变量值：</p>
		<p>public void afterCompletion(boolean committed) {</p>
		<p>  System.out.println("afterCompletion: " + committed);</p>
		<p>   if (committed == false) {</p>
		<p>      try {</p>
		<p>         checkingBalance = selectChecking();</p>
		<p>         savingBalance = selectSaving();</p>
		<p>      } catch (SQLException ex) {</p>
		<p>          throw new EJBException("afterCompletion SQLException:</p>
		<p>         " + ex.getMessage());</p>
		<p>      }</p>
		<p>   }</p>
		<p>}</p>
		<p>
				<b>容器管理事务中不允许使用的方法</b>
		</p>
		<p>你不应该调用可能干扰容器设置的事务界线的方法，下面列出了所有禁止的方法：</p>
		<p>☆ java.sql.Connection接口的commit、setAutoCommit和rollback方法</p>
		<p>☆ javax.ejb.EJBContext 接口的getUserTransaction方法</p>
		<p>☆ javax.transaction.UserTransaction接口的所有方法 </p>
		<p>然而你可以在Bean管理事务中使用这些方法设置事务界限。</p>
		<h2>
				<a name="#14-3">
				</a>
				<strong>
						<font size="3">三．Bean管理事务</font>
				</strong>
		</h2>
		<p>在一个Bean管理事务中，会话Bean或者Message-driven Bean是用代码显式设置事务界线的。实体Bean不能使用Bean管理事务，只能使用容器管理的事务。虽然容器管理事务Bean需要较少的代码，但它也有一个局限：方法执行时，它只能关联一个事务或不关联任何事务。如果这个局限使你Bean编码困难，你应该考虑使用Bean管理事务。（译者：实际上J2EE服务器不支持嵌套事物，那么Bean管理事务唯一的优点就是可以在一个方法中一次启动多个事务）</p>
		<p>下面的伪码很好说明了Bean管理事对商业逻辑的紧密控制。通过检查各种条件，伪码决定是否在商业方法中启动或停止不同的事务。</p>
		<p>begin transaction</p>
		<p>...</p>
		<p>update table-a</p>
		<p>...</p>
		<p>if (condition-x)</p>
		<p>   commit transaction</p>
		<p>else if (condition-y)</p>
		<p>   update table-b</p>
		<p>   commit transaction</p>
		<p>else</p>
		<p>   rollback transaction</p>
		<p>   begin transaction</p>
		<p>   update table-c</p>
		<p>   commit transaction</p>
		<p>当为会话Bean或Message-driver Bean的Bean管理事务编码时，你必须决定是使用jdbc或者JTA事务。下面的内容论述了两种事务类型。</p>
		<p>
				<b>JDBC 事务</b>
		</p>
		<p>JDBC事务通过DBMS事务管理器来控制。你可能会为了使用会话Bean中的原有代码而采用JDBC事务将这些代码封装到一个事务中。使用JDBC事务，要调用java.sql.Connection接口的commit和rollback方法。事务启动是隐式的。一个事务的从最近的提交、回滚或连接操作后的第一个SQL的语句开始。（这个规则通常是正确的，但可能DBMS厂商的不同而不同）</p>
		<p>
				<strong>代码资源</strong>
		</p>
		<p>下面的例子在j2eetutorial/examples/src/ejb/warehouse目录下。在命令行窗口中进入j2eetutorial/examples目录执行ant bank命令编译这些源文件，执行ant create-warehouse-table命令创建要用到的表，一个样本WarehouseApp.ear文件在j2eetutorial/example/ears 目录下。</p>
		<p>下面的代码来自WarehouseEJB例子，一个会话Bean通过使用Connection接口的方法来划定Bean管理事务界限。ship方法以调用名为con的连接对象的setAutoCommit方法开始，该方法通知DBMS不要自动提交每个SQL语句。接下来ship 方法更新order_item和inventory数据表。如果更新成功，这个事务就会被提交。如果出现异常，事务就回滚。</p>
		<p>public void ship (String productId, String orderId, int </p>
		<p>quantity) {</p>
		<p>  try {</p>
		<p>      con.setAutoCommit(false);</p>
		<p>      updateOrderItem(productId, orderId);</p>
		<p>      updateInventory(productId, quantity);</p>
		<p>      con.commit();</p>
		<p>   } catch (Exception ex) {</p>
		<p>       try {</p>
		<p>          con.rollback();</p>
		<p>          throw new EJBException("Transaction failed: " +</p>
		<p>             ex.getMessage());</p>
		<p>       } catch (SQLException sqx) {</p>
		<p>           throw new EJBException("Rollback failed: " +</p>
		<p>              sqx.getMessage());</p>
		<p>       }</p>
		<p>   }</p>
		<p>} </p>
		<p>
				<b>JTA 事务</b>
		</p>
		<p>JTA是Java Transaction API 的缩写。这些API 允许你用独立于具体的事务管理器实现的方法确定事务界限。J2EE SDK 事务管理器通过Java事务服务（Java Transaction Service, JTS）实现。但是你的代码并不直接调用JTS中的方法，而是调用JTA方法来替代，JTA方法会调用底层的JTS实现。</p>
		<p>JTA事务被J2EE 事务管理器管理。你可能需要使用一个JTA事务，因为它能够统一操作不同厂商的数据库。一个特定DBMS的事务管理器不能工作在不同种类的数据库上。然而J2EE事务管理器仍然有一个限制——它不支持嵌套事务。就是说，它不能在前一个事务结束前启动另一个事务。</p>
		<p>下面例子的源代码在j2eetutorial/examples/src/ejb/teller目录下，在命令行窗口进入j2eetutorial/examples目录，执行ant teller命令编译这些源文件，执行ant create-bank-teller命令创建要用到的表。一个样本TellerApp.ear文件在j2eetutorial/examples/ears目录下。</p>
		<p>要自己确定事务界限，可以调用javax.transaction.UserTransaction接口的begin、commit和rollback方法来确定事务界限（该接口只能在SessionBean中使用，实体Bean不允许使用用户自定义的）。下面选自TellerBean类的代码示范了UserTransaction的用法。begin和commit方法确定了数据库操作的事务界限，如果操作失败则调用rollback回滚事务并抛出EJBException异常。</p>
		<p>public void withdrawCash(double amount) {</p>
		<p>  UserTransaction ut = context.getUserTransaction();</p>
		<p>  try {</p>
		<p>      ut.begin();</p>
		<p>      updateChecking(amount);</p>
		<p>      machineBalance -= amount;</p>
		<p>      insertMachine(machineBalance);</p>
		<p>      ut.commit();</p>
		<p>   } catch (Exception ex) {</p>
		<p>       try {</p>
		<p>          ut.rollback();</p>
		<p>       } catch (SystemException syex) {</p>
		<p>           throw new EJBException</p>
		<p>              ("Rollback failed: " + syex.getMessage());</p>
		<p>       }</p>
		<p>       throw new EJBException </p>
		<p>          ("Transaction failed: " + ex.getMessage());</p>
		<p>    }</p>
		<p>}</p>
		<p>
				<b>非提交返回事务</b>
		</p>
		<p>使用Bean管理事务的无状态会话Bean在事务返回前必须提交或者返回事务，而有状态的会话Bean没有这个限制。</p>
		<p>对于使用JTA事务的有状态会话Bean，Bean实例和事务的关联越过大量用户调用被保持，甚至被调用的每个商业方法都打开和关闭数据库连接，该市无关联也不断开，直到事务完成（或回滚）。</p>
		<p>对于使用JDBC事务的有状态会话Bean，JDBC连接越过用户调用保持Bean和事务之间的关联。连接关闭，事务关联将被释放。</p>
		<p>
				<b>在Bean管理事务中不允许使用的方法</b>
		</p>
		<p>在Bean管理的事务中不能调用EJBContext接口的getRollbackOnly和setRollbackOnly方法，这两个方法只能在容器管理事务中被调用。在Bean管理事务中，应调用UserTransaction接口的getStatus和rollback方法。</p>
		<h2>
				<a name="#14-4">
				</a>
				<strong>
						<font size="3">四．企业Bean事务摘要</font>
				</strong>
		</h2>
		<p>如果你不能确定怎么在企业Bean中使用事务，可以用这个小技巧：在Bean的部署描述符中，制定事务类型为容器管理，把整个Bean（所有方法）的事务属性设置为Required。大多数情况下，这个配置可以满足你的事务需求。</p>
		<p>表14-2列出了不同类型的企业Bean所允许使用的事务类型。实体Bean只能使用容器管理事务，但可以在部署描述符中配置事务属性，并可以调用EJBContext接口的setRollbackOnly方法来回滚事务。</p>
		<table cellspacing="0" cellpadding="0" align="center" border="1">
				<tbody>
						<tr>
								<td colspan="4">
										<p align="center">
												<b>表 14-2 </b>
												<b>企业Bean</b>
												<b>允许的事务类型</b>
										</p>
								</td>
								<td height="21">
								</td>
						</tr>
						<tr>
								<td rowspan="2">
										<p>
												<b>企业Bean</b>
												<b>类型</b>
										</p>
								</td>
								<td rowspan="2">
										<p>
												<b>容器管理事务</b>
										</p>
								</td>
								<td colspan="2">
										<p>Bean<b>管理事务</b></p>
								</td>
								<td> </td>
						</tr>
						<tr>
								<td>
										<p>JTA</p>
								</td>
								<td>
										<p>JDBC</p>
								</td>
								<td> </td>
						</tr>
						<tr>
								<td>
										<p>实体Bean</p>
								</td>
								<td>
										<p>Y</p>
								</td>
								<td>
										<p>N</p>
								</td>
								<td>
										<p>N</p>
								</td>
								<td> </td>
						</tr>
						<tr>
								<td>
										<p>会话Bean</p>
								</td>
								<td>
										<p>Y</p>
								</td>
								<td>
										<p>Y</p>
								</td>
								<td>
										<p>Y</p>
								</td>
								<td> </td>
						</tr>
						<tr>
								<td>
										<p>Message-driven</p>
								</td>
								<td>
										<p>Y</p>
								</td>
								<td>
										<p>Y</p>
								</td>
								<td>
										<p>Y</p>
								</td>
								<td> </td>
						</tr>
				</tbody>
		</table>
		<p>会话Bean既可以使用容器管理事务也可以使用Bean管理事务。Bean管理事务又有两种类型：JDBC事务和JTA事务。JDBC事务使用Connection接口的commit和rollback方法来划分事务界限。JTA事务使用UserTransaction接口的begin、commit和rollback方法来划分事务界限。</p>
		<p>在Bean管理事务的会话Bean中，混合使用JTA事务和JDBC事务是可能的。但是我不推荐这样使用，因为这样会造成代码的调试和维护都很困难。</p>
		<p>Message-driver Bean和会话Bean一样既可以使用容器管理事务也可以使用Bean管理事务。</p>
		<h2>
				<a name="#14-5">
				</a>
				<strong>
						<font size="3">五．事务超时</font>
				</strong>
		</h2>
		<p>对于容器管理事务，事务超时间隔是通过设置default.properties文件中ransaction.timeout属性的值来确定的，该文件在J2EE SDK安装目录的config子目录下。如下例将事务超时间隔设置为5秒钟：</p>
		<p>transaction.timeout=5</p>
		<p>这样，当事务在5秒钟内还没有完成，容器将回滚该事务。</p>
		<p>J2EE SDK安装后，超时间隔的缺省值为0，表示不计算超时，无论事务执行多长时间，除非异常出错回滚，一直等待事务完成。</p>
		<p>只有使用容器管理事务的企业Bean才会受到transaction.timeout属性值的影响。Bean管理的JTA事务使用UserTransaction接口的setTransactionTimeout方法来设置事务超时间隔。</p>
		<h2>
				<a name="#14-6">
				</a>
				<strong>
						<font size="3">六．隔离级别</font>
				</strong>
		</h2>
		<p>事务不仅保证事务界限内的数据库操作全部完成（或回滚）同时还隔离数据库更新语句。隔离级别描述被修改的数据对其他事物的可见度。</p>
		<p>假如一个应用程序在事务中修改一个顾客的电话号码，在事务结束前另一个应用程序要读取该条记录的电话号码。那么第二个应用程序是读取修改过但还没提交的数据，还是读取未修改前的老数据呢？答案就取决于事务的隔离级别。如果事务允许其他程序读取未提交的数据，会因为不用等待事务结束而提高性能，同时也有一个缺点，如果事务回滚，其他应用程序读取的将是错误的数据。</p>
		<p>容器管理持久性（CMP）的实体Bean的事务级别无法修改，它们使用DBMS的默认个理解别，通常是READ_COMMITTED。</p>
		<p>Bean管理持久性（BMP）的实体Bean和两种会话Bean都可以通过在程序中调用底层DBMS提供的API来设置事务级别。例如，一个DBMS可能允许你如下调用setTransactionIsolation方法将隔离级别设置成可读取未提交数据：</p>
		<p>Connection con;</p>
		<p>...</p>
		<p>con.setTransactionIsolation(TRANSACTION_READ_UNCOMMITTED);</p>
		<p>不要在事务执行期间更改隔离级别，通常隔离级别的更改会引起DBMS产生一次隐式提交。因为隔离级别的控制会跟具体的DBMS厂商不同而不同，具体的信息请参考DBMS的文档。J2EE平台规范不包括隔离级别标准。</p>
		<h2>
				<a name="#14-7">
				</a>
				<strong>
						<font size="3">七．更新多个数据库</font>
				</strong>
		</h2>
		<p>J2EE事务管理器控制着除了Bean管理的JDBC事务以外的所有企业Bean事务，它允许企业Bean在同一个事务中更新多个数据库。下面示范在单个事务中更新多个数据库的两个应用。</p>
		<p>图14-2中，客户端调用Bean-A的商业方法，商业方法启动一个事务，更新数据库X和Y，Bean-A的商业方法有调用Bean-B的商业方法，Bean-B的商业方法更新数据库Z然后返回事务的控制权给Bean-A的商业方法，由Bean-A提交该事务。三个数据库的更新都在同一个事务中发生。</p>
		<p>
				<img height="301" src="http://www.leftworld.net/online/j2ee/14/image004.gif" width="459" border="0" />
		</p>
		<p>
				<strong>图 14-2 更新多个数据库</strong>
		</p>
		<p>图14-3中，客户端调用Bean-A的商业方法，该商业方法启动一个事务并更新数据库X，然后调用另一个J2EE服务器中的Bean-B的方法，该方法更新数据库Y。J2EE服务器保证两个数据库的更新都在同一个事务中进行（笔者认为应该是第一个J2EE服务器的事务管理器管理整个事物）。</p>
		<p>
				<img height="311" src="http://www.leftworld.net/online/j2ee/14/image006.gif" width="443" border="0" />
		</p>
		<p>
				<strong>图 14-3 跨越J2EE服务器更新多个数据库</strong>
		</p>
		<h2>
				<a name="#14-8">
				</a>
				<strong>
						<font size="3">八．Web 组件事务</font>
				</strong>
		</h2>
		<p>Web组件中划分事务界限可以使用java.sql.Connection接口和javax.transaction.UserTransaction接口中的任意一个。跟Bean管理事务的会话Bean使用一样的两个接口。这两个接口的使用方法参考前面几节的内容。Web组件事务的例子在第10章Servlet技术第四节共享信息的访问数据库小节讲述过。</p>
<img src ="http://www.blogjava.net/joeyeezhang/aggbug/76412.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/joeyeezhang/" target="_blank">joeyeezhang</a> 2006-10-20 16:39 <a href="http://www.blogjava.net/joeyeezhang/archive/2006/10/20/76412.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>the difference of Hashtable and HashMap</title><link>http://www.blogjava.net/joeyeezhang/archive/2006/05/27/48474.html</link><dc:creator>joeyeezhang</dc:creator><author>joeyeezhang</author><pubDate>Sat, 27 May 2006 05:16:00 GMT</pubDate><guid>http://www.blogjava.net/joeyeezhang/archive/2006/05/27/48474.html</guid><wfw:comment>http://www.blogjava.net/joeyeezhang/comments/48474.html</wfw:comment><comments>http://www.blogjava.net/joeyeezhang/archive/2006/05/27/48474.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/joeyeezhang/comments/commentRss/48474.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/joeyeezhang/services/trackbacks/48474.html</trackback:ping><description><![CDATA[
		<br />1.Hashtable是Dictionary的子类，HashMap是Map接口的一个实现类；<br />2.Hashtable中的方法是同步的，而HashMap中的方法在缺省情况下是非同步的。即是说，在多线程应用程序中，不用专门的操作就安全地可以使用Hashtable了；而对于HashMap，则需要额外的同步机制。但HashMap的同步问题可通过Collections的一个静态方法得到解决：<br />Map Collections.synchronizedMap(Map m)<br />这个方法返回一个同步的Map，这个Map封装了底层的HashMap的所有方法，使得底层的HashMap即使是在多线程的环境中也是安全的。<br />3.在HashMap中，null可以作为键，这样的键只有一个；可以有一个或多个键所对应的值为null。当get()方法返回null值时，即可以表示HashMap中没有该键，也可以表示该键所对应的值为null。因此，在HashMap中不能由get()方法来判断HashMap中是否存在某个键，而应该用containsKey()方法来判断。<br /><img src ="http://www.blogjava.net/joeyeezhang/aggbug/48474.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/joeyeezhang/" target="_blank">joeyeezhang</a> 2006-05-27 13:16 <a href="http://www.blogjava.net/joeyeezhang/archive/2006/05/27/48474.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>《最后期限》-项目管理101条重要原则</title><link>http://www.blogjava.net/joeyeezhang/archive/2006/05/24/47892.html</link><dc:creator>joeyeezhang</dc:creator><author>joeyeezhang</author><pubDate>Wed, 24 May 2006 13:31:00 GMT</pubDate><guid>http://www.blogjava.net/joeyeezhang/archive/2006/05/24/47892.html</guid><wfw:comment>http://www.blogjava.net/joeyeezhang/comments/47892.html</wfw:comment><comments>http://www.blogjava.net/joeyeezhang/archive/2006/05/24/47892.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/joeyeezhang/comments/commentRss/47892.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/joeyeezhang/services/trackbacks/47892.html</trackback:ping><description><![CDATA[
		<p>
				<br />《最后期限》-项目管理重要原则</p>
		<br />
		<p>优质管理的四大要素：<br />    选择正确的人。<br />    为他们分配正确的工作。<br />    保持他们的积极性。<br />    帮助团队凝聚起来并保持团队的凝聚力。<br />    (其他一切都只是"文案"。)</p>
		<br />
		<p>安全和变化<br />    除非感到安全，否则人们就不能去迎接变化。 <br />    在所有成功的工程中(以及在绝大多数其他有价值的工作中)，变化都是基本的要素之一。 <br />    安全感的缺乏会让人们反对变化。 <br />    逃避风险是致命的，因为这会让你也得不到与风险同在的利益。 <br />    人们可能会因为来自客观世界的直接的恐吓而觉得没有安全感，但是如果察觉到管理者可能滥用权力来惩罚自己，他们也会觉得没有安全感。</p>
		<br />
		<p>负面效应<br />    威胁不是提高业绩最好的方法。<br />    如果分配的时间一开始就不够，不管威胁有多么吓人，工作也无法按时完成。<br />    更糟糕的是，如果目标没有实现，你就必须兑现你的威胁。</p>
		<br />
		<p>管理者必需的身体部位<br />    管理涉及到心、肠胃、灵魂和鼻子。<br />    因此……用心来领导，相信你的肠胃(相信你的预感)，构筑团队的灵魂，训练一个能嗅出谎言的鼻子。</p>
		<br />
		<p>用指挥战争来作为管理的一个比喻<br />    在战役开始的时候，管理者真正的工作已经完成了。</p>
		<br />
		<p>面试和招聘<br />    招聘涉及到所有与管理相关的身体部位：心、灵魂、鼻子和肠胃(但是主要是肠胃)。<br />    不要试图单独去招聘——两副肠胃远比一副肠胃的两倍要好。<br />    对于新的雇员，让他们承担与以前曾经成功过的同样难度的项目，把有挑战性的目标推迟到下一次。<br />    征求提示：你最希望雇的那个人可能还知道其他很好的人选。<br />    多听，少说。<br />    如果先把材料整理好，那么所有的事情都会进行得更好。</p>
		<br />
		<p>
				<br />生产力的提高<br />    没有"短期生产力提高"这样的东西。<br />    生产力的提高是来自长期投资的。<br />    任何承诺立刻见效的东西都很可能是江湖游医所卖的万灵油。</p>
		<br />
		<p>风险控制<br />    通过控制风险来管理项目。<br />    为每个项目创建并维护风险统计表。<br />    跟踪根源性的风险，而不只是最后那讨厌的结果。<br />    评估每种风险具体化的概率和可能造成的开销。<br />    对于每种风险，预测标志其具体化的早期征兆。<br />    任命一个风险控制官，这个人不应该维护组织内部"我能行"的态度。<br />    建立简单的(可能是匿名的)通道，让坏消息能传递到高层。</p>
		<br />
		<p>防止失败<br />　    壮士断腕。<br />　    控制住失败比优化成功更能提高你全面的成绩。<br />　    要有闯劲，尽早取消失败的工作。<br />　    除非必要，否则就不要自己去凝聚一个团队：出去找一个已经成型的团队来用。<br />　    保持好的团队在一起(只要他们自己愿意)，以帮助你的继任者避免团队凝聚得慢或者不能凝聚的问题。<br />　    把凝聚在一起的团队--准备好、并且也愿意接受新的工作--作为项目的收获之一。<br />　    项目开始时浪费的一天和最后阶段浪费的一天对项目造成的伤害是同等的。<br />    有无数种方法可以浪费一天的时间……但是没有任何一种方法可以拿回一天的时间。</p>
		<br />
		<p>开发过程的建模和模拟<br />　    将你关于完成工作过程的直觉建模。<br />　    在同事的交流中使用这些模型，以便交流、提炼关于项目运转的思想。<br />　    用模型来模拟项目的结果。<br />　    根据实际的结果来调整模型。</p>
		<br />
		<p>"病态的政治"<br />    每一天，你都必须准确度拿自已的工作去打赌......<br />    ......但是这也不能保证"病态的政治"不会影响你。<br />    "病态的政治"可能在任何地方出现，哪怕是在最健康的组织里面。<br />    "病态的政治"的特征：对个人权势的渴望超过了组织本身的目标。<br />    即使这种不合理的目标与组织的目标背道而驰，它也可能出现。<br />    "病态的政治"的副作用：它使精简的项目变得危险。</p>
		<br />
		<p>度量<br />    度量每个产品的规模。<br />    不要执着于单位——在等待客观度量的时候，先用你自己的主观单位。<br />    从所有能得到的原始数据(可计算的软件特征)自己构造度量单位。<br />    不断完善你的度量方程式，直到它的计算结果与原始数据库中的项目工作量有最好的对应关系。<br />    借助数据库画一条趋势线，把预期的工作量作为人造度量单位值的函数显示出来。<br />    现在，针对每个要度量的项目，计算出人造度量单位值，并根据这个值在趋势线上找到预期工作量值。<br />    用生产力趋势周围的干扰水平作为映射的公差指示。</p>
		<br />
		<p>过程和过程改进<br />    好的过程和持续的过程是绝好的目标。<br />    它们也是非常自然的目标：优秀的技术工作者一定会关注它们，而不管你是否告诉他们。<br />    正式的过程改进程序需要花钱、花时间；特定的过程改进工作还会延缓项目进度。尽管最终体现出生产力上的收获，它们也不可能抵消花在过程改进上的时间。<br />    但是项目有希望从单个的、正确选择的方法改进中得到足够的收益，并赢回为这次改变付出的时间和金钱。<br />    在项目进行过程中，不要希望在超过一个地方的范围内实施必改进。多种技术的改进程序（比如说提高整整一个CMM等级)很可能让项目比不实施这些程序完成得更晚。<br />    标准过程的危险就在于人们可能失去重要的走捷径的机会。<br />    特别是对于人员超编的项目，标准的过程看去会很严谨，因为它们制造出了足够的工作（有用的和无用的），让所有人都忙于不停。</p>
		<br />
		<p>改变完成工作的方式<br />    如果不大幅减少调试时间，就没有办法让项目大幅度提前完成。<br />    高速完成的项目用在调试上的时间成比例地少得多。<br />    高速完成的项目用在设计上的时间也成比例地多得多。<br />    如果你不关心别人， 不照顾别人，就别想让他们做一些不同寻常的事情。如果要让他们改变，就必须去了解（并赞赏）他们的过去。</p>
		<br />
		<p>压力的效果<br />    压力之下的人无法更快的思考。<br />    增加加班时间只会降低生产力。<br />    短期的压力乃至于加班可能是有用的策略，因为它们能使员工集中精力，并且让他们感到工作的重要性。但是长期的压力肯定是错误的。<br />    经理们之所以会施加那么多压力，也许是因为他们不知道该做什么，或者因为其他办法的困难而感到气馁。<br />    最坏的猜想：使用压力和加班的真正原因是为了在项目失败的时候让所有人看上去能好一点。<br />    <br />愤怒的经理<br />    管理中的愤怒和羞辱是会传染的。如果高级管理者喜欢骂人，低级管理者也会有样学样（就象经常被骂的小孩很容易变成爱骂人的父母）。<br />    管理中的辱骂常被认为是一种刺激，可以让员工提高效率。在" 胡萝卜加大棒"的管理策略中，辱骂是最常见的"大棒"。但是，哪有人被辱骂之后还能做得更好的？<br />    如果经理使用辱骂的方法来刺激员工，这就表现经理的无能，而不是员工的无能。</p>
		<br />
		<p>含糊的规格文档<br />    规格文档中的含糊标志着不同的系统参与者之间存在未解决的冲突。<br />    如果一份规格文档不包含完整的输入输出列表，那么它就是毫无希望的：它根本就没有开始说明任何东西。<br />    没有人会告诉你一份规格文档是不是糟糕。人们往往倾向于责备自己，而不责备文档。</p>
		<br />
		<p>冲突<br />    只要在开发过程中有多个参与者，就一定会有冲突存在。<br />    创建、安装系统的业务中特别容易出现冲突。<br />    绝大多数系统开发团队都缺乏解决冲突的能力。<br />    冲突应当引起重视。冲突并不是缺乏职业道德的行为。<br />    应当提前声明：所有人的"赢"都是受重视的。确保每个级别的人都能赢。<br />    谈判困难，调解容易。<br />    如果两个人的利益是完全或者部分相斥的，预先做好安排，准备好请双方通过调解来解决冲突。<br />    记住：我们都站在同一边；跟我们对立的，是我们要解决的问题。<br />    <br />催化剂的角色<br />    有这样一种催化剂式的人格。这样的人会帮助团队成型并凝聚，保持团队的健康和生产力，从而对项目作出贡献。就算"催化剂"别的什么都不干（其实，通常他们还会干很多别的事），这种催化剂的角色也是重要而有价值的。<br />    调解是"催化剂"的一项特殊工作。调解是可以学的，而且只需要很小的投资就能学会。<br />    调解应该从一个小小的仪式开始。"我能帮你们调解一下吗？"在解决冲突的时候，这是必要的第一个步骤。</p>
		<br />
		<p>人类的错误<br />    将你置于死的，不是你不知道的东西……而正是你"知道"绝不会置你于死地的东西。</p>
		<br />
		<p>人员安排<br />    在早期，人员超编会迫使项目跨过关键的设计阶段（这是为了让所有的人都有事可做）。<br />    如果在设计完成之前， 工作先被分给了许多人，那么人与人之间、工作与工作之间的接口就会很复杂。<br />    这会使团队内部耦合度提高，会议时间、重复劳动和无效的工作都会增加。<br />    理想的人员安排是这样的：在项目的大部分时间内由小型核心团队来做设计工作，在开发的最后阶段（时间安排的最后1/6）加入大量的人手。<br />    可怕的猜想：时间安排紧迫的项目，与时间安排比较合理的项目比起来，完成的时间反而会更长。</p>
		<br />
		<p>项目社会学<br />    让不必与会的人可以放心离开，从而保持会议的精简。有一份公开的议程，并严格执行，这是最简单的办法。<br />    项目需要仪式。<br />    用小小的仪式来使人们注意项目的目标的理想状态：小规模会议、零缺陷工作等等。<br />    采取行动，防止人们随便发怒。<br />    记住：愤怒=恐惧。随便对下级发怒的经理一定是因为恐惧才会这样做的。<br />    意见：如果所有人都懂得"愤怒=恐惧"这个道理，就能明显看出发怒的人是在害怕。由于无法隐瞒自己的恐惧，他也就不会再生气了。（这不能解决这些生气的人的问题，但是肯定可以让其他人好受一些。）<br />    <br />"病态的政治"(旧话重提)<br />    别想根治一个病态的人。<br />    不要浪费时间，也不要因为尝试治疗上司的病态而使自己受到威胁。<br />    有时候，你唯一的的选择就是等待，等问题自己解决，或者等一个让你继续前进的机会。<br />    奇迹是有可能发生的(但是千万别去指望它)。</p>
		<br />
		<p>精兵简政<br />    精兵简政是失败公司使用的办法，它使员工负但失败的责任。<br />    公司的目标应该正好相反：兴旺而人性化。<br />    当你听到"精兵简政"这个词的时候，请记住它的弦外之音：失败和恐吓。</p>
		<br />
		<p>基本常识<br />    项目既需要目标，也需要计划。<br />    而且这两者应该不同。</p>
<img src ="http://www.blogjava.net/joeyeezhang/aggbug/47892.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/joeyeezhang/" target="_blank">joeyeezhang</a> 2006-05-24 21:31 <a href="http://www.blogjava.net/joeyeezhang/archive/2006/05/24/47892.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>tag</title><link>http://www.blogjava.net/joeyeezhang/archive/2006/03/21/36577.html</link><dc:creator>joeyeezhang</dc:creator><author>joeyeezhang</author><pubDate>Tue, 21 Mar 2006 02:58:00 GMT</pubDate><guid>http://www.blogjava.net/joeyeezhang/archive/2006/03/21/36577.html</guid><wfw:comment>http://www.blogjava.net/joeyeezhang/comments/36577.html</wfw:comment><comments>http://www.blogjava.net/joeyeezhang/archive/2006/03/21/36577.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/joeyeezhang/comments/commentRss/36577.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/joeyeezhang/services/trackbacks/36577.html</trackback:ping><description><![CDATA[
		<font face="Courier New" size="2">&gt;“山高人为峰”，这句话也可以用在技术的研究上。</font>
		<p>
				<font face="Courier New" size="2">   <strong>在</strong>jsp规范里，标签具有比javabean更丰富的运行时协议。它可以非常机密的和jsp的表示逻辑联系在一起，同时，又具有javabean相同业务处理能力。所以，标签的学习成为迫切的需要，但为了满足实际项目的开发，自定义标签的学习是不容错过的。</font>
		</p>
		<p>
				<font face="Courier New" size="2">   <strong>通</strong>过实现接口或者继承现有的类，我们就可以开发自定义的标签。</font>
		</p>
		<p>
				<font face="Courier New">
						<font size="2">   <strong>常</strong>用的接口有：             <em>JspTag</em></font>
				</font>
		</p>
		<p>
				<font face="Courier New">
						<font size="2">                       <em>Tag</em>       <em>SimpleTag</em>&lt;----<strong>SimpleTagSupport</strong></font>
				</font>
		</p>
		<p>
				<font face="Courier New" size="2">               <em>IterationTag</em> &lt;----<strong>TagSupport</strong>            </font>
		</p>
		<p>
				<font face="Courier New">
						<font size="2">                    <em>BodyTag</em>&lt;----<strong>BodyTagSupport</strong></font>
				</font>
		</p>
		<p>
				<font face="Courier New" size="2">   <strong>自</strong>定义标签的开发包括两个部分的开发：</font>
		</p>
		<p>
				<font face="Courier New" size="2">（1）、开发标签的处理程序（java类）</font>
		</p>
		<p>
				<font face="Courier New" size="2">（2）、标签描述文件（.tld文件）</font>
		</p>
		<p>
				<font face="Courier New" size="2">   <strong>自</strong>定义标签的种类有许多，可以根据实际项目的需要进行编写。但为了不重复的开发轮子，jsp标准推出JSTL（标准标签库）。</font>
		</p>
		<p>
				<font face="Courier New" size="2">   <strong>开</strong>始我们的旅行吧，只要是快乐的，谁都想干，做的好与不好就另当别论了。</font>
		</p>
		<p>
				<font face="Courier New" size="2">（1）从Hello World开始吧，自定义一个类它实现Tag接口，Tag接口主要定义的是标签声明周期的方法，比如：doStartTag()，doEndTag()等，通过PageContext对象来访问Jsp页面的上下文：</font>
		</p>
		<p>
				<font face="Courier New" size="2">package com.xmddl.tag.demo;</font>
		</p>
		<p>
				<font face="Courier New" size="2">import javax.servlet.jsp.JspException;<br />import javax.servlet.jsp.PageContext;<br />import javax.servlet.jsp.tagext.Tag;</font>
		</p>
		<p>
				<font face="Courier New" size="2">public class HelloTag implements Tag {<br /> private PageContext context;<br /> private Tag parent;<br /> public void setPageContext(PageContext ctx) {<br />  this.context=ctx;</font>
		</p>
		<p>
				<font face="Courier New" size="2"> }</font>
		</p>
		<p>
				<font face="Courier New" size="2"> public void setParent(Tag tag) {<br />  this.parent=tag;</font>
		</p>
		<p>
				<font face="Courier New" size="2"> }</font>
		</p>
		<p>
				<font face="Courier New" size="2"> public Tag getParent() {<br />  return this.parent;<br /> }</font>
		</p>
		<p>
				<font face="Courier New" size="2"> public int doStartTag() throws JspException {<br />  System.out.println("doStartTag");<br />  return Tag.SKIP_BODY;<br /> }</font>
		</p>
		<p>
				<font face="Courier New" size="2"> public int doEndTag() throws JspException {<br />  System.out.println("doStartTag");<br />  try {<br />   this.context.getOut().write("Hello World");<br />  } catch (Exception e) {<br />   e.printStackTrace();<br />  }<br />  return Tag.EVAL_PAGE;<br /> }</font>
		</p>
		<p>
				<font face="Courier New" size="2"> public void release() {<br />  // TODO Auto-generated method stub<br /> }</font>
		</p>
		<p>
				<font face="Courier New" size="2">}<br /></font>
		</p>
		<p>
				<font face="Courier New" size="2"> Tag接口中静态常量：</font>
		</p>
		<p>
				<font face="Courier New" size="2">Tag.SKIP_BODY</font>
		</p>
		<p>
				<font face="Courier New" size="2">Tag.EVAL_PAGE</font>
		</p>
		<p>
				<font face="Courier New" size="2">Tag.EVAL_BODY_INCLUDE</font>
		</p>
		<p>
				<font face="Courier New" size="2">Tag.SKIP_PAGE</font>
		</p>
		<p>
				<font face="Courier New" size="2">（2）编写标签描述文件（.tld）：</font>
		</p>
		<p>
				<font face="Courier New" size="2">&lt;?xml version="1.0" encoding="UTF-8" ?&gt;<br />&lt;taglib xmlns="</font>
				<a href="http://java.sun.com/xml/ns/j2ee">
						<font face="Courier New" size="2">http://java.sun.com/xml/ns/j2ee</font>
				</a>
				<font face="Courier New" size="2">"<br />    xmlns:xsi="</font>
				<a href="http://www.w3.org/2001/XMLSchema-instance">
						<font face="Courier New" size="2">http://www.w3.org/2001/XMLSchema-instance</font>
				</a>
				<font face="Courier New" size="2">"<br />    xsi:schemaLocation="</font>
				<a href="http://java.sun.com/xml/ns/j2ee">
						<font face="Courier New" size="2">http://java.sun.com/xml/ns/j2ee</font>
				</a>
				<font face="Courier New" size="2">
				</font>
				<a href="http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd">
						<font face="Courier New" size="2">http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd</font>
				</a>
				<font face="Courier New" size="2">"<br />    version="2.0"&gt;</font>
		</p>
		<p>
				<font face="Courier New" size="2">  &lt;description&gt;xmddl 1.1 hello library&lt;/description&gt;<br />  &lt;display-name&gt;xmddl hello&lt;/display-name&gt;<br />  &lt;tlib-version&gt;1.1&lt;/tlib-version&gt;<br />  &lt;short-name&gt;hello&lt;/short-name&gt;<br />  &lt;uri&gt;http://www.xmddl.com/tag&lt;/uri&gt;<br /> <br />  </font>
				<strong>
						<font face="Courier New" size="2">&lt;tag&gt;<br />    &lt;description&gt;我的Hello World&lt;/description&gt;<br />    &lt;name&gt;hello&lt;/name&gt;<br />    &lt;tag-class&gt;com.xmddl.tag.demo.HelloTag&lt;/tag-class&gt;<br />    &lt;body-content&gt;empty&lt;/body-content&gt;<br />  &lt;/tag&gt;</font>
				</strong>
		</p>
		<p>
				<font face="Courier New" size="2">&lt;/taglib&gt;</font>
		</p>
		<p>
				<font face="Courier New" size="2">（3）、在web.xml中对标签的引用：</font>
		</p>
		<p>
				<font face="Courier New" size="2">&lt;?xml version="1.0" encoding="UTF-8"?&gt;<br />&lt;!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "</font>
				<a href="http://java.sun.com/dtd/web-app_2_3.dtd">
						<font face="Courier New" size="2">http://java.sun.com/dtd/web-app_2_3.dtd</font>
				</a>
				<font face="Courier New" size="2">"&gt;<br />&lt;web-app&gt;<br /> &lt;taglib&gt;<br />  </font>
				<font face="Courier New">
						<font size="2">
								<strong>&lt;taglib-uri&gt;http://www.xmddl.com/tag&lt;/taglib-uri&gt;<br />  &lt;taglib-location&gt;/WEB-INF/mytag.tld&lt;/taglib-location&gt;<br /> &lt;/taglib&gt;<br /></strong>&lt;/web-app&gt;</font>
				</font>
		</p>
		<p>
				<font face="Courier New" size="2">（4）、编写测试页面：</font>
		</p>
		<p>
				<font face="Courier New" size="2">&lt;%@ page language="java" pageEncoding="UTF-8" import="java.util.*"%&gt;<br />&lt;%@ taglib uri="http://www.xmddl.com/tag" prefix="lu"%&gt;<br />&lt;html&gt;<br />  &lt;head&gt;<br />    &lt;title&gt;MyJsp.jsp&lt;/title&gt;<br />    &lt;meta http-equiv="pragma" content="no-cache"&gt;<br />    &lt;meta http-equiv="cache-control" content="no-cache"&gt;<br />    &lt;meta http-equiv="expires" content="0"&gt;    <br />    &lt;meta http-equiv="keywords" content="keyword1,keyword2,keyword3"&gt;<br />    &lt;meta http-equiv="description" content="This is my page"&gt;<br />  &lt;/head&gt;<br />  <br />  &lt;body&gt;<br />    &lt;h1&gt;This is a test tag page for person.&lt;/h1&gt; &lt;br&gt;<br />  </font>
				<font face="Courier New">
						<font size="2">
								<strong>&lt;lu:hello/&gt;<br /></strong>  &lt;/body&gt;<br />&lt;/html&gt;<br />下面看看标签中的属性是如何使用的，我们直接继承TagSupport类来编写：</font>
				</font>
		</p>
		<p>
				<font face="Courier New" size="2"> package com.xmddl.tag.demo;</font>
		</p>
		<p>
				<font face="Courier New" size="2">import javax.servlet.jsp.JspException;<br />import javax.servlet.jsp.tagext.Tag;<br />import javax.servlet.jsp.tagext.TagSupport;</font>
		</p>
		<p>
				<font face="Courier New" size="2">public class HelloSupport extends TagSupport {<br /> private String value;<br /> private static final long serialVersionUID = 1L;<br /> public HelloSupport(){<br />  super();<br /> }<br /> public int doStartTag() throws JspException {<br />  System.out.println("doStartTag");<br />  return TagSupport.EVAL_BODY_INCLUDE;<br /> }</font>
		</p>
		<p>
				<font face="Courier New" size="2"> public int doEndTag() throws JspException {<br />  System.out.println("doEndTag");<br />  try {<br />   this.pageContext.getOut().write("标签的属性值："+this.value);<br />  } catch (Exception e) {<br />   e.printStackTrace();<br />  }<br />  return Tag.EVAL_PAGE;<br /> }<br /> public int doAfterBody() throws JspException{<br />  System.out.println("doAfterBody");<br />  try {<br />   this.pageContext.getOut().write("&lt;br&gt;以上为标签的内容&lt;br&gt;");<br />  } catch (Exception e) {<br />   e.printStackTrace();<br />  }<br />  return TagSupport.EVAL_BODY_INCLUDE;<br />  <br /> }<br /> public String getValue() {<br />  return value;<br /> }<br /> public void setValue(String value) {<br />  this.value = value;<br /> }<br />}<br />======================</font>
		</p>
		<p>
				<font face="Courier New" size="2">...</font>
		</p>
		<p>
				<font face="Courier New" size="2">&lt;tag&gt;<br />    &lt;name&gt;hellosupport&lt;/name&gt;<br />    &lt;tag-class&gt;com.xmddl.tag.demo.HelloSupport&lt;/tag-class&gt;<br />    &lt;body-content&gt;JSP&lt;/body-content&gt;<br />    &lt;attribute&gt;<br />        &lt;name&gt;value&lt;/name&gt;<br />        &lt;required&gt;true&lt;/required&gt;<br />        &lt;rtexprvalue&gt;false&lt;/rtexprvalue&gt;<br />    &lt;/attribute&gt;<br />  &lt;/tag&gt;</font>
		</p>
		<p>
				<font face="Courier New" size="2">...</font>
		</p>
		<p>
				<font face="Courier New" size="2">=======================</font>
		</p>
		<p>
				<font face="Courier New" size="2">&lt;%@ page language="java" pageEncoding="gb2312" import="java.util.*"%&gt;<br />&lt;%@ taglib prefix="lu" uri="</font>
				<a href="http://www.xmddl.com/tag">
						<font face="Courier New" size="2">http://www.xmddl.com/tag</font>
				</a>
				<font face="Courier New" size="2">" %&gt;<br />&lt;html&gt;<br />  &lt;head&gt;<br />  &lt;/head&gt;<br />  &lt;body&gt;</font>
		</p>
		<p>
				<font face="Courier New" size="2">    &lt;h1&gt;This is a test tag page for person.&lt;/h1&gt; &lt;br&gt;<br />  &lt;lu:hellosupport value="red"&gt;xmddl&lt;/lu:hellosupport&gt;<br />  &lt;/body&gt;<br />&lt;/html&gt;</font>
		</p>
		<p>
				<font face="Courier New" size="2"> 利用TagSupport带来很大的方便，只要重写其中的部分方法即可实现一个标签处理程序，以上开发的标签程序和JSTL1.1标签库一起使用时出现一些疑问，原来标签的uri="</font>
				<a href="http://java.sun.com/jsp/jstl/core">
						<font face="Courier New" size="2">http://java.sun.com/<strong>jsp</strong>/jstl/core</font>
				</a>
				<font face="Courier New" size="2">"，但是，在如下的测试中会发现c:out中的值输不出来，所以，只能改为uri="</font>
				<a href="http://java.sun.com/jstl/core">
						<font face="Courier New" size="2">http://java.sun.com/jstl/core</font>
				</a>
				<font face="Courier New" size="2">"，</font>
		</p>
		<p>
				<font face="Courier New" size="2">&lt;%@ page language="java" pageEncoding="gb2312" import="java.util.*"%&gt;<br />&lt;%@ taglib prefix="lu" uri="</font>
				<a href="http://www.xmddl.com/tag">
						<font face="Courier New" size="2">http://www.xmddl.com/tag</font>
				</a>
				<font face="Courier New" size="2">" %&gt;<br />&lt;%@ taglib prefix="c" uri="</font>
				<a href="http://java.sun.com/jstl/core">
						<font face="Courier New" size="2">http://java.sun.com/jstl/core</font>
				</a>
				<font face="Courier New" size="2">" %&gt;<br /><br />&lt;html&gt;<br />  &lt;head&gt;<br />  &lt;/head&gt;<br />  &lt;body&gt;</font>
		</p>
		<p>
				<font face="Courier New" size="2">    &lt;h1&gt;This is a test tag page for person.&lt;/h1&gt; &lt;br&gt;<br />    &lt;%request.setAttribute("name","xmddl"); %&gt;<br />    &lt;%session.setAttribute("names","xmddls"); %&gt;<br />    <br />  &lt;lu:hellosupport value="red"&gt;xmddl&lt;/lu:hellosupport&gt;&lt;hr&gt;<br />  &lt;c:forEach begin="1" end="5" step="1"&gt;<br />   name=&lt;c:out value="${name}"&gt;&lt;/c:out&gt;<br />   names=&lt;c:out value="${names}"&gt;&lt;/c:out&gt;&lt;br&gt;<br />  &lt;/c:forEach&gt;<br />  &lt;/body&gt;<br />&lt;/html&gt;<br />输出如下：</font>
		</p>
		<h1>
				<font face="Courier New" size="2">This is a test tag page for person.</font>
				<br />
				<font face="Courier New" size="2">xmddl<br />以上为标签的内容<br />标签的属性值：red </font>
		</h1>
		<font face="Courier New" size="2">
				<hr />
		</font>
		<p>
				<font face="Courier New" size="2">name=xmddl names=xmddls<br />name=xmddl names=xmddls<br />name=xmddl names=xmddls<br />name=xmddl names=xmddls<br />name=xmddl names=xmddls</font>
		</p>
		<p>
				<font face="Courier New" size="2">接着看看迭代标签的开发，首先，我们关注的是接口IterationTag，还有它的实现类TagSupport，接口BodyTag对IterationTag的扩展，还有BodyTag的实现类BodyTagSupport。</font>
		</p>
		<p>
				<font face="Courier New" size="2">package com.xmddl.tag.demo;</font>
		</p>
		<p>
				<font face="Courier New" size="2">import java.io.IOException;</font>
		</p>
		<p>
				<font face="Courier New" size="2">import javax.servlet.jsp.JspException;<br />import javax.servlet.jsp.tagext.BodyTagSupport;<br />public class MyIteratorSupport extends BodyTagSupport {<br /> private static final long serialVersionUID = 1L;<br /> public String count;<br /> public String strColor;<br /> private int cou;<br /> <br /> public void setCount(String count) {<br />  this.count = count;<br />  cou=Integer.parseInt(count);<br /> }<br /> <br /> public int doAfterBody() throws JspException {<br />  try {<br />   this.pageContext.getOut().write("&lt;br&gt;");<br />  } catch (IOException e) {<br />   // TODO Auto-generated catch block<br />   e.printStackTrace();<br />  }<br />  if(cou&gt;1){<br />   cou--;<br />   return MyIteratorTag.EVAL_BODY_AGAIN;<br />  }else{<br />   return MyIteratorTag.SKIP_BODY;<br />  }<br /> }<br /> public int doStartTag() throws JspException {<br />  try {<br />   this.pageContext.getOut().write("&lt;font color='"+this.strColor+"'&gt;");<br />  } catch (IOException e) {<br />   // TODO Auto-generated catch block<br />   e.printStackTrace();<br />  }<br />  return MyIteratorTag.EVAL_BODY_INCLUDE;<br /> }</font>
		</p>
		<p>
				<font face="Courier New" size="2"> public int doEndTag() throws JspException {<br />  try {<br />   this.pageContext.getOut().write("&lt;/font&gt;");<br />  } catch (IOException e) {<br />   // TODO Auto-generated catch block<br />   e.printStackTrace();<br />  }<br />  return MyIteratorTag.EVAL_PAGE;<br /> }</font>
				<font face="Courier New" size="2">
				</font>
		</p>
		<p> public int getCou() {<br />  return cou;<br /> }</p>
		<p> public void setCou(int cou) {<br />  this.cou = cou;<br /> }</p>
		<p> public String getCount() {<br />  return count;<br /> }</p>
		<p> public String getStrColor() {<br />  return strColor;<br /> }</p>
		<p> public void setStrColor(String strColor) {<br />  this.strColor = strColor;<br /> }</p>
		<p>}<br />＝＝＝＝＝＝＝＝＝＝＝＝＝＝</p>
		<p>。。。</p>
		<p>&lt;tag&gt;<br />    &lt;description&gt;对颜色的设置&lt;/description&gt;<br />    &lt;name&gt;myColor&lt;/name&gt;<br />    &lt;tag-class&gt;com.xmddl.tag.demo.MyIteratorSupport&lt;/tag-class&gt;<br />    &lt;body-content&gt;JSP&lt;/body-content&gt;<br />    &lt;attribute&gt;<br />        &lt;name&gt;count&lt;/name&gt;<br />        &lt;required&gt;true&lt;/required&gt;<br />    &lt;/attribute&gt;<br />    &lt;attribute&gt;<br />        &lt;name&gt;strColor&lt;/name&gt;<br />        &lt;required&gt;false&lt;/required&gt;<br />    &lt;/attribute&gt;<br />   &lt;/tag&gt;</p>
		<p>。。。</p>
		<p>＝＝＝＝＝＝＝＝＝＝＝＝＝＝</p>
		<p>&lt;%@ page language="java" pageEncoding="gb2312" import="java.util.*"%&gt;<br />&lt;%@ taglib prefix="lu" uri="<a href="http://www.xmddl.com/tag">http://www.xmddl.com/tag</a>" %&gt;<br />&lt;html&gt;<br />  &lt;head&gt;<br />  &lt;/head&gt;<br />  &lt;body&gt;</p>
		<p>    &lt;h1&gt;This is a test tag page for person.&lt;/h1&gt; &lt;br&gt;<br />    &lt;lu:myColor count="3" strColor="blue"&gt;xmddl&lt;/lu:myColor&gt;<br />  &lt;/body&gt;<br />&lt;/html&gt;</p>
		<p>输出如下</p>
		<h1>
				<font size="3">This is a test tag page for person.</font>
		</h1>
		<p>
				<br />
				<font color="blue">xmddl<br />xmddl<br />xmddl</font>
				<br />
		</p>
<img src ="http://www.blogjava.net/joeyeezhang/aggbug/36577.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/joeyeezhang/" target="_blank">joeyeezhang</a> 2006-03-21 10:58 <a href="http://www.blogjava.net/joeyeezhang/archive/2006/03/21/36577.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>细说Java之util类</title><link>http://www.blogjava.net/joeyeezhang/archive/2006/02/13/30415.html</link><dc:creator>joeyeezhang</dc:creator><author>joeyeezhang</author><pubDate>Mon, 13 Feb 2006 02:36:00 GMT</pubDate><guid>http://www.blogjava.net/joeyeezhang/archive/2006/02/13/30415.html</guid><wfw:comment>http://www.blogjava.net/joeyeezhang/comments/30415.html</wfw:comment><comments>http://www.blogjava.net/joeyeezhang/archive/2006/02/13/30415.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/joeyeezhang/comments/commentRss/30415.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/joeyeezhang/services/trackbacks/30415.html</trackback:ping><description><![CDATA[线性表，链表，哈希表是常用的数据结构，在进行Java开发时，JDK已经为我们提供了一系列相应的类来实现基本的数据结构。这些类均在java.util包中。本文试图通过简单的描述，向读者阐述各个类的作用以及如何正确使用这些类。&nbsp;&nbsp;<BR><BR>Collection<BR>├List<BR>│├LinkedList<BR>│├ArrayList<BR>│└Vector<BR>│　└Stack<BR>└Set<BR>Map<BR>├Hashtable<BR>├HashMap<BR>└WeakHashMap<BR><BR>Collection接口<BR>　　Collection是最基本的集合接口，一个Collection代表一组Object，即Collection的元素（Elements）。一些Collection允许相同的元素而另一些不行。一些能排序而另一些不行。Java&nbsp;&nbsp;SDK不提供直接继承自Collection的类，Java&nbsp;&nbsp;SDK提供的类都是继承自Collection的“子接口”如List和Set。<BR>　　所有实现Collection接口的类都必须提供两个标准的构造函数：无参数的构造函数用于创建一个空的Collection，有一个Collection参数的构造函数用于创建一个新的Collection，这个新的Collection与传入的Collection有相同的元素。后一个构造函数允许用户复制一个Collection。<BR>　　如何遍历Collection中的每一个元素？不论Collection的实际类型如何，它都支持一个iterator()的方法，该方法返回一个迭代子，使用该迭代子即可逐一访问Collection中每一个元素。典型的用法如下：<BR>　　　　Iterator&nbsp;&nbsp;it&nbsp;&nbsp;=&nbsp;&nbsp;collection.iterator();&nbsp;&nbsp;//&nbsp;&nbsp;获得一个迭代子<BR>　　　　while(it.hasNext())&nbsp;&nbsp;{<BR>　　　　　　Object&nbsp;&nbsp;obj&nbsp;&nbsp;=&nbsp;&nbsp;it.next();&nbsp;&nbsp;//&nbsp;&nbsp;得到下一个元素<BR>　　　　}<BR>　　由Collection接口派生的两个接口是List和Set。<BR><BR>List接口<BR>　　List是有序的Collection，使用此接口能够精确的控制每个元素插入的位置。用户能够使用索引（元素在List中的位置，类似于数组下标）来访问List中的元素，这类似于Java的数组。<BR>和下面要提到的Set不同，List允许有相同的元素。<BR>　　除了具有Collection接口必备的iterator()方法外，List还提供一个listIterator()方法，返回一个ListIterator接口，和标准的Iterator接口相比，ListIterator多了一些add()之类的方法，允许添加，删除，设定元素，还能向前或向后遍历。<BR>　　实现List接口的常用类有LinkedList，ArrayList，Vector和Stack。<BR><BR>LinkedList类<BR>　　LinkedList实现了List接口，允许null元素。此外LinkedList提供额外的get，remove，insert方法在LinkedList的首部或尾部。这些操作使LinkedList可被用作堆栈（stack），队列（queue）或双向队列（deque）。<BR>　　注意LinkedList没有同步方法。如果多个线程同时访问一个List，则必须自己实现访问同步。一种解决方法是在创建List时构造一个同步的List：<BR>　　　　List&nbsp;&nbsp;list&nbsp;&nbsp;=&nbsp;&nbsp;Collections.synchronizedList(new&nbsp;&nbsp;LinkedList(...));<BR><BR>ArrayList类<BR>　　ArrayList实现了可变大小的数组。它允许所有元素，包括null。ArrayList没有同步。<BR>size，isEmpty，get，set方法运行时间为常数。但是add方法开销为分摊的常数，添加n个元素需要O(n)的时间。其他的方法运行时间为线性。<BR>　　每个ArrayList实例都有一个容量（Capacity），即用于存储元素的数组的大小。这个容量可随着不断添加新元素而自动增加，但是增长算法并没有定义。当需要插入大量元素时，在插入前可以调用ensureCapacity方法来增加ArrayList的容量以提高插入效率。<BR>　　和LinkedList一样，ArrayList也是非同步的（unsynchronized）。<BR><BR>Vector类<BR>　　Vector非常类似ArrayList，但是Vector是同步的。由Vector创建的Iterator，虽然和ArrayList创建的Iterator是同一接口，但是，因为Vector是同步的，当一个Iterator被创建而且正在被使用，另一个线程改变了Vector的状态（例如，添加或删除了一些元素），这时调用Iterator的方法时将抛出ConcurrentModificationException，因此必须捕获该异常。<BR><BR>Stack&nbsp;&nbsp;类<BR>　　Stack继承自Vector，实现一个后进先出的堆栈。Stack提供5个额外的方法使得Vector得以被当作堆栈使用。基本的push和pop方法，还有peek方法得到栈顶的元素，empty方法测试堆栈是否为空，search方法检测一个元素在堆栈中的位置。Stack刚创建后是空栈。<BR><BR>Set接口<BR>　　Set是一种不包含重复的元素的Collection，即任意的两个元素e1和e2都有e1.equals(e2)=false，Set最多有一个null元素。<BR>　　很明显，Set的构造函数有一个约束条件，传入的Collection参数不能包含重复的元素。<BR>　　请注意：必须小心操作可变对象（Mutable&nbsp;&nbsp;Object）。如果一个Set中的可变元素改变了自身状态导致Object.equals(Object)=true将导致一些问题。<BR><BR>Map接口<BR>　　请注意，Map没有继承Collection接口，Map提供key到value的映射。一个Map中不能包含相同的key，每个key只能映射一个value。Map接口提供3种集合的视图，Map的内容可以被当作一组key集合，一组value集合，或者一组key-value映射。<BR><BR>Hashtable类<BR>　　Hashtable继承Map接口，实现一个key-value映射的哈希表。任何非空（non-null）的对象都可作为key或者value。<BR>　　添加数据使用put(key,&nbsp;&nbsp;value)，取出数据使用get(key)，这两个基本操作的时间开销为常数。<BR>Hashtable通过initial&nbsp;&nbsp;capacity和load&nbsp;&nbsp;factor两个参数调整性能。通常缺省的load&nbsp;&nbsp;factor&nbsp;&nbsp;0.75较好地实现了时间和空间的均衡。增大load&nbsp;&nbsp;factor可以节省空间但相应的查找时间将增大，这会影响像get和put这样的操作。<BR>使用Hashtable的简单示例如下，将1，2，3放到Hashtable中，他们的key分别是”one”，”two”，”three”：<BR>　　　　Hashtable&nbsp;&nbsp;numbers&nbsp;&nbsp;=&nbsp;&nbsp;new&nbsp;&nbsp;Hashtable();<BR>　　　　numbers.put(“one”,&nbsp;&nbsp;new&nbsp;&nbsp;Integer(1));<BR>　　　　numbers.put(“two”,&nbsp;&nbsp;new&nbsp;&nbsp;Integer(2));<BR>　　　　numbers.put(“three”,&nbsp;&nbsp;new&nbsp;&nbsp;Integer(3));<BR>　　要取出一个数，比如2，用相应的key：<BR>　　　　Integer&nbsp;&nbsp;n&nbsp;&nbsp;=&nbsp;&nbsp;(Integer)numbers.get(“two”);<BR>　　　　System.out.println(“two&nbsp;&nbsp;=&nbsp;&nbsp;”&nbsp;&nbsp;+&nbsp;&nbsp;n);<BR>　　由于作为key的对象将通过计算其散列函数来确定与之对应的value的位置，因此任何作为key的对象都必须实现hashCode和equals方法。hashCode和equals方法继承自根类Object，如果你用自定义的类当作key的话，要相当小心，按照散列函数的定义，如果两个对象相同，即obj1.equals(obj2)=true，则它们的hashCode必须相同，但如果两个对象不同，则它们的hashCode不一定不同，如果两个不同对象的hashCode相同，这种现象称为冲突，冲突会导致操作哈希表的时间开销增大，所以尽量定义好的hashCode()方法，能加快哈希表的操作。<BR>　　如果相同的对象有不同的hashCode，对哈希表的操作会出现意想不到的结果（期待的get方法返回null），要避免这种问题，只需要牢记一条：要同时复写equals方法和hashCode方法，而不要只写其中一个。<BR>　　Hashtable是同步的。<BR><BR>HashMap类<BR>　　HashMap和Hashtable类似，不同之处在于HashMap是非同步的，并且允许null，即null&nbsp;&nbsp;value和null&nbsp;&nbsp;key。，但是将HashMap视为Collection时（values()方法可返回Collection），其迭代子操作时间开销和HashMap的容量成比例。因此，如果迭代操作的性能相当重要的话，不要将HashMap的初始化容量设得过高，或者load&nbsp;&nbsp;factor过低。<BR><BR>WeakHashMap类<BR>　　WeakHashMap是一种改进的HashMap，它对key实行“弱引用”，如果一个key不再被外部所引用，那么该key可以被GC回收。<BR><BR>总结<BR>　　如果涉及到堆栈，队列等操作，应该考虑用List，对于需要快速插入，删除元素，应该使用LinkedList，如果需要快速随机访问元素，应该使用ArrayList。<BR>　　如果程序在单线程环境中，或者访问仅仅在一个线程中进行，考虑非同步的类，其效率较高，如果多个线程可能同时操作一个类，应该使用同步的类。<BR>　　要特别注意对哈希表的操作，作为key的对象要正确复写equals和hashCode方法。<BR>　　尽量返回接口而非实际的类型，如返回List而非ArrayList，这样如果以后需要将ArrayList换成LinkedList时，客户端代码不用改变。这就是针对抽象编程。 <img src ="http://www.blogjava.net/joeyeezhang/aggbug/30415.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/joeyeezhang/" target="_blank">joeyeezhang</a> 2006-02-13 10:36 <a href="http://www.blogjava.net/joeyeezhang/archive/2006/02/13/30415.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Common command  in java</title><link>http://www.blogjava.net/joeyeezhang/archive/2006/02/06/29690.html</link><dc:creator>joeyeezhang</dc:creator><author>joeyeezhang</author><pubDate>Mon, 06 Feb 2006 05:17:00 GMT</pubDate><guid>http://www.blogjava.net/joeyeezhang/archive/2006/02/06/29690.html</guid><wfw:comment>http://www.blogjava.net/joeyeezhang/comments/29690.html</wfw:comment><comments>http://www.blogjava.net/joeyeezhang/archive/2006/02/06/29690.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/joeyeezhang/comments/commentRss/29690.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/joeyeezhang/services/trackbacks/29690.html</trackback:ping><description><![CDATA[<P><FONT size=2>J2EE应用/<BR></FONT><FONT size=2>|__EJB组件/(haiejb.jar)<BR></FONT><FONT size=2><FONT size=2>|&nbsp; |__META-INF/<BR></FONT><FONT size=2>|&nbsp; |&nbsp; |__ejb-jar.xml<BR></FONT><FONT size=2>|&nbsp; |&nbsp; |__jboss.xml<BR></FONT><FONT size=2>|&nbsp; |__ejbs/<BR></FONT><FONT size=2>|&nbsp;&nbsp;&nbsp;&nbsp; |__HaiHome.class<BR></FONT><FONT size=2>|&nbsp;&nbsp;&nbsp;&nbsp; |__HaiClient.class<BR></FONT><FONT size=2>|&nbsp;&nbsp;&nbsp;&nbsp; |__HaiBean.class<BR></FONT>|__WEB应用/(haiejb.war)<BR><FONT size=2>|&nbsp; |__haiejb.jsp<BR></FONT><FONT size=2>|&nbsp; |__WEB-INF/<BR></FONT><FONT size=2>|&nbsp;&nbsp;&nbsp;&nbsp; |__web.xml<BR></FONT><FONT size=2>|&nbsp;&nbsp;&nbsp;&nbsp; |__jboss-web.xml<BR></FONT><FONT size=2>|__META-INF/</FONT>&nbsp;<BR><FONT size=2>&nbsp;&nbsp; |__application.xml<BR>javac -classpath %classpath% -d [outpath] *.java<BR>jar cvf haiejb.jar META-INF/ ejbs/<BR>jar cvf haiejb.war haiejb.jsp WEB-INF/<BR>jar cvf haiejb.ear haiejb.jar haiejb.war META-INF/</FONT></FONT></P><img src ="http://www.blogjava.net/joeyeezhang/aggbug/29690.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/joeyeezhang/" target="_blank">joeyeezhang</a> 2006-02-06 13:17 <a href="http://www.blogjava.net/joeyeezhang/archive/2006/02/06/29690.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>The sample of calendar </title><link>http://www.blogjava.net/joeyeezhang/archive/2006/01/25/29195.html</link><dc:creator>joeyeezhang</dc:creator><author>joeyeezhang</author><pubDate>Wed, 25 Jan 2006 06:09:00 GMT</pubDate><guid>http://www.blogjava.net/joeyeezhang/archive/2006/01/25/29195.html</guid><wfw:comment>http://www.blogjava.net/joeyeezhang/comments/29195.html</wfw:comment><comments>http://www.blogjava.net/joeyeezhang/archive/2006/01/25/29195.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/joeyeezhang/comments/commentRss/29195.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/joeyeezhang/services/trackbacks/29195.html</trackback:ping><description><![CDATA[public List getWorkDailyLogList(String type, Date date,String userId,String queryInfo)<BR>&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; StringBuffer sql = new StringBuffer("select guid, title as 标题,convert(char(10),createddate,20) as 日期");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sql.append(getAppRelationCore().getPersonManagementFacade().getColumnStatement("Hr_dailylog")).append("&nbsp; from Hr_dailyLog where convert(char(10),createddate,20) between ? and ?&nbsp; and creator = ? and title like '%'+?+'%'");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String params[] =&nbsp; new String[4];<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GregorianCalendar calendar = new GregorianCalendar();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; calendar.setTime(date);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(type.equals("week"))<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; calendar.set(GregorianCalendar.DAY_OF_WEEK,1);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; params[0] = PortalUtil.convertDateToString(PortalUtil.SQL_DATE_PATTERN, calendar.getTime());<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; calendar.set(GregorianCalendar.DAY_OF_WEEK,7);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; params[1] = PortalUtil.convertDateToString(PortalUtil.SQL_DATE_PATTERN, calendar.getTime());<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(type.equals("month"))<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; calendar.set(GregorianCalendar.DAY_OF_MONTH,1);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; params[0] = PortalUtil.convertDateToString(PortalUtil.SQL_DATE_PATTERN, calendar.getTime());<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; calendar.set(GregorianCalendar.DAY_OF_MONTH,calendar.getActualMaximum(GregorianCalendar.DAY_OF_MONTH));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; params[1] = PortalUtil.convertDateToString(PortalUtil.SQL_DATE_PATTERN, calendar.getTime());<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(type.equals("year"))<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; calendar.set(GregorianCalendar.DAY_OF_YEAR,1);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; params[0] = PortalUtil.convertDateToString(PortalUtil.SQL_DATE_PATTERN, calendar.getTime());<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; calendar.set(GregorianCalendar.DAY_OF_YEAR,calendar.getActualMaximum(GregorianCalendar.DAY_OF_YEAR));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; params[1] = PortalUtil.convertDateToString(PortalUtil.SQL_DATE_PATTERN, calendar.getTime());<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(type.equals("day"))<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; params[0] = PortalUtil.convertDateToString(PortalUtil.SQL_DATE_PATTERN, calendar.getTime());<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; params[1] = PortalUtil.convertDateToString(PortalUtil.SQL_DATE_PATTERN, calendar.getTime());<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; params[2] = userId;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; params[3] = queryInfo;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return this.getCommonDAO().findBySQL(sql.toString(),params).getRows();&nbsp; //To change body of implemented methods use File | Settings | File Templates.<BR>&nbsp;&nbsp;&nbsp; }<img src ="http://www.blogjava.net/joeyeezhang/aggbug/29195.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/joeyeezhang/" target="_blank">joeyeezhang</a> 2006-01-25 14:09 <a href="http://www.blogjava.net/joeyeezhang/archive/2006/01/25/29195.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>