﻿<?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-关注基于J2EE的系统设计与实现,总结项目经验与思考。</title><link>http://www.blogjava.net/NeonWay/</link><description>spring、hibernate、webwork框架实践与探索。</description><language>zh-cn</language><lastBuildDate>Fri, 01 May 2026 07:49:22 GMT</lastBuildDate><pubDate>Fri, 01 May 2026 07:49:22 GMT</pubDate><ttl>60</ttl><item><title>gb2312 gbk unicode utf-8等编码解析</title><link>http://www.blogjava.net/NeonWay/archive/2007/01/29/96437.html</link><dc:creator>王彦锋的技术实践</dc:creator><author>王彦锋的技术实践</author><pubDate>Mon, 29 Jan 2007 01:59:00 GMT</pubDate><guid>http://www.blogjava.net/NeonWay/archive/2007/01/29/96437.html</guid><wfw:comment>http://www.blogjava.net/NeonWay/comments/96437.html</wfw:comment><comments>http://www.blogjava.net/NeonWay/archive/2007/01/29/96437.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/NeonWay/comments/commentRss/96437.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/NeonWay/services/trackbacks/96437.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;&nbsp;<a href='http://www.blogjava.net/NeonWay/archive/2007/01/29/96437.html'>阅读全文</a><img src ="http://www.blogjava.net/NeonWay/aggbug/96437.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/NeonWay/" target="_blank">王彦锋的技术实践</a> 2007-01-29 09:59 <a href="http://www.blogjava.net/NeonWay/archive/2007/01/29/96437.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在java中严格校验日期正确性</title><link>http://www.blogjava.net/NeonWay/archive/2007/01/25/95855.html</link><dc:creator>王彦锋的技术实践</dc:creator><author>王彦锋的技术实践</author><pubDate>Thu, 25 Jan 2007 02:09:00 GMT</pubDate><guid>http://www.blogjava.net/NeonWay/archive/2007/01/25/95855.html</guid><wfw:comment>http://www.blogjava.net/NeonWay/comments/95855.html</wfw:comment><comments>http://www.blogjava.net/NeonWay/archive/2007/01/25/95855.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/NeonWay/comments/commentRss/95855.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/NeonWay/services/trackbacks/95855.html</trackback:ping><description><![CDATA[
		<p>   /**<br />     * 检验输入是否为正确的日期格式(不含秒的任何情况),严格要求日期正确性,格式:yyyy-MM-dd HH:mm<br />     * @param sourceDate<br />     * @return<br />     */<br />    public static boolean checkDate(String sourceDate){<br />        if(sourceDate==null){<br />            return false;<br />        }<br />        try {<br />               SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");<br />               dateFormat.setLenient(false);<br />               dateFormat.parse(sourceDate);<br />               return true;<br />        } catch (Exception e) {<br />        }<br />         return false;<br />    }<br />    <br />    /**<br />     * 根据日期获得日期(不含秒的任何情况),严格要求日期正确性,格式:yyyy-MM-dd HH:mm<br />     * @param sourceDate<br />     * @return<br />     */<br />    public static Date getDate(String sourceDate) throws DataFormatException{<br />           if(sourceDate==null){<br />               throw new DataFormatException("源数据为null");<br />           }<br />           try {<br />                     SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");<br />                     dateFormat.setLenient(false);<br />                      return dateFormat.parse(sourceDate);<br />           } catch (Exception e) {<br />                     throw new DataFormatException("源数据格式错误");<br />           }<br />    }<br /><br /><br />//日期格式可以自定义.<br />    public static void main(String[] args) throws IOException {<br />             String s="2006-01-30 12:26";<br />             System.out.println(s+"是否为日期:"+Test.checkDate(s));<br /><br />             s="2006-1-30 12:26";<br />             System.out.println(s+"是否为日期:"+Test.checkDate(s));<br />        <br />            s="2006-01-32 12:26";<br />            System.out.println(s+"是否为日期:"+Test.checkDate(s));<br />    }</p>
<img src ="http://www.blogjava.net/NeonWay/aggbug/95855.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/NeonWay/" target="_blank">王彦锋的技术实践</a> 2007-01-25 10:09 <a href="http://www.blogjava.net/NeonWay/archive/2007/01/25/95855.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java里抽象类和接口的区别 </title><link>http://www.blogjava.net/NeonWay/archive/2007/01/22/95243.html</link><dc:creator>王彦锋的技术实践</dc:creator><author>王彦锋的技术实践</author><pubDate>Mon, 22 Jan 2007 03:22:00 GMT</pubDate><guid>http://www.blogjava.net/NeonWay/archive/2007/01/22/95243.html</guid><wfw:comment>http://www.blogjava.net/NeonWay/comments/95243.html</wfw:comment><comments>http://www.blogjava.net/NeonWay/archive/2007/01/22/95243.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/NeonWay/comments/commentRss/95243.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/NeonWay/services/trackbacks/95243.html</trackback:ping><description><![CDATA[摘自:http://blog.csdn.net/shooray/archive/2006/10/31/1357935.aspx<br /><br /><font face="Verdana">abstract class和interface是Java语言中对于抽象类定义进行支持的两种机制，正是由于这两种机制的存在，才赋予了Java强大的面向对象能力。abstract class和interface之间在对于抽象类定义的支持方面具有很大的相似性，甚至可以相互替换，因此很多开发者在进行抽象类定义时对于abstract class和interface的选择显得比较随意。其实，两者之间还是有很大的区别的，对于它们的选择甚至反映出对于问题领域本质的理解、对于设计意图的理解是否正确、合理。本文将对它们之间的区别进行一番剖析，试图给开发者提供一个在二者之间进行选择的依据。  <br /><br />理解抽象类  <br /><br />abstract class和interface在Java语言中都是用来进行抽象类（本文中的抽象类并非从abstract class翻译而来，它表示的是一个抽象体，而abstract class为Java语言中用于定义抽象类的一种方法，请读者注意区分）定义的，那么什么是抽象类，使用抽象类能为我们带来什么好处呢？  <br /><br />在面向对象的概念中，我们知道所有的对象都是通过类来描绘的，但是反过来却不是这样。并不是所有的类都是用来描绘对象的，如果一个类中没有包含足够的信息来描绘一个具体的对象，这样的类就是抽象类。抽象类往往用来表征我们在对问题领域进行分析、设计中得出的抽象概念，是对一系列看上去不同，但是本质上相同的具体概念的抽象。比如：如果我们进行一个图形编辑软件的开发，就会发现问题领域存在着圆、三角形这样一些具体概念，它们是不同的，但是它们又都属于形状这样一个概念，形状这个概念在问题领域是不存在的，它就是一个抽象概念。正是因为抽象的概念在问题领域没有对应的具体概念，所以用以表征抽象概念的抽象类是不能够实例化的。  <br /><br />在面向对象领域，抽象类主要用来进行类型隐藏。我们可以构造出一个固定的一组行为的抽象描述，但是这组行为却能够有任意个可能的具体实现方式。这个抽象描述就是抽象类，而这一组任意个可能的具体实现则表现为所有可能的派生类。模块可以操作一个抽象体。由于模块依赖于一个固定的抽象体，因此它可以是不允许修改的；同时，通过从这个抽象体派生，也可扩展此模块的行为功能。熟悉OCP的读者一定知道，为了能够实现面向对象设计的一个最核心的原则OCP(Open-Closed Principle)，抽象类是其中的关键所在。  <br /><br /><br />从语法定义层面看abstract class和interface  <br /><br />在语法层面，Java语言对于abstract class和interface给出了不同的定义方式，下面以定义一个名为Demo的抽象类为例来说明这种不同。  <br /><br />使用abstract class的方式定义Demo抽象类的方式如下：  <br /><br />abstract class Demo ｛  <br /> abstract void method1();  <br /> abstract void method2();  <br /> …  <br />｝  <br /><br />使用interface的方式定义Demo抽象类的方式如下：  <br /><br />interface Demo {  <br /> void method1();  <br /> void method2();  <br /> …  <br />}  <br /><br />在abstract class方式中，Demo可以有自己的数据成员，也可以有非abstarct的成员方法，而在interface方式的实现中，Demo只能够有静态的不能被修改的数据成员（也就是必须是static final的，不过在interface中一般不定义数据成员），所有的成员方法都是abstract的。从某种意义上说，interface是一种特殊形式的abstract class。  <br /><br />      从编程的角度来看，abstract class和interface都可以用来实现"design by contract"的思想。但是在具体的使用上面还是有一些区别的。  <br /><br />首先，abstract class在Java语言中表示的是一种继承关系，一个类只能使用一次继承关系。但是，一个类却可以实现多个interface。也许，这是Java语言的设计者在考虑Java对于多重继承的支持方面的一种折中考虑吧。  <br /><br />其次，在abstract class的定义中，我们可以赋予方法的默认行为。但是在interface的定义中，方法却不能拥有默认行为，为了绕过这个限制，必须使用委托，但是这会 增加一些复杂性，有时会造成很大的麻烦。  <br /><br />在抽象类中不能定义默认行为还存在另一个比较严重的问题，那就是可能会造成维护上的麻烦。因为如果后来想修改类的界面（一般通过abstract class或者interface来表示）以适应新的情况（比如，添加新的方法或者给已用的方法中添加新的参数）时，就会非常的麻烦，可能要花费很多的时间（对于派生类很多的情况，尤为如此）。但是如果界面是通过abstract class来实现的，那么可能就只需要修改定义在abstract class中的默认行为就可以了。  <br /><br />同样，如果不能在抽象类中定义默认行为，就会导致同样的方法实现出现在该抽象类的每一个派生类中，违反了"one rule，one place"原则，造成代码重复，同样不利于以后的维护。因此，在abstract class和interface间进行选择时要非常的小心。  <br /><br /><br />从设计理念层面看abstract class和interface  <br /><br />上面主要从语法定义和编程的角度论述了abstract class和interface的区别，这些层面的区别是比较低层次的、非本质的。本小节将从另一个层面：abstract class和interface所反映出的设计理念，来分析一下二者的区别。作者认为，从这个层面进行分析才能理解二者概念的本质所在。  <br /><br />前面已经提到过，abstarct class在Java语言中体现了一种继承关系，要想使得继承关系合理，父类和派生类之间必须存在"is a"关系，即父类和派生类在概念本质上应该是相同的（参考文献〔3〕中有关于"is a"关系的大篇幅深入的论述，有兴趣的读者可以参考）。对于interface 来说则不然，并不要求interface的实现者和interface定义在概念本质上是一致的，仅仅是实现了interface定义的契约而已。为了使论述便于理解，下面将通过一个简单的实例进行说明。  <br /><br />考虑这样一个例子，假设在我们的问题领域中有一个关于Door的抽象概念，该Door具有执行两个动作open和close，此时我们可以通过abstract class或者interface来定义一个表示该抽象概念的类型，定义方式分别如下所示：  <br /><br />使用abstract class方式定义Door：  <br /><br />abstract class Door {  <br /> abstract void open();  <br /> abstract void close()；  <br />}  <br /><br />   <br />使用interface方式定义Door：  <br /><br /><br />interface Door {  <br /> void open();  <br /> void close();  <br />}  <br /><br />   <br />其他具体的Door类型可以extends使用abstract class方式定义的Door或者implements使用interface方式定义的Door。看起来好像使用abstract class和interface没有大的区别。  <br /><br />如果现在要求Door还要具有报警的功能。我们该如何设计针对该例子的类结构呢（在本例中，主要是为了展示abstract class和interface反映在设计理念上的区别，其他方面无关的问题都做了简化或者忽略）？下面将罗列出可能的解决方案，并从设计理念层面对这些不同的方案进行分析。  <br /><br />解决方案一：  <br /><br />简单的在Door的定义中增加一个alarm方法，如下：  <br /><br />abstract class Door {  <br /> abstract void open();  <br /> abstract void close()；  <br /> abstract void alarm();  <br />}  <br /><br />   <br />或者  <br /><br />interface Door {  <br /> void open();  <br /> void close();  <br /> void alarm();  <br />}  <br /><br />   <br />那么具有报警功能的AlarmDoor的定义方式如下：  <br /><br />class AlarmDoor extends Door {  <br /> void open() { … }  <br /> void close() { … }  <br /> void alarm() { … }  <br />}  <br /><br />   <br />或者  <br /><br />class AlarmDoor implements Door ｛  <br /> void open() { … }  <br /> void close() { … }  <br /> void alarm() { … }  <br />｝  <br /><br />这种方法违反了面向对象设计中的一个核心原则ISP（Interface Segregation Priciple），在Door的定义中把Door概念本身固有的行为方法和另外一个概念"报警器"的行为方法混在了一起。这样引起的一个问题是那些仅仅依赖于Door这个概念的模块会因为"报警器"这个概念的改变（比如：修改alarm方法的参数）而改变，反之依然。  <br /><br />解决方案二：  <br /><br />既然open、close和alarm属于两个不同的概念，根据ISP原则应该把它们分别定义在代表这两个概念的抽象类中。定义方式有：这两个概念都使用abstract class方式定义；两个概念都使用interface方式定义；一个概念使用abstract class方式定义，另一个概念使用interface方式定义。  <br /><br />显然，由于Java语言不支持多重继承，所以两个概念都使用abstract class方式定义是不可行的。后面两种方式都是可行的，但是对于它们的选择却反映出对于问题领域中的概念本质的理解、对于设计意图的反映是否正确、合理。我们一一来分析、说明。  <br /><br />如果两个概念都使用interface方式来定义，那么就反映出两个问题：1、我们可能没有理解清楚问题领域，AlarmDoor在概念本质上到底是Door还是报警器？2、如果我们对于问题领域的理解没有问题，比如：我们通过对于问题领域的分析发现AlarmDoor在概念本质上和Door是一致的，那么我们在实现时就没有能够正确的揭示我们的设计意图，因为在这两个概念的定义上（均使用interface方式定义）反映不出上述含义。  <br /><br />如果我们对于问题领域的理解是：AlarmDoor在概念本质上是Door，同时它有具有报警的功能。我们该如何来设计、实现来明确的反映出我们的意思呢？前面已经说过，abstract class在Java语言中表示一种继承关系，而继承关系在本质上是"is a"关系。所以对于Door这个概念，我们应该使用abstarct class方式来定义。另外，AlarmDoor又具有报警功能，说明它又能够完成报警概念中定义的行为，所以报警概念可以通过interface方式定义。如下所示：  <br /><br />abstract class Door {  <br /> abstract void open();  <br /> abstract void close()；  <br />}  <br />interface Alarm {  <br /> void alarm();  <br />}  <br />class AlarmDoor extends Door implements Alarm {  <br /> void open() { … }  <br /> void close() { … }  <br />    void alarm() { … }  <br />}  <br /><br />   <br />这种实现方式基本上能够明确的反映出我们对于问题领域的理解，正确的揭示我们的设计意图。其实abstract class表示的是"is a"关系，interface表示的是"like a"关系，大家在选择时可以作为一个依据，当然这是建立在对问题领域的理解上的，比如：如果我们认为AlarmDoor在概念本质上是报警器，同时又具有Door的功能，那么上述的定义方式就要反过来了。  </font> <img src ="http://www.blogjava.net/NeonWay/aggbug/95243.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/NeonWay/" target="_blank">王彦锋的技术实践</a> 2007-01-22 11:22 <a href="http://www.blogjava.net/NeonWay/archive/2007/01/22/95243.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>spring事务探索 </title><link>http://www.blogjava.net/NeonWay/archive/2007/01/15/93872.html</link><dc:creator>王彦锋的技术实践</dc:creator><author>王彦锋的技术实践</author><pubDate>Mon, 15 Jan 2007 02:10:00 GMT</pubDate><guid>http://www.blogjava.net/NeonWay/archive/2007/01/15/93872.html</guid><wfw:comment>http://www.blogjava.net/NeonWay/comments/93872.html</wfw:comment><comments>http://www.blogjava.net/NeonWay/archive/2007/01/15/93872.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/NeonWay/comments/commentRss/93872.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/NeonWay/services/trackbacks/93872.html</trackback:ping><description><![CDATA[引自：<a href="http://www.javaeye.com/topic/11190?page=1">http://www.javaeye.com/topic/11190?page=1<br /></a><br /><p>spring自建事务管理模块。而且这个事务管理是一个抽象设计，可以应用到很多场合，包括普通的DataSource，jta，jms和hibernate上。</p><p>要正确使用spring的事务，首先需要了解spring在事务设计上的一些概念 <br />统观spring事务，围绕着两个核心PlatformTransactionManager和TransactionStatus </p><p>PlatformTransactionManager直译过来就是平台相关事务，这里的平台指的是“事务源”，包括刚才我说的DataSource，jta等等。这些无一不是一个事务源。广义的说，凡是可以完成事务性操作的对象，都可以设计出相对应的PlatformTransactionManager，只要这个事务源支持commit，rollback和getTransaction语意。</p><p>查看spring代码，可以发现这些manager实现事务，就是调用事务源的事务操作方法</p><p>比如</p><p>HibernateTransactionManager <br /></p><div class="code_title">代码</div><div class="code_div"><div class="dp-highlighter"><div class="bar"></div><ol class="dp-j"><li class="alt"><span><span class="keyword">protected</span><span> </span><span class="keyword">void</span><span> doCommit(DefaultTransactionStatus status) {   </span></span></li><li class=""><span>        HibernateTransactionObject txObject = (HibernateTransactionObject) status.getTransaction();   </span></li><li class="alt"><span>        </span><span class="keyword">if</span><span> (status.isDebug()) {   </span></li><li class=""><span>            logger.debug(</span><span class="string">"Committing Hibernate transaction on session ["</span><span> +   </span></li><li class="alt"><span>                    txObject.getSessionHolder().getSession() + </span><span class="string">"]"</span><span>);   </span></li><li class=""><span>        }   </span></li><li class="alt"><span>        </span><span class="keyword">try</span><span> {   </span></li><li class=""><span>            txObject.getSessionHolder().getTransaction().commit();   </span></li><li class="alt"><span>        }   </span></li><li class=""><span>...   </span></li><li class="alt"><span>  </span></li><li class=""><span>    }  </span></li></ol></div></div><script><![CDATA[ender_code();]]&gt;</script><p></p><p>jdbc 的DataSourceTransactionManager <br /></p><div class="code_title">代码</div><div class="code_div"><div class="dp-highlighter"><div class="bar"></div><ol class="dp-j"><li class="alt"><span><span class="keyword">protected</span><span> </span><span class="keyword">void</span><span> doCommit(DefaultTransactionStatus status) {   </span></span></li><li class=""><span>        DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();   </span></li><li class="alt"><span>        Connection con = txObject.getConnectionHolder().getConnection();   </span></li><li class=""><span>        </span><span class="keyword">if</span><span> (status.isDebug()) {   </span></li><li class="alt"><span>            logger.debug(</span><span class="string">"Committing JDBC transaction on connection ["</span><span> + con + </span><span class="string">"]"</span><span>);   </span></li><li class=""><span>        }   </span></li><li class="alt"><span>        </span><span class="keyword">try</span><span> {   </span></li><li class=""><span>            con.commit();   </span></li><li class="alt"><span>        }   </span></li><li class=""><span>        ...   </span></li><li class="alt"><span>    }  </span></li></ol></div></div><script><![CDATA[ender_code();]]&gt;</script><p></p><p>那么PlatformTransactionManager以什么依据处理事务呢？ <br />是TransactionStatus <br />查看api发现这个接口有三个方法 <br />isNewTransaction() ，isRollbackOnly()，setRollbackOnly() <br />PlatformTransactionManager就是根据前两个方法决定是否要创建一个新事务，是要递交还是回滚。至于第三个方法是改变事务当前状态的，很多地方都要用到，偏偏PlatformTransactionManager自身好像不怎么用，毕竟事务状态的改变是由程序员代码决定的，不需要一个manager多管闲事。</p><p>总结上面所说的，spring的事务由PlatformTransactionManager管理，manager最后调用事务源的方法来实现一个事务过程。而manager通过TransactionStatus 来决定如何实现。 <br />接下去说spring事务中的TransactionTemplate和TransactionInterceptor</p><p>TransactionTemplate其实和spring中其他的template的作用类似，起到化简代码的作用，不要被它那么长的名字吓倒了，事实上这个template并不是什么非常核心的对象。如果比较学究派的，可以去看看template设计模式，在此就不再对此赘述了。 <br />为什么要有TransactionTemplate？先来看看如果没有TransactionTemplate，我们的代码该怎么写</p><p>先来看看spring reference中的一段代码 <br /></p><div class="code_title">代码</div><div class="code_div"><div class="dp-highlighter"><div class="bar"></div><ol class="dp-j"><li class="alt"><span><span>DefaultTransactionDefinition def = </span><span class="keyword">new</span><span> DefaultTransactionDefinition()   </span></span></li><li class=""><span>def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);   </span></li><li class="alt"><span>  </span></li><li class=""><span>TransactionStatus status = transactionManager.getTransaction(def);   </span></li><li class="alt"><span>  </span></li><li class=""><span></span><span class="keyword">try</span><span> {   </span></li><li class="alt"><span>    </span><span class="comment">// execute your business logic here </span><span>  </span></li><li class=""><span>} </span><span class="keyword">catch</span><span> (MyException ex) {   </span></li><li class="alt"><span>    transactionManager.rollback(status);   </span></li><li class=""><span>    </span><span class="keyword">throw</span><span> ex;   </span></li><li class="alt"><span>}   </span></li><li class=""><span>transactionManager.commit(status);  </span></li></ol></div></div><script><![CDATA[ender_code();]]&gt;</script><br />这是直接使用transactionManager的例子，可以看到真正执行business logic 的地方是在try当中那段，前后的代码都是为了完成事务管理的。如果每个business logic都要写上那么一段，我肯定是疯了。我们翻出TransactionTemplate的代码看看他怎么化简了我们的代码 
<p></p><p></p><div class="code_title">代码</div><div class="code_div"><div class="dp-highlighter"><div class="bar"></div><ol class="dp-j"><li class="alt"><span><span class="keyword">public</span><span> Object execute(TransactionCallback action) </span><span class="keyword">throws</span><span> TransactionException {   </span></span></li><li class=""><span>        TransactionStatus status = </span><span class="keyword">this</span><span>.transactionManager.getTransaction(</span><span class="keyword">this</span><span>);   </span></li><li class="alt"><span>        Object result = </span><span class="keyword">null</span><span>;   </span></li><li class=""><span>        </span><span class="keyword">try</span><span> {   </span></li><li class="alt"><span>            result = action.doInTransaction(status);   </span></li><li class=""><span>        }   </span></li><li class="alt"><span>        </span><span class="keyword">catch</span><span> (RuntimeException ex) {   </span></li><li class=""><span>            </span><span class="comment">// transactional code threw application exception -&gt; rollback </span><span>  </span></li><li class="alt"><span>            rollbackOnException(status, ex);   </span></li><li class=""><span>            </span><span class="keyword">throw</span><span> ex;   </span></li><li class="alt"><span>        }   </span></li><li class=""><span>        </span><span class="keyword">catch</span><span> (Error err) {   </span></li><li class="alt"><span>            </span><span class="comment">// transactional code threw error -&gt; rollback </span><span>  </span></li><li class=""><span>            rollbackOnException(status, err);   </span></li><li class="alt"><span>            </span><span class="keyword">throw</span><span> err;   </span></li><li class=""><span>        }   </span></li><li class="alt"><span>        </span><span class="keyword">this</span><span>.transactionManager.commit(status);   </span></li><li class=""><span>        </span><span class="keyword">return</span><span> result;   </span></li><li class="alt"><span>    }  </span></li></ol></div></div><script><![CDATA[ender_code();]]&gt;</script><p></p><p>同上面的代码如出一辙，前后是事务处理代码，当中那段result = action.doInTransaction(status);是我们的应用代码。至于action是什么，全看各位的需要了。但是有一点要主要，如果利用TransactionTemplate，那么他不管你扔出什么异常都会回滚事务，但是回滚的是哪个事务呢？继续挖代码 <br /></p><div class="code_title">代码</div><div class="code_div"><div class="dp-highlighter"><div class="bar"></div><ol class="dp-j"><li class="alt"><span><span class="keyword">private</span><span> </span><span class="keyword">void</span><span> rollbackOnException(TransactionStatus status, Throwable ex) </span><span class="keyword">throws</span><span> TransactionException {   </span></span></li><li class=""><span>        </span><span class="keyword">if</span><span> (logger.isDebugEnabled()) {   </span></li><li class="alt"><span>            logger.debug(</span><span class="string">"Initiating transaction rollback on application exception"</span><span>, ex);   </span></li><li class=""><span>        }   </span></li><li class="alt"><span>        </span><span class="keyword">try</span><span> {   </span></li><li class=""><span>            </span><span class="keyword">this</span><span>.transactionManager.rollback(status);   </span></li><li class="alt"><span>        }   </span></li><li class=""><span>        </span><span class="keyword">catch</span><span> (RuntimeException ex2) {   </span></li><li class="alt"><span>            logger.error(</span><span class="string">"Application exception overridden by rollback exception"</span><span>, ex);   </span></li><li class=""><span>            </span><span class="keyword">throw</span><span> ex2;   </span></li><li class="alt"><span>        }   </span></li><li class=""><span>        </span><span class="keyword">catch</span><span> (Error err) {   </span></li><li class="alt"><span>            logger.error(</span><span class="string">"Application exception overridden by rollback error"</span><span>, ex);   </span></li><li class=""><span>            </span><span class="keyword">throw</span><span> err;   </span></li><li class="alt"><span>        }   </span></li><li class=""><span>    }  </span></li></ol></div></div><script><![CDATA[ender_code();]]&gt;</script><br />真相大白，是对template所持有的某个transactionManager进行回滚。所以如果你的应用代码用的是事务源a的一些资源，比如到服务器a的一个datasource，但是你的transactionManager管理的是另一些资源，比如服务器b的一个datasource，代码铁定不会正常运行 
<p></p><p>特别是在一些多事务源的程序里，这点千万不能搞错。如果多个事务源之间要完成全局事务，还是老老实实用分布式事务管理服务吧（jta）</p><p>那么TransactionInterceptor是干什么的？这个是spring 的声明式事务的支持方式。因为用TransactionTemplate要硬编码，而且调整事务策略很麻烦（不是说不能调。举个例子原来程序抛出异常A需要回滚，现在不需要要，我就可以把a catch吃掉。这时候template就不会回滚了。但是每次调整都要重写编码。）而用TransactionInterceptor就可以将这些调整写在配置中。我们再来挖TransactionInterceptor的代码</p><p></p><div class="code_title">代码</div><div class="code_div"><div class="dp-highlighter"><div class="bar"></div><ol class="dp-j"><li class="alt"><span><span class="keyword">public</span><span> Object invoke(MethodInvocation invocation) </span><span class="keyword">throws</span><span> Throwable {   </span></span></li><li class=""><span>        </span><span class="comment">// Work out the target class: may be null. </span><span>  </span></li><li class="alt"><span>        </span><span class="comment">// The TransactionAttributeSource should be passed the target class </span><span>  </span></li><li class=""><span>        </span><span class="comment">// as well as the method, which may be from an interface </span><span>  </span></li><li class="alt"><span>        Class targetClass = (invocation.getThis() != </span><span class="keyword">null</span><span>) ? invocation.getThis().getClass() : </span><span class="keyword">null</span><span>;   </span></li><li class=""><span>           </span></li><li class="alt"><span>        </span><span class="comment">// Create transaction if necessary </span><span>  </span></li><li class=""><span>        TransactionInfo txInfo = createTransactionIfNecessary(invocation.getMethod(), targetClass);   </span></li><li class="alt"><span>  </span></li><li class=""><span>        Object retVal = </span><span class="keyword">null</span><span>;   </span></li><li class="alt"><span>        </span><span class="keyword">try</span><span> {   </span></li><li class=""><span>            </span><span class="comment">// This is an around advice. </span><span>  </span></li><li class="alt"><span>            </span><span class="comment">// Invoke the next interceptor in the chain. </span><span>  </span></li><li class=""><span>            </span><span class="comment">// This will normally result in a target object being invoked. </span><span>  </span></li><li class="alt"><span>            retVal = invocation.proceed();   </span></li><li class=""><span>        }   </span></li><li class="alt"><span>        </span><span class="keyword">catch</span><span> (Throwable ex) {   </span></li><li class=""><span>            </span><span class="comment">// target invocation exception </span><span>  </span></li><li class="alt"><span>            doCloseTransactionAfterThrowing(txInfo, ex);   </span></li><li class=""><span>            </span><span class="keyword">throw</span><span> ex;   </span></li><li class="alt"><span>        }   </span></li><li class=""><span>        </span><span class="keyword">finally</span><span> {   </span></li><li class="alt"><span>            doFinally(txInfo);   </span></li><li class=""><span>        }   </span></li><li class="alt"><span>        doCommitTransactionAfterReturning(txInfo);   </span></li><li class=""><span>  </span></li><li class="alt"><span>        </span><span class="keyword">return</span><span> retVal;   </span></li><li class=""><span>    }  </span></li></ol></div></div><script><![CDATA[ender_code();]]&gt;</script><br />万变不离其宗。 
<p></p><p>所以使用spring的事务管理需要作这些事 <br />1，设置好事务源，比如DataSource，hibernate的session。如果有多个事务源要考虑他们之间是否有全局事务，如果有，老老实实用jta，否则就需要自己写一个manager了 <br />2，设置manager，根据你的事务源选择对应的PlatformTransactionManager <br />3，选择实现事物的方式，用template还是interceptor。用template代码直观点，但是template所管辖的manager和你应用代码所用的事务源要一致。如果用interceptor千万注意，一定要调用interceptor那个bean，而不是原始的那个target。在坛子上我已经看到至少有两个朋友说spring事物不起作用，从配置和代码上看都正确，这时要好好查查，调用的bean是哪一个。 <br />4，这个是设计问题了，推荐事务处于一个较高层次，比如service上的某个函数，而底层的dao可以不考虑事务，否则可能会出现事务嵌套，增加程序复杂度。</p><img src ="http://www.blogjava.net/NeonWay/aggbug/93872.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/NeonWay/" target="_blank">王彦锋的技术实践</a> 2007-01-15 10:10 <a href="http://www.blogjava.net/NeonWay/archive/2007/01/15/93872.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>理解 java中String</title><link>http://www.blogjava.net/NeonWay/archive/2007/01/15/93859.html</link><dc:creator>王彦锋的技术实践</dc:creator><author>王彦锋的技术实践</author><pubDate>Mon, 15 Jan 2007 01:56:00 GMT</pubDate><guid>http://www.blogjava.net/NeonWay/archive/2007/01/15/93859.html</guid><wfw:comment>http://www.blogjava.net/NeonWay/comments/93859.html</wfw:comment><comments>http://www.blogjava.net/NeonWay/archive/2007/01/15/93859.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/NeonWay/comments/commentRss/93859.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/NeonWay/services/trackbacks/93859.html</trackback:ping><description><![CDATA[
		<a href="http://www.javaeye.com/post/199106">http://www.javaeye.com/post/199106</a>
		<br />
		<br /> 要理解 java中String的运作方式，必须明确一点：String是一个非可变类（immutable）。什么是非可变类呢？简单说来，非可变类的实例是不能被修改的，每个实例中包含的信息都必须在该实例创建的时候就提供出来，并且在对象的整个生存周期内固定不变。java为什么要把String设计为非可变类呢？你可以问问 james Gosling ：）。但是非可变类确实有着自身的优势，如状态单一，对象简单，便于维护。其次，该类对象对象本质上是线程安全的，不要求同步。此外用户可以共享非可变对象，甚至可以共享它们的内部信息。（详见 《Effective java》item 13）。String类在java中被大量运用，甚至在class文件中都有其身影，因此将其设计为简单轻便的非可变类是比较合适的。 
<p>一、创建。<br />    好了，知道String是非可变类以后，我们可以进一步了解String的构造方式了。创建一个Stirng对象，主要就有以下两种方式：<br /></p><div class="code_title">java 代码</div><div class="dp-highlighter"><div class="bar"></div><ol class="dp-j"><li class="alt"><span><span>String str1 = </span><span class="keyword">new</span><span> String(</span><span class="string">"abc"</span><span>);    </span></span></li><li class=""><span>Stirng str2 = </span><span class="string">"abc"</span><span>;  </span></li></ol></div><p>     虽然两个语句都是返回一个String对象的引用，但是jvm对两者的处理方式是不一样的。对于第一种，jvm会马上在heap中创建一个String对象，然后将该对象的引用返回给用户。对于第二种，jvm首先会在内部维护的strings pool中通过String的 equels 方法查找是对象池中是否存放有该String对象，如果有，则返回已有的String对象给用户，而不会在heap中重新创建一个新的String对象；如果对象池中没有该String对象，jvm则在heap中创建新的String对象，将其引用返回给用户，同时将该引用添加至strings pool中。注意：使用第一种方法创建对象时，jvm是不会主动把该对象放到strings pool里面的，除非程序调用 String的intern方法。看下面的例子：</p><div class="code_title">java 代码</div><div class="dp-highlighter"><div class="bar"></div><ol class="dp-j"><li class="alt"><span><span>String str1 = </span><span class="keyword">new</span><span> String(</span><span class="string">"abc"</span><span>); </span><span class="comment">//jvm 在堆上创建一个String对象 </span><span>  </span></span></li><li class=""><span>  </span></li><li class="alt"><span> </span><span class="comment">//jvm 在strings pool中找不到值为“abc”的字符串，因此 </span><span>  </span></li><li class=""><span> </span><span class="comment">//在堆上创建一个String对象，并将该对象的引用加入至strings pool中 </span><span>  </span></li><li class="alt"><span> </span><span class="comment">//此时堆上有两个String对象 </span><span>  </span></li><li class=""><span>Stirng str2 = </span><span class="string">"abc"</span><span>;   </span></li><li class="alt"><span>  </span></li><li class=""><span> </span><span class="keyword">if</span><span>(str1 == str2){   </span></li><li class="alt"><span>         System.out.println(</span><span class="string">"str1 == str2"</span><span>);   </span></li><li class=""><span> }</span><span class="keyword">else</span><span>{   </span></li><li class="alt"><span>         System.out.println(</span><span class="string">"str1 != str2"</span><span>);   </span></li><li class=""><span> }   </span></li><li class="alt"><span>  </span><span class="comment">//打印结果是 str1 != str2,因为它们是堆上两个不同的对象 </span><span>  </span></li><li class=""><span>  </span></li><li class="alt"><span>  String str3 = </span><span class="string">"abc"</span><span>;   </span></li><li class=""><span> </span><span class="comment">//此时，jvm发现strings pool中已有“abc”对象了，因为“abc”equels “abc” </span><span>  </span></li><li class="alt"><span> </span><span class="comment">//因此直接返回str2指向的对象给str3，也就是说str2和str3是指向同一个对象的引用 </span><span>  </span></li><li class=""><span>  </span><span class="keyword">if</span><span>(str2 == str3){   </span></li><li class="alt"><span>         System.out.println(</span><span class="string">"str2 == str3"</span><span>);   </span></li><li class=""><span>  }</span><span class="keyword">else</span><span>{   </span></li><li class="alt"><span>         System.out.println(</span><span class="string">"str2 != str3"</span><span>);   </span></li><li class=""><span>  }   </span></li><li class="alt"><span> </span><span class="comment">//打印结果为 str2 == str3</span><span>  </span></li></ol></div><p>   再看下面的例子：<br /></p><div class="code_title">java 代码</div><div class="dp-highlighter"><div class="bar"></div><ol class="dp-j"><li class="alt"><span><span>String str1 = </span><span class="keyword">new</span><span> String(</span><span class="string">"abc"</span><span>); </span><span class="comment">//jvm 在堆上创建一个String对象 </span><span>  </span></span></li><li class=""><span>  </span></li><li class="alt"><span>str1 = str1.intern();   </span></li><li class=""><span></span><span class="comment">//程序显式将str1放到strings pool中，intern运行过程是这样的：首先查看strings pool </span><span>  </span></li><li class="alt"><span></span><span class="comment">//有没“abc”对象的引用，没有，则在堆中新建一个对象，然后将新对象的引用加入至 </span><span>  </span></li><li class=""><span></span><span class="comment">//strings pool中。执行完该语句后，str1原来指向的String对象已经成为垃圾对象了，随时会 </span><span>  </span></li><li class="alt"><span></span><span class="comment">//被GC收集。 </span><span>  </span></li><li class=""><span>  </span></li><li class="alt"><span></span><span class="comment">//此时，jvm发现strings pool中已有“abc”对象了，因为“abc”equels “abc” </span><span>  </span></li><li class=""><span></span><span class="comment">//因此直接返回str1指向的对象给str2，也就是说str2和str1引用着同一个对象， </span><span>  </span></li><li class="alt"><span></span><span class="comment">//此时，堆上的有效对象只有一个。 </span><span>  </span></li><li class=""><span>Stirng str2 = </span><span class="string">"abc"</span><span>;   </span></li><li class="alt"><span>  </span></li><li class=""><span> </span><span class="keyword">if</span><span>(str1 == str2){   </span></li><li class="alt"><span>         System.out.println(</span><span class="string">"str1 == str2"</span><span>);   </span></li><li class=""><span> }</span><span class="keyword">else</span><span>{   </span></li><li class="alt"><span>         System.out.println(</span><span class="string">"str1 != str2"</span><span>);   </span></li><li class=""><span> }   </span></li><li class="alt"><span>  </span><span class="comment">//打印结果是 str1 == str2 </span><span>  </span></li><li class=""><span>  </span></li></ol></div><p><br /></p><p>    为什么jvm可以这样处理String对象呢？就是因为String的非可变性。既然所引用的对象一旦创建就永不更改，那么多个引用共用一个对象时互不影响。</p><p><br />二、串接（Concatenation）。<br />     java程序员应该都知道滥用String的串接操作符是会影响程序的性能的。性能问题从何而来呢？归根结底就是String类的非可变性。既然String对象都是非可变的，也就是对象一旦创建了就不能够改变其内在状态了，但是串接操作明显是要增长字符串的，也就是要改变String的内部状态，两者出现了矛盾。怎么办呢？要维护String的非可变性，只好在串接完成后新建一个String 对象来表示新产生的字符串了。也就是说，每一次执行串接操作都会导致新对象的产生，如果串接操作执行很频繁，就会导致大量对象的创建，性能问题也就随之而来了。<br />    为了解决这个问题，jdk为String类提供了一个可变的配套类，StringBuffer。使用StringBuffer对象，由于该类是可变的，串接时仅仅时改变了内部数据结构，而不会创建新的对象，因此性能上有很大的提高。针对单线程，jdk 5.0还提供了StringBuilder类，在单线程环境下，由于不用考虑同步问题，使用该类使性能得到进一步的提高。</p><p>三、String的长度<br />   我们可以使用串接操作符得到一个长度更长的字符串，那么，String对象最多能容纳多少字符呢？查看String的源代码我们可以得知类String中是使用域 count 来记录对象字符的数量，而count 的类型为 int，因此，我们可以推测最长的长度为 2^32，也就是4G。<br />    不过，我们在编写源代码的时候，如果使用 Sting str = "aaaa";的形式定义一个字符串，那么双引号里面的ASCII字符最多只能有 65534 个。为什么呢？因为在class文件的规范中， CONSTANT_Utf8_info表中使用一个16位的无符号整数来记录字符串的长度的，最多能表示 65536个字节，而java class 文件是使用一种变体UTF-8格式来存放字符的，null值使用两个字节来表示，因此只剩下 65536－ 2 ＝ 65534个字节。也正是变体UTF-8的原因，如果字符串中含有中文等非ASCII字符，那么双引号中字符的数量会更少（一个中文字符占用三个字节）。如果超出这个数量，在编译的时候编译器会报错。</p><img src ="http://www.blogjava.net/NeonWay/aggbug/93859.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/NeonWay/" target="_blank">王彦锋的技术实践</a> 2007-01-15 09:56 <a href="http://www.blogjava.net/NeonWay/archive/2007/01/15/93859.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>解惑 spring 嵌套事务</title><link>http://www.blogjava.net/NeonWay/archive/2006/12/29/90727.html</link><dc:creator>王彦锋的技术实践</dc:creator><author>王彦锋的技术实践</author><pubDate>Fri, 29 Dec 2006 06:06:00 GMT</pubDate><guid>http://www.blogjava.net/NeonWay/archive/2006/12/29/90727.html</guid><wfw:comment>http://www.blogjava.net/NeonWay/comments/90727.html</wfw:comment><comments>http://www.blogjava.net/NeonWay/archive/2006/12/29/90727.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/NeonWay/comments/commentRss/90727.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/NeonWay/services/trackbacks/90727.html</trackback:ping><description><![CDATA[
		<p>
				<a href="http://www.javaeye.com/topic/35907">http://www.javaeye.com/topic/35907</a>
				<br />
				<br />解惑 spring 嵌套事务 </p>
		<p>/** <br />* @author 王政 <br />* @date 2006-11-24 <br />* @note 转载请注明出处 <br />*/ <br /><br />在所有使用 spring 的应用中, 声明式事务管理可能是使用率最高的功能了, 但是, 从我观察到的情况看, <br />绝大多数人并不能深刻理解事务声明中不同事务传播属性配置的的含义, 让我们来看一下 TransactionDefinition 接口中的定义</p>
		<p>
		</p>
		<div class="code_title">代码</div>
		<div class="code_div">
				<div class="dp-highlighter">
						<div class="bar">
						</div>
						<ol class="dp-xml">
								<li class="alt">
										<span>
												<span>/**   </span>
										</span>
								</li>
								<li class="">
										<span>     * Support a current transaction, create a new one if none exists.   </span>
								</li>
								<li class="alt">
										<span>     * Analogous to EJB transaction attribute of the same name.   </span>
								</li>
								<li class="">
										<span>     * </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">p</span>
										<span class="tag">&gt;</span>
										<span>This is typically the default setting of a transaction definition.   </span>
								</li>
								<li class="alt">
										<span>     */   </span>
								</li>
								<li class="">
										<span>    int </span>
										<span class="attribute">PROPAGATION_REQUIRED</span>
										<span> = </span>
										<span class="attribute-value">0</span>
										<span>;   </span>
								</li>
								<li class="alt">
										<span>  </span>
								</li>
								<li class="">
										<span>    /**   </span>
								</li>
								<li class="alt">
										<span>     * Support a current transaction, execute non-transactionally if none exists.   </span>
								</li>
								<li class="">
										<span>     * Analogous to EJB transaction attribute of the same name.   </span>
								</li>
								<li class="alt">
										<span>     * </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">p</span>
										<span class="tag">&gt;</span>
										<span>Note: For transaction managers with transaction synchronization,   </span>
								</li>
								<li class="">
										<span>     * PROPAGATION_SUPPORTS is slightly different from no transaction at all,   </span>
								</li>
								<li class="alt">
										<span>     * as it defines a transaction scopp that synchronization will apply for.   </span>
								</li>
								<li class="">
										<span>     * As a consequence, the same resources (JDBC Connection, Hibernate Session, etc)   </span>
								</li>
								<li class="alt">
										<span>     * will be shared for the entire specified scope. Note that this depends on   </span>
								</li>
								<li class="">
										<span>     * the actual synchronization configuration of the transaction manager.   </span>
								</li>
								<li class="alt">
										<span>     * @see org.springframework.transaction.support.AbstractPlatformTransactionManager#setTransactionSynchronization   </span>
								</li>
								<li class="">
										<span>     */   </span>
								</li>
								<li class="alt">
										<span>    int </span>
										<span class="attribute">PROPAGATION_SUPPORTS</span>
										<span> = </span>
										<span class="attribute-value">1</span>
										<span>;   </span>
								</li>
								<li class="">
										<span>  </span>
								</li>
								<li class="alt">
										<span>    /**   </span>
								</li>
								<li class="">
										<span>     * Support a current transaction, throw an exception if none exists.   </span>
								</li>
								<li class="alt">
										<span>     * Analogous to EJB transaction attribute of the same name.   </span>
								</li>
								<li class="">
										<span>     */   </span>
								</li>
								<li class="alt">
										<span>    int </span>
										<span class="attribute">PROPAGATION_MANDATORY</span>
										<span> = </span>
										<span class="attribute-value">2</span>
										<span>;   </span>
								</li>
								<li class="">
										<span>  </span>
								</li>
								<li class="alt">
										<span>    /**   </span>
								</li>
								<li class="">
										<span>     * Create a new transaction, suspend the current transaction if one exists.   </span>
								</li>
								<li class="alt">
										<span>     * Analogous to EJB transaction attribute of the same name.   </span>
								</li>
								<li class="">
										<span>     * </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">p</span>
										<span class="tag">&gt;</span>
										<span>Note: Actual transaction suspension will not work on out-of-the-box   </span>
								</li>
								<li class="alt">
										<span>     * on all transaction managers. This in particular applies to JtaTransactionManager,   </span>
								</li>
								<li class="">
										<span>     * which requires the </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">code</span>
										<span class="tag">&gt;</span>
										<span>javax.transaction.TransactionManager</span>
										<span class="tag">&lt;/</span>
										<span class="tag-name">code</span>
										<span class="tag">&gt;</span>
										<span> to be   </span>
								</li>
								<li class="alt">
										<span>     * made available it to it (which is server-specific in standard J2EE).   </span>
								</li>
								<li class="">
										<span>     * @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager   </span>
								</li>
								<li class="alt">
										<span>     */   </span>
								</li>
								<li class="">
										<span>    int </span>
										<span class="attribute">PROPAGATION_REQUIRES_NEW</span>
										<span> = </span>
										<span class="attribute-value">3</span>
										<span>;   </span>
								</li>
								<li class="alt">
										<span>  </span>
								</li>
								<li class="">
										<span>    /**   </span>
								</li>
								<li class="alt">
										<span>     * Execute non-transactionally, suspend the current transaction if one exists.   </span>
								</li>
								<li class="">
										<span>     * Analogous to EJB transaction attribute of the same name.   </span>
								</li>
								<li class="alt">
										<span>     * </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">p</span>
										<span class="tag">&gt;</span>
										<span>Note: Actual transaction suspension will not work on out-of-the-box   </span>
								</li>
								<li class="">
										<span>     * on all transaction managers. This in particular applies to JtaTransactionManager,   </span>
								</li>
								<li class="alt">
										<span>     * which requires the </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">code</span>
										<span class="tag">&gt;</span>
										<span>javax.transaction.TransactionManager</span>
										<span class="tag">&lt;/</span>
										<span class="tag-name">code</span>
										<span class="tag">&gt;</span>
										<span> to be   </span>
								</li>
								<li class="">
										<span>     * made available it to it (which is server-specific in standard J2EE).   </span>
								</li>
								<li class="alt">
										<span>     * @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager   </span>
								</li>
								<li class="">
										<span>     */   </span>
								</li>
								<li class="alt">
										<span>    int </span>
										<span class="attribute">PROPAGATION_NOT_SUPPORTED</span>
										<span> = </span>
										<span class="attribute-value">4</span>
										<span>;   </span>
								</li>
								<li class="">
										<span>  </span>
								</li>
								<li class="alt">
										<span>    /**   </span>
								</li>
								<li class="">
										<span>     * Execute non-transactionally, throw an exception if a transaction exists.   </span>
								</li>
								<li class="alt">
										<span>     * Analogous to EJB transaction attribute of the same name.   </span>
								</li>
								<li class="">
										<span>     */   </span>
								</li>
								<li class="alt">
										<span>    int </span>
										<span class="attribute">PROPAGATION_NEVER</span>
										<span> = </span>
										<span class="attribute-value">5</span>
										<span>;   </span>
								</li>
								<li class="">
										<span>  </span>
								</li>
								<li class="alt">
										<span>    /**   </span>
								</li>
								<li class="">
										<span>     * Execute within a nested transaction if a current transaction exists,   </span>
								</li>
								<li class="alt">
										<span>     * behave like PROPAGATION_REQUIRED else. There is no analogous feature in EJB.   </span>
								</li>
								<li class="">
										<span>     * </span>
										<span class="tag">&lt;</span>
										<span class="tag-name">p</span>
										<span class="tag">&gt;</span>
										<span>Note: Actual creation of a nested transaction will only work on specific   </span>
								</li>
								<li class="alt">
										<span>     * transaction managers. Out of the box, this only applies to the JDBC   </span>
								</li>
								<li class="">
										<span>     * DataSourceTransactionManager when working on a JDBC 3.0 driver.   </span>
								</li>
								<li class="alt">
										<span>     * Some JTA providers might support nested transactions as well.   </span>
								</li>
								<li class="">
										<span>     * @see org.springframework.jdbc.datasource.DataSourceTransactionManager   </span>
								</li>
								<li class="alt">
										<span>     */   </span>
								</li>
								<li class="">
										<span>    int </span>
										<span class="attribute">PROPAGATION_NESTED</span>
										<span> = </span>
										<span class="attribute-value">6</span>
										<span>;   </span>
								</li>
						</ol>
				</div>
		</div>
		<script><![CDATA[ender_code();]]&gt;</script>
		<p>
		</p>
		<p>我们可以看到, 在 spring 中一共定义了六种事务传播属性, 如果你觉得看起来不够直观, 那么我来转贴一个满大街都有的翻译</p>
		<p>
		</p>
		<div class="quote_title">引用</div>
		<div class="quote_div">
				<br />PROPAGATION_REQUIRED -- 支持当前事务，如果当前没有事务，就新建一个事务。这是最常见的选择。 <br />PROPAGATION_SUPPORTS -- 支持当前事务，如果当前没有事务，就以非事务方式执行。 <br />PROPAGATION_MANDATORY -- 支持当前事务，如果当前没有事务，就抛出异常。 <br />PROPAGATION_REQUIRES_NEW -- 新建事务，如果当前存在事务，把当前事务挂起。 <br />PROPAGATION_NOT_SUPPORTED -- 以非事务方式执行操作，如果当前存在事务，就把当前事务挂起。 <br />PROPAGATION_NEVER -- 以非事务方式执行，如果当前存在事务，则抛出异常。 <br />PROPAGATION_NESTED -- 如果当前存在事务，则在嵌套事务内执行。如果当前没有事务，则进行与PROPAGATION_REQUIRED类似的操作。 <br />前六个策略类似于EJB CMT，第七个（PROPAGATION_NESTED）是Spring所提供的一个特殊变量。 <br />它要求事务管理器或者使用JDBC 3.0 Savepoint API提供嵌套事务行为（如Spring的DataSourceTransactionManager） <br /></div>
		<p>
		</p>
		<p>在我所见过的误解中, 最常见的是下面这种:</p>
		<p>
		</p>
		<div class="quote_title">引用</div>
		<div class="quote_div">
				<br />假如有两个业务接口 ServiceA 和 ServiceB, 其中 ServiceA 中有一个方法实现如下 
<p></p><p>/** <br />* 事务属性配置为 PROPAGATION_REQUIRED <br />*/ <br />void methodA() { <br />// 调用 ServiceB 的方法 <br />ServiceB.methodB(); <br />}</p><p>那么如果 ServiceB 的 methodB 如果配置了事务, 就必须配置为 PROPAGATION_NESTED <br /></p></div>
		<p>
		</p>
		<p>这种想法可能害了不少人, 认为 Service 之间应该避免互相调用, 其实根本不用担心这点，PROPAGATION_REQUIRED 已经说得很明白, <br />如果当前线程中已经存在事务, 方法调用会加入此事务, 果当前没有事务，就新建一个事务, 所以 ServiceB#methodB() 的事务只要遵循最普通的规则配置为 PROPAGATION_REQUIRED 即可, 如果 ServiceB#methodB (我们称之为内部事务, 为下文打下基础) 抛了异常, 那么 ServiceA#methodA(我们称之为外部事务) 如果没有特殊配置此异常时事务提交 (即 +MyCheckedException的用法), 那么整个事务是一定要 rollback 的, 什么 Service 只能调 Dao 之类的言论纯属无稽之谈, spring 只负责配置了事务属性方法的拦截, 它怎么知道你这个方法是在 Service 还是 Dao 里 ?</p>
		<p>说了这么半天, 那到底什么是真正的事务嵌套呢, 解释之前我们来看一下 Juergen Hoeller 的原话</p>
		<p>
		</p>
		<div class="quote_title">Juergen Hoeller 写道</div>
		<div class="quote_div">
				<br />PROPAGATION_REQUIRES_NEW starts a new, independent "inner" transaction for the given scope. This transaction will be committed or rolled back completely independent from the outer transaction, having its own isolation scope, its own set of locks, etc. The outer transaction will get suspended at the beginning of the inner one, and resumed once the inner one has completed. 
<p></p><p>Such independent inner transactions are for example used for id generation through manual sequences, where the access to the sequence table should happen in its own transactions, to keep the lock there as short as possible. The goal there is to avoid tying the sequence locks to the (potentially much longer running) outer transaction, with the sequence lock not getting released before completion of the outer transaction.</p><p>PROPAGATION_NESTED on the other hand starts a "nested" transaction, which is a true subtransaction of the existing one. What will happen is that a savepoint will be taken at the start of the nested transaction. íf the nested transaction fails, we will roll back to that savepoint. The nested transaction is part of of the outer transaction, so it will only be committed at the end of of the outer transaction.</p><p>Nested transactions essentially allow to try some execution subpaths as subtransactions: rolling back to the state at the beginning of the failed subpath, continuing with another subpath or with the main execution path there - all within one isolated transaction, and not losing any previous work done within the outer transaction.</p><p>For example, consider parsing a very large input file consisting of account transfer blocks: The entire file should essentially be parsed within one transaction, with one single commit at the end. But if a block fails, its transfers need to be rolled back, writing a failure marker somewhere. You could either start over the entire transaction every time a block fails, remembering which blocks to skip - or you mark each block as a nested transaction, only rolling back that specific set of operations, keeping the previous work of the outer transaction. The latter is of course much more efficient, in particular when a block at the end of the file fails. <br /></p></div>
		<p>
		</p>
		<p>
		</p>
		<div class="quote_title">Juergen Hoeller 写道</div>
		<div class="quote_div">
				<br />Rolling back the entire transaction is the choice of the demarcation code/config that started the outer transaction. 
<p></p><p>So if an inner transaction throws an exception and is supposed to be rolled back (according to the rollback rules), the transaction will get rolled back to the savepoint taken at the start of the inner transaction. The immediate calling code can then decide to catch the exception and proceed down some other path within the outer transaction.</p><p>If the code that called the inner transaction lets the exception propagate up the call chain, the exception will eventually reach the demarcation code of the outer transaction. At that point, the rollback rules of the outer transaction decide whether to trigger a rollback. That would be a rollback of the entire outer transaction then.</p><p>So essentially, it depends on your exception handling. If you catch the exception thrown by the inner transaction, you can proceed down some other path within the outer transaction. If you let the exception propagate up the call chain, it's eventually gonna cause a rollback of the entire outer transaction. <br /></p></div>
		<p>
		</p>
		<p>也就是说, 最容易弄混淆的其实是 PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED, 那么这两种方式又有何区别呢? 我简单的翻译一下 Juergen Hoeller 的话 : <br /><br /><span style="COLOR: red">PROPAGATION_REQUIRES_NEW 启动一个新的, 不依赖于环境的 "内部" 事务. 这个事务将被完全 commited 或 rolled back 而不依赖于外部事务, 它拥有自己的隔离范围, 自己的锁, 等等. 当内部事务开始执行时, 外部事务将被挂起, 内务事务结束时, 外部事务将继续执行.</span><br /></p>
		<p>
				<span style="COLOR: red">另一方面, PROPAGATION_NESTED 开始一个 "嵌套的" 事务, 它是已经存在事务的一个真正的子事务. 潜套事务开始执行时, 它将取得一个 savepoint. 如果这个嵌套事务失败, 我们将回滚到此 savepoint. 潜套事务是外部事务的一部分, 只有外部事务结束后它才会被提交.</span>
		</p>
		<p>
				<span style="COLOR: red">由此可见, PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED 的最大区别在于, PROPAGATION_REQUIRES_NEW 完全是一个新的事务, 而 PROPAGATION_NESTED 则是外部事务的子事务, 如果外部事务 commit, 潜套事务也会被 commit, 这个规则同样适用于 roll back.</span>
				<br />
				<br />
				<br />那么外部事务如何利用嵌套事务的 savepoint 特性呢, 我们用代码来说话 <br /><br /></p>
		<div class="code_title">代码</div>
		<div class="code_div">
				<div class="dp-highlighter">
						<div class="bar">
						</div>
						<ol class="dp-j">
								<li class="alt">
										<span>
												<span>ServiceA {   </span>
										</span>
								</li>
								<li class="">
										<span>       </span>
								</li>
								<li class="alt">
										<span>    </span>
										<span class="comment">/** </span> </li>
								<li class="">
										<span>
												<span class="comment">     * 事务属性配置为 PROPAGATION_REQUIRED </span> </span>
								</li>
								<li class="alt">
										<span>
												<span class="comment">     */</span>
												<span>  </span>
										</span>
								</li>
								<li class="">
										<span>    </span>
										<span class="keyword">void</span>
										<span> methodA() {   </span>
								</li>
								<li class="alt">
										<span>        ServiceB.methodB();   </span>
								</li>
								<li class="">
										<span>    }   </span>
								</li>
								<li class="alt">
										<span>  </span>
								</li>
								<li class="">
										<span>}   </span>
								</li>
								<li class="alt">
										<span>  </span>
								</li>
								<li class="">
										<span>ServiceB {   </span>
								</li>
								<li class="alt">
										<span>       </span>
								</li>
								<li class="">
										<span>    </span>
										<span class="comment">/** </span> </li>
								<li class="alt">
										<span>
												<span class="comment">     * 事务属性配置为 PROPAGATION_REQUIRES_NEW </span> </span>
								</li>
								<li class="">
										<span>
												<span class="comment">     */</span>
												<span>    </span>
										</span>
								</li>
								<li class="alt">
										<span>    </span>
										<span class="keyword">void</span>
										<span> methodB() {   </span>
								</li>
								<li class="">
										<span>    }   </span>
								</li>
								<li class="alt">
										<span>       </span>
								</li>
								<li class="">
										<span>}      </span>
								</li>
						</ol>
				</div>
		</div>
		<script><![CDATA[ender_code();]]&gt;</script>
		<p>
		</p>
		<p>这种情况下, 因为 ServiceB#methodB 的事务属性为 PROPAGATION_REQUIRES_NEW, 所以两者不会发生任何关系, ServiceA#methodA 和 ServiceB#methodB 不会因为对方的执行情况而影响事务的结果, 因为它们根本就是两个事务, 在 ServiceB#methodB 执行时 ServiceA#methodA 的事务已经挂起了 (关于事务挂起的内容已经超出了本文的讨论范围, 有时间我会再写一些挂起的文章) .</p>
		<p>那么 PROPAGATION_NESTED 又是怎么回事呢? 继续看代码</p>
		<p>
		</p>
		<div class="code_title">代码</div>
		<div class="code_div">
				<div class="dp-highlighter">
						<div class="bar">
						</div>
						<ol class="dp-j">
								<li class="alt">
										<span>
												<span>ServiceA {   </span>
										</span>
								</li>
								<li class="">
										<span>       </span>
								</li>
								<li class="alt">
										<span>    </span>
										<span class="comment">/** </span> </li>
								<li class="">
										<span>
												<span class="comment">     * 事务属性配置为 PROPAGATION_REQUIRED </span> </span>
								</li>
								<li class="alt">
										<span>
												<span class="comment">     */</span>
												<span>  </span>
										</span>
								</li>
								<li class="">
										<span>    </span>
										<span class="keyword">void</span>
										<span> methodA() {   </span>
								</li>
								<li class="alt">
										<span>        ServiceB.methodB();   </span>
								</li>
								<li class="">
										<span>    }   </span>
								</li>
								<li class="alt">
										<span>  </span>
								</li>
								<li class="">
										<span>}   </span>
								</li>
								<li class="alt">
										<span>  </span>
								</li>
								<li class="">
										<span>ServiceB {   </span>
								</li>
								<li class="alt">
										<span>       </span>
								</li>
								<li class="">
										<span>    </span>
										<span class="comment">/** </span> </li>
								<li class="alt">
										<span>
												<span class="comment">     * 事务属性配置为 PROPAGATION_NESTED </span> </span>
								</li>
								<li class="">
										<span>
												<span class="comment">     */</span>
												<span>    </span>
										</span>
								</li>
								<li class="alt">
										<span>    </span>
										<span class="keyword">void</span>
										<span> methodB() {   </span>
								</li>
								<li class="">
										<span>    }   </span>
								</li>
								<li class="alt">
										<span>       </span>
								</li>
								<li class="">
										<span>}      </span>
								</li>
						</ol>
				</div>
		</div>
		<script><![CDATA[ender_code();]]&gt;</script>
		<p>
		</p>
		<p>现在的情况就变得比较复杂了, ServiceB#methodB 的事务属性被配置为 PROPAGATION_NESTED, 此时两者之间又将如何协作呢? 从 Juergen Hoeller 的原话中我们可以找到答案, ServiceB#methodB 如果 rollback, 那么内部事务(即 ServiceB#methodB) 将回滚到它执行前的 SavePoint(注意, 这是本文中第一次提到它, 潜套事务中最核心的概念), 而外部事务(即 ServiceA#methodA) 可以有以下两种处理方式:</p>
		<p>1. 改写 ServiceA 如下 <br /></p>
		<div class="code_title">代码</div>
		<div class="code_div">
				<div class="dp-highlighter">
						<div class="bar">
						</div>
						<ol class="dp-j">
								<li class="alt">
										<span>
												<span>ServiceA {   </span>
										</span>
								</li>
								<li class="">
										<span>       </span>
								</li>
								<li class="alt">
										<span>    </span>
										<span class="comment">/** </span> </li>
								<li class="">
										<span>
												<span class="comment">     * 事务属性配置为 PROPAGATION_REQUIRED </span> </span>
								</li>
								<li class="alt">
										<span>
												<span class="comment">     */</span>
												<span>  </span>
										</span>
								</li>
								<li class="">
										<span>    </span>
										<span class="keyword">void</span>
										<span> methodA() {   </span>
								</li>
								<li class="alt">
										<span>        </span>
										<span class="keyword">try</span>
										<span> {   </span>
								</li>
								<li class="">
										<span>            ServiceB.methodB();   </span>
								</li>
								<li class="alt">
										<span>        } </span>
										<span class="keyword">catch</span>
										<span> (SomeException) {   </span>
								</li>
								<li class="">
										<span>            </span>
										<span class="comment">// 执行其他业务, 如 ServiceC.methodC(); </span>
										<span>  </span>
								</li>
								<li class="alt">
										<span>        }   </span>
								</li>
								<li class="">
										<span>    }   </span>
								</li>
								<li class="alt">
										<span>  </span>
								</li>
								<li class="">
										<span>}   </span>
								</li>
								<li class="alt">
										<span>  </span>
								</li>
						</ol>
				</div>
		</div>
		<script><![CDATA[ender_code();]]&gt;</script>
		<p>
		</p>
		<p>这种方式也是潜套事务最有价值的地方, 它起到了分支执行的效果, 如果 ServiceB.methodB 失败, 那么执行 ServiceC.methodC(), 而 ServiceB.methodB 已经回滚到它执行之前的 SavePoint, 所以不会产生脏数据(相当于此方法从未执行过), 这种特性可以用在某些特殊的业务中, 而 PROPAGATION_REQUIRED 和 PROPAGATION_REQUIRES_NEW 都没有办法做到这一点. (题外话 : 看到这种代码, 似乎似曾相识, 想起了 prototype.js 中的 Try 函数 )</p>
		<p>2. 代码不做任何修改, 那么如果内部事务(即 ServiceB#methodB) rollback, 那么首先 ServiceB.methodB 回滚到它执行之前的 SavePoint(在任何情况下都会如此), <br />外部事务(即 ServiceA#methodA) 将根据具体的配置决定自己是 commit 还是 rollback (+MyCheckedException). <br /><br /><br />上面大致讲述了潜套事务的使用场景, 下面我们来看如何在 spring 中使用 PROPAGATION_NESTED, 首先来看 AbstractPlatformTransactionManager</p>
		<p>
		</p>
		<div class="code_title">代码</div>
		<div class="code_div">
				<div class="dp-highlighter">
						<div class="bar">
						</div>
						<ol class="dp-j">
								<li class="alt">
										<span>
												<span class="comment">/** </span> </span>
								</li>
								<li class="">
										<span>
												<span class="comment"> * Create a TransactionStatus for an existing transaction. </span> </span>
								</li>
								<li class="alt">
										<span>
												<span class="comment"> */</span>
												<span>  </span>
										</span>
								</li>
								<li class="">
										<span>
										</span>
										<span class="keyword">private</span>
										<span> TransactionStatus handleExistingTransaction(   </span>
								</li>
								<li class="alt">
										<span>        TransactionDefinition definition, Object transaction, </span>
										<span class="keyword">boolean</span>
										<span> debugEnabled)   </span>
								</li>
								<li class="">
										<span>        </span>
										<span class="keyword">throws</span>
										<span> TransactionException {   </span>
								</li>
								<li class="alt">
										<span>  </span>
								</li>
								<li class="">
										<span>   ... 省略   </span>
								</li>
								<li class="alt">
										<span>  </span>
								</li>
								<li class="">
										<span>    </span>
										<span class="keyword">if</span>
										<span> (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {   </span>
								</li>
								<li class="alt">
										<span>        </span>
										<span class="keyword">if</span>
										<span> (!isNestedTransactionAllowed()) {   </span>
								</li>
								<li class="">
										<span>            </span>
										<span class="keyword">throw</span>
										<span> </span>
										<span class="keyword">new</span>
										<span> NestedTransactionNotSupportedException(   </span>
								</li>
								<li class="alt">
										<span>                    </span>
										<span class="string">"Transaction manager does not allow nested transactions by default - "</span>
										<span> +   </span>
								</li>
								<li class="">
										<span>                    </span>
										<span class="string">"specify 'nestedTransactionAllowed' property with value 'true'"</span>
										<span>);   </span>
								</li>
								<li class="alt">
										<span>        }   </span>
								</li>
								<li class="">
										<span>        </span>
										<span class="keyword">if</span>
										<span> (debugEnabled) {   </span>
								</li>
								<li class="alt">
										<span>            logger.debug(</span>
										<span class="string">"Creating nested transaction with name ["</span>
										<span> + definition.getName() + </span>
										<span class="string">"]"</span>
										<span>);   </span>
								</li>
								<li class="">
										<span>        }   </span>
								</li>
								<li class="alt">
										<span>        </span>
										<span class="keyword">if</span>
										<span> (useSavepointForNestedTransaction()) {   </span>
								</li>
								<li class="">
										<span>            </span>
										<span class="comment">// Create savepoint within existing Spring-managed transaction, </span>
										<span>  </span>
								</li>
								<li class="alt">
										<span>            </span>
										<span class="comment">// through the SavepointManager API implemented by TransactionStatus. </span>
										<span>  </span>
								</li>
								<li class="">
										<span>            </span>
										<span class="comment">// Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization. </span>
										<span>  </span>
								</li>
								<li class="alt">
										<span>            DefaultTransactionStatus status =   </span>
								</li>
								<li class="">
										<span>                    newTransactionStatus(definition, transaction, </span>
										<span class="keyword">false</span>
										<span>, </span>
										<span class="keyword">false</span>
										<span>, debugEnabled, </span>
										<span class="keyword">null</span>
										<span>);   </span>
								</li>
								<li class="alt">
										<span>            status.createAndHoldSavepoint();   </span>
								</li>
								<li class="">
										<span>            </span>
										<span class="keyword">return</span>
										<span> status;   </span>
								</li>
								<li class="alt">
										<span>        }   </span>
								</li>
								<li class="">
										<span>        </span>
										<span class="keyword">else</span>
										<span> {   </span>
								</li>
								<li class="alt">
										<span>            </span>
										<span class="comment">// Nested transaction through nested begin and commit/rollback calls. </span>
										<span>  </span>
								</li>
								<li class="">
										<span>            </span>
										<span class="comment">// Usually only for JTA: Spring synchronization might get activated here </span>
										<span>  </span>
								</li>
								<li class="alt">
										<span>            </span>
										<span class="comment">// in case of a pre-existing JTA transaction. </span>
										<span>  </span>
								</li>
								<li class="">
										<span>            doBegin(transaction, definition);   </span>
								</li>
								<li class="alt">
										<span>            </span>
										<span class="keyword">boolean</span>
										<span> newSynchronization = (</span>
										<span class="keyword">this</span>
										<span>.transactionSynchronization != SYNCHRONIZATION_NEVER);   </span>
								</li>
								<li class="">
										<span>            </span>
										<span class="keyword">return</span>
										<span> newTransactionStatus(definition, transaction, </span>
										<span class="keyword">true</span>
										<span>, newSynchronization, debugEnabled, </span>
										<span class="keyword">null</span>
										<span>);   </span>
								</li>
								<li class="alt">
										<span>        }   </span>
								</li>
								<li class="">
										<span>    }   </span>
								</li>
								<li class="alt">
										<span>}   </span>
								</li>
						</ol>
				</div>
		</div>
		<script><![CDATA[ender_code();]]&gt;</script>
		<br />
		<br />一目了然 
<p></p><p><span style="COLOR: red">1. 我们要设置 transactionManager 的 nestedTransactionAllowed 属性为 true, 注意, 此属性默认为 false!!!</span></p><p>再看 AbstractTransactionStatus#createAndHoldSavepoint() 方法</p><p></p><div class="code_title">代码</div><div class="code_div"><div class="dp-highlighter"><div class="bar"></div><ol class="dp-j"><li class="alt"><span><span class="comment">/** </span> </span></li><li class=""><span><span class="comment"> * Create a savepoint and hold it for the transaction. </span> </span></li><li class="alt"><span><span class="comment"> * @throws org.springframework.transaction.NestedTransactionNotSupportedException </span> </span></li><li class=""><span><span class="comment"> * if the underlying transaction does not support savepoints </span> </span></li><li class="alt"><span><span class="comment"> */</span><span>  </span></span></li><li class=""><span></span><span class="keyword">public</span><span> </span><span class="keyword">void</span><span> createAndHoldSavepoint() </span><span class="keyword">throws</span><span> TransactionException {   </span></li><li class="alt"><span>    setSavepoint(getSavepointManager().createSavepoint());   </span></li><li class=""><span>}   </span></li></ol></div></div><script><![CDATA[ender_code();]]&gt;</script><p></p><p>可以看到 Savepoint 是 SavepointManager.createSavepoint 实现的, 再看 SavepointManager 的层次结构, 发现 <br />其 Template 实现是 JdbcTransactionObjectSupport, 常用的 DatasourceTransactionManager, HibernateTransactionManager <br />中的 TransactonObject 都是它的子类 :</p><p><img src="http://www.javaeye.com/upload/attachment/pic/2754/6dfbf7bb-eb70-485c-82d6-a172e7ad8114-thumb.jpg" border="0" /><br /><br />JdbcTransactionObjectSupport 告诉我们必须要满足两个条件才能 createSavepoint : <br /><br /><span style="COLOR: red">2. java.sql.Savepoint 必须存在, 即 jdk 版本要 1.4+ <br />3. Connection.getMetaData().supportsSavepoints() 必须为 true, 即 jdbc drive 必须支持 JDBC 3.0 </span><br />确保以上条件都满足后, 你就可以尝试使用 PROPAGATION_NESTED 了. (全文完) <br /></p><img src ="http://www.blogjava.net/NeonWay/aggbug/90727.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/NeonWay/" target="_blank">王彦锋的技术实践</a> 2006-12-29 14:06 <a href="http://www.blogjava.net/NeonWay/archive/2006/12/29/90727.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Servlet和Filter的url匹配以及url-pattern详解</title><link>http://www.blogjava.net/NeonWay/archive/2006/12/21/89194.html</link><dc:creator>王彦锋的技术实践</dc:creator><author>王彦锋的技术实践</author><pubDate>Thu, 21 Dec 2006 01:23:00 GMT</pubDate><guid>http://www.blogjava.net/NeonWay/archive/2006/12/21/89194.html</guid><wfw:comment>http://www.blogjava.net/NeonWay/comments/89194.html</wfw:comment><comments>http://www.blogjava.net/NeonWay/archive/2006/12/21/89194.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/NeonWay/comments/commentRss/89194.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/NeonWay/services/trackbacks/89194.html</trackback:ping><description><![CDATA[
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td colspan="2">
										<hr />
								</td>
						</tr>
						<tr>
								<td colspan="2">
										<div class="postbodydiv" id="188730_body">
												<table width="100%">
														<tbody>
																<tr>
																		<td>  
<h1 style="LINE-HEIGHT: 150%; TEXT-ALIGN: center" align="center"><font size="4"><span lang="EN-US" style="FONT-FAMILY: \" verdana\?,\?sans-serif\??="">Servlet</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri">和</span><span lang="EN-US" style="FONT-FAMILY: \" verdana\?,\?sans-serif\??="">Filter</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri">的</span><span lang="EN-US" style="FONT-FAMILY: \" verdana\?,\?sans-serif\??="">url</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri">匹配以及</span><span lang="EN-US" style="FONT-FAMILY: \" verdana\?,\?sans-serif\??="">url-pattern</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri">详解</span><span lang="EN-US" style="FONT-FAMILY: \" verdana\?,\?sans-serif\??=""><?XML:NAMESPACE PREFIX = O /?><o:p></o:p></span></font></h1><p class="MsoNormal" style="LINE-HEIGHT: 150%"><span lang="EN-US" style="FONT-FAMILY: \" verdana\?,\?sans-serif\??=""><span style="mso-tab-count: 1">      </span>Servlet</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri">和</span><span lang="EN-US" style="FONT-FAMILY: \" verdana\?,\?sans-serif\??="">filter</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri">是</span><span lang="EN-US" style="FONT-FAMILY: \" verdana\?,\?sans-serif\??="">J2EE</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri">开发中常用的技术，使用方便，配置简单，老少皆宜。估计大多数朋友都是直接配置用，也没有关心过具体的细节，今天遇到一个问题，上网查了</span><span lang="EN-US" style="FONT-FAMILY: \" verdana\?,\?sans-serif\??="">servlet</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri">的规范才发现，</span><span lang="EN-US" style="FONT-FAMILY: \" verdana\?,\?sans-serif\??="">servlet</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri">和</span><span lang="EN-US" style="FONT-FAMILY: \" verdana\?,\?sans-serif\??="">filter</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri">中的</span><span lang="EN-US" style="FONT-FAMILY: \" verdana\?,\?sans-serif\??="">url-pattern</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri">还是有一些文章在里面的，总结了一些东西，放出来供大家参考，以免遇到问题又要浪费时间。</span><span lang="EN-US" style="FONT-FAMILY: \" verdana\?,\?sans-serif\??=""><o:p></o:p></span></p><h2 style="LINE-HEIGHT: 150%"><span lang="EN-US" style="FONT-FAMILY: \" verdana\?,\?sans-serif\??=""><span style="mso-tab-count: 1">   <font size="3"> 一，</font></span><font size="3">servlet</font></span><font size="3"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Cambria">容器对</span><span lang="EN-US" style="FONT-FAMILY: \" verdana\?,\?sans-serif\??="">url</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Cambria">的匹配过程：</span><span lang="EN-US" style="FONT-FAMILY: \" verdana\?,\?sans-serif\??=""><o:p></o:p></span></font></h2><p class="MsoNormal" style="LINE-HEIGHT: 150%"><span lang="EN-US" style="FONT-FAMILY: \" verdana\?,\?sans-serif\??=""><span style="mso-tab-count: 1">      </span><o:p></o:p></span></p><table class="MsoNormalTable" style="BORDER-RIGHT: medium none; BORDER-TOP: medium none; BACKGROUND: #f2f2f2; BORDER-LEFT: medium none; BORDER-BOTTOM: medium none; BORDER-COLLAPSE: collapse; mso-shading: windowtext; mso-pattern: gray-5 auto; mso-border-alt: solid #9BBB59 1.0pt; mso-yfti-tbllook: 1184; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt; mso-border-insideh: 1.0pt solid #9BBB59; mso-border-insidev: 1.0pt solid #9BBB59" cellspacing="0" cellpadding="0" border="1"><tbody><tr style="mso-yfti-irow: 0; mso-yfti-firstrow: yes; mso-yfti-lastrow: yes"><td style="BORDER-RIGHT: #9bbb59 1pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: #9bbb59 1pt solid; PADDING-LEFT: 5.4pt; PADDING-BOTTOM: 0cm; BORDER-LEFT: #9bbb59 1pt solid; WIDTH: 426.1pt; PADDING-TOP: 0cm; BORDER-BOTTOM: #9bbb59 1pt solid" valign="top" width="568"><p class="MsoNormal" style="TEXT-INDENT: 21pt; LINE-HEIGHT: 150%"><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">当一个请求发送到</span><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?="">servlet</span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">容器的时候，容器先会将请求的</span><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?="">url</span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">减去当前应用上下文的</span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-weight: bold">路径</span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">作为</span><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?="">servlet</span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">的映射</span><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?="">url</span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">，比如我访问的是</span><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?=""><a href="http://localhost/test/aaa.html">http://localhost/test/aaa.html</a></span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">，我的应用上下文是</span><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?="" mso-hansi-font-family:="" calibri;="">test</span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">，容器会将</span><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?=""><a href="http://localhost/test">http://localhost/test</a></span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">去掉，剩下的</span><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?="">/aaa.html</span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">部分拿来做</span><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?="">servlet</span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">的映射匹配。这个映射匹配过程是有顺序的，而且当有一个</span><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?="">servlet</span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">匹配成功以后，就不会去理会剩下的</span><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?="">servlet</span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">了（</span><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?="">filter</span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">不同，后文会提到）。其匹配规则和顺序如下：</span><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?=""><o:p></o:p></span></p><p class="MsoListParagraph" style="MARGIN-LEFT: 42pt; TEXT-INDENT: -21pt; LINE-HEIGHT: 150%; mso-char-indent-count: 0; mso-list: l1 level1 lfo1"><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold;="" mso-fareast-font-family:="" verdana;="" mso-bidi-font-family:="" verdana?=""><span style="mso-list: Ignore">1.<span style="FONT: 7pt \" times="" new="" roman\??="">     </span></span></span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">精确路径匹配。例子：比如</span><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?="">servletA </span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">的</span><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?="">url-pattern</span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">为</span><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?=""> /test</span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">，</span><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?="">servletB</span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">的</span><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?="">url-pattern</span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">为</span><span style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?=""><span lang="EN-US">/* </span></span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">，这个时候，如果我访问的</span><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?="">url</span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">为</span><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?=""><a href="http://localhost/test">http://localhost/test</a></span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">，这个时候容器就会先</span><span style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?=""></span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">进行精确路径匹配，发现</span><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?="">/test</span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">正好被</span><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?="">servletA</span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">精确匹配，那么就去调用</span><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?="">servletA</span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">，也不会去理会其他的</span><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?="">servlet</span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">了。</span><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?=""><o:p></o:p></span></p><p class="MsoListParagraph" style="MARGIN-LEFT: 42pt; TEXT-INDENT: -21pt; LINE-HEIGHT: 150%; mso-char-indent-count: 0; mso-list: l1 level1 lfo1"><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold;="" mso-fareast-font-family:="" verdana;="" mso-bidi-font-family:="" verdana?=""><span style="mso-list: Ignore">2.<span style="FONT: 7pt \" times="" new="" roman\??="">     </span></span></span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">最长路径匹配。例子：</span><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?="">servletA</span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">的</span><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?="">url-pattern</span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">为</span><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?="">/test/*</span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">，而</span><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?="">servletB</span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">的</span><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?="">url-pattern</span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">为</span><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?="">/test/a/*</span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">，此时访问</span><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?=""><a href="http://localhost/test/a">http://localhost/test/a</a></span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">时，容器会选择路径最长的</span><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?="">servlet</span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">来匹配，也就是这里的</span><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?="">servletB</span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">。</span><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?=""><o:p></o:p></span></p><p class="MsoListParagraph" style="MARGIN-LEFT: 42pt; TEXT-INDENT: -21pt; LINE-HEIGHT: 150%; mso-char-indent-count: 0; mso-list: l1 level1 lfo1"><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold;="" mso-fareast-font-family:="" verdana;="" mso-bidi-font-family:="" verdana?=""><span style="mso-list: Ignore">3.<span style="FONT: 7pt \" times="" new="" roman\??="">     </span></span></span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">扩展匹配，如果</span><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?="">url</span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">最后一段包含扩展，容器将会根据扩展选择合适的</span><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?="">servlet</span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">。例子：</span><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?="">servletA</span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">的</span><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?="">url-pattern</span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">：</span><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?="">*.action<o:p></o:p></span></p><p class="MsoListParagraph" style="MARGIN-LEFT: 42pt; TEXT-INDENT: -21pt; LINE-HEIGHT: 150%; mso-char-indent-count: 0; mso-list: l1 level1 lfo1"><strong><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-fareast-font-family:="" verdana;="" mso-bidi-font-family:="" verdana?=""><span style="mso-list: Ignore">4.<span style="FONT: 7pt \" times="" new="" roman\??="">     </span></span></span></strong><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">如果前面三条规则都没有找到一个</span><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?="">servlet</span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">，容器会根据</span><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?="">url</span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">选择对应的请求资源。如果应用定义了一个</span><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?="">default servlet</span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">，则容器会将请求丢给</span><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?="">default servlet</span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-weight: bold">（什么是</span><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-bidi-font-weight:="" bold?="">default servlet</span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Verdana; mso-bidi-font-weight: bold">？后面会讲）</span><span style="COLOR: black; FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri; mso-bidi-font-weight: bold">。</span><strong><span lang="EN-US" style="COLOR: black; FONT-FAMILY: \" verdana\?,\?sans-serif\??=""><o:p></o:p></span></strong></p></td></tr></tbody></table><p class="MsoNormal" style="LINE-HEIGHT: 150%"><span lang="EN-US" style="FONT-FAMILY: \" verdana\?,\?sans-serif\??=""><span style="mso-spacerun: yes"> </span><span style="mso-tab-count: 1">    </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri">根据这个规则表，就能很清楚的知道</span><span lang="EN-US" style="FONT-FAMILY: \" verdana\?,\?sans-serif\??="">servlet</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri">的匹配过程，所以定义</span><span lang="EN-US" style="FONT-FAMILY: \" verdana\?,\?sans-serif\??="">servlet</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri">的时候也要考虑</span><span lang="EN-US" style="FONT-FAMILY: \" verdana\?,\?sans-serif\??="">url-pattern</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri">的写法，以免出错。</span><span lang="EN-US" style="FONT-FAMILY: \" verdana\?,\?sans-serif\??=""><o:p></o:p></span></p><p class="MsoNormal" style="LINE-HEIGHT: 150%"><span lang="EN-US" style="FONT-FAMILY: \" verdana\?,\?sans-serif\??=""><span style="mso-tab-count: 1">      </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri">对于</span><span lang="EN-US" style="FONT-FAMILY: \" verdana\?,\?sans-serif\??="">filter</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri">，不会像</span><span lang="EN-US" style="FONT-FAMILY: \" verdana\?,\?sans-serif\??="">servlet</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri">那样只匹配一个</span><span lang="EN-US" style="FONT-FAMILY: \" verdana\?,\?sans-serif\??="">servlet</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri">，因为</span><span lang="EN-US" style="FONT-FAMILY: \" verdana\?,\?sans-serif\??="">filter</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri">的集合是一个链，所以只会有处理的顺序不同，而不会出现只选择一个</span><span lang="EN-US" style="FONT-FAMILY: \" verdana\?,\?sans-serif\??="">filter</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri">。</span><span lang="EN-US" style="FONT-FAMILY: \" verdana\?,\?sans-serif\??="">Filter</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri">的处理顺序和</span><span lang="EN-US" style="FONT-FAMILY: \" verdana\?,\?sans-serif\??="">filter-mapping</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri">在</span><span lang="EN-US" style="FONT-FAMILY: \" verdana\?,\?sans-serif\??="">web.xml</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Calibri">中定义的顺序相同。</span><span lang="EN-US" style="FONT-FAMILY: \" verdana\?,\?sans-serif\??=""><o:p></o:p></span></p><h2 style="LINE-HEIGHT: 150%"><font size="3"><span lang="EN-US" style="FONT-FAMILY: \" verdana\?,\?sans-serif\??="">    二，url-pattern</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Verdana; mso-hansi-font-family: Cambria">详解</span><span lang="EN-US" style="FONT-FAMILY: \" verdana\?,\?sans-serif\?;="" mso-hansi-font-family:="" cambria?=""><o:p></o:p></span></font></h2><p class="MsoNormal" style="LINE-HEIGHT: 150%"><span lang="EN-US"><span style="mso-tab-count: 1">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri">在</span><span lang="EN-US">web.xml</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri">文件中，以下语法用于定义映射：</span></p><p class="MsoListParagraph" style="MARGIN-LEFT: 41.9pt; TEXT-INDENT: -21pt; LINE-HEIGHT: 150%; mso-char-indent-count: 0; mso-list: l0 level1 lfo2"><span lang="EN-US" style="FONT-FAMILY: Wingdings; mso-fareast-font-family: Wingdings; mso-bidi-font-family: Wingdings"><span style="mso-list: Ignore">l<span style="FONT: 7pt \" times="" new="" roman\??="">  </span></span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri">以</span><span lang="EN-US">”/’</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri">开头和以</span><span lang="EN-US">”/*”</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri">结尾的是用来做路径映射的。</span></p><p class="MsoListParagraph" style="MARGIN-LEFT: 41.9pt; TEXT-INDENT: -21pt; LINE-HEIGHT: 150%; mso-char-indent-count: 0; mso-list: l0 level1 lfo2"><span lang="EN-US" style="FONT-FAMILY: Wingdings; mso-fareast-font-family: Wingdings; mso-bidi-font-family: Wingdings"><span style="mso-list: Ignore">l<span style="FONT: 7pt \" times="" new="" roman\??="">  </span></span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri">以前缀</span><span lang="EN-US">”*.”</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri">开头的是用来做扩展映射的。</span></p><p class="MsoListParagraph" style="MARGIN-LEFT: 41.9pt; TEXT-INDENT: -21pt; LINE-HEIGHT: 150%; mso-char-indent-count: 0; mso-list: l0 level1 lfo2"><span lang="EN-US" style="FONT-FAMILY: Wingdings; mso-fareast-font-family: Wingdings; mso-bidi-font-family: Wingdings"><span style="mso-list: Ignore">l<span style="FONT: 7pt \" times="" new="" roman\??="">  </span></span></span><span lang="EN-US">“/” </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri">是用来定义</span><span lang="EN-US">default servlet</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri">映射的。</span></p><p class="MsoListParagraph" style="MARGIN-LEFT: 41.9pt; TEXT-INDENT: -21pt; LINE-HEIGHT: 150%; mso-char-indent-count: 0; mso-list: l0 level1 lfo2"><span lang="EN-US" style="FONT-FAMILY: Wingdings; mso-fareast-font-family: Wingdings; mso-bidi-font-family: Wingdings"><span style="mso-list: Ignore">l<span style="FONT: 7pt \" times="" new="" roman\??="">  </span></span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri">剩下的都是用来定义详细映射的。比如：</span><span lang="EN-US"> /aa/bb/cc.action</span></p><p class="MsoNormal" style="TEXT-INDENT: 20.9pt; LINE-HEIGHT: 150%"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri">所以，为什么定义</span><span lang="EN-US">”/*.action”</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri">这样一个看起来很正常的匹配会错？因为这个匹配即属于路径映射，也属于扩展映射，导致容器无法判断。</span></p><p class="MsoNormal" style="TEXT-INDENT: 20.9pt; LINE-HEIGHT: 150%"><span lang="EN-US"><o:p> </o:p></span></p><p class="MsoNormal" style="TEXT-INDENT: 20.9pt; LINE-HEIGHT: 150%"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri">【参考内容】</span></p><p class="MsoNormal" style="TEXT-INDENT: 20.9pt; LINE-HEIGHT: 150%"><span lang="EN-US"><span style="mso-tab-count: 3">                            </span></span><strong style="mso-bidi-font-weight: normal"><span lang="EN-US" style="FONT-SIZE: 14pt; LINE-HEIGHT: 150%">Java <span style="mso-spacerun: yes"> </span>Servlet 2.4 Specification<o:p></o:p></span></strong></p></td>
																</tr>
														</tbody>
												</table>
										</div>
								</td>
						</tr>
				</tbody>
		</table>
		<a href="http://www.javaeye.com/topic/39332?page=1">http://www.javaeye.com/topic/39332?page=1</a>
<img src ="http://www.blogjava.net/NeonWay/aggbug/89194.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/NeonWay/" target="_blank">王彦锋的技术实践</a> 2006-12-21 09:23 <a href="http://www.blogjava.net/NeonWay/archive/2006/12/21/89194.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于网页采集</title><link>http://www.blogjava.net/NeonWay/archive/2006/11/15/81328.html</link><dc:creator>王彦锋的技术实践</dc:creator><author>王彦锋的技术实践</author><pubDate>Wed, 15 Nov 2006 09:24:00 GMT</pubDate><guid>http://www.blogjava.net/NeonWay/archive/2006/11/15/81328.html</guid><wfw:comment>http://www.blogjava.net/NeonWay/comments/81328.html</wfw:comment><comments>http://www.blogjava.net/NeonWay/archive/2006/11/15/81328.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/NeonWay/comments/commentRss/81328.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/NeonWay/services/trackbacks/81328.html</trackback:ping><description><![CDATA[
		<a href="/ekinglong/archive/2006/11/12/80704.html">http://www.blogjava.net/ekinglong/archive/2006/11/12/80704.html</a>
		<br />
		<br />
		<a href="/ekinglong/archive/2006/10/27/77688.html">http://www.blogjava.net/ekinglong/archive/2006/10/27/77688.html</a>
<img src ="http://www.blogjava.net/NeonWay/aggbug/81328.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/NeonWay/" target="_blank">王彦锋的技术实践</a> 2006-11-15 17:24 <a href="http://www.blogjava.net/NeonWay/archive/2006/11/15/81328.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>摘：SQL Server 数据行大小</title><link>http://www.blogjava.net/NeonWay/archive/2006/11/03/78863.html</link><dc:creator>王彦锋的技术实践</dc:creator><author>王彦锋的技术实践</author><pubDate>Fri, 03 Nov 2006 02:20:00 GMT</pubDate><guid>http://www.blogjava.net/NeonWay/archive/2006/11/03/78863.html</guid><wfw:comment>http://www.blogjava.net/NeonWay/comments/78863.html</wfw:comment><comments>http://www.blogjava.net/NeonWay/archive/2006/11/03/78863.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/NeonWay/comments/commentRss/78863.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/NeonWay/services/trackbacks/78863.html</trackback:ping><description><![CDATA[
		<div id="articleContent">
				<p>
						<font size="2">我们看到有些数据库表中有两个字段，类型分别为 varchar(8000)、varchar(8000)，我们说这种设计是错误的，为什么呢？</font>
				</p>
				<p>
						<font size="2">SQL Server 存储中有个“页”的概念，一个“页”是 8K 大小，而一个数据行（一条记录）必须存储在一个页中，不能拆开存储在多个页中，也就是说一个数据行（一条记录）的大小最多是 8K，实际上由于 SQL Server 的页还具有页头，某些类型的字段还要额外占用一些空间，<strong>一个数据行（一条记录）的最大大小常常也只有七千多字节</strong>。</font>
				</p>
				<p>
						<font size="2">上述表设计中一个字段就达到了 8000 字节，不要说一个数据行（一条记录）了。</font>
				</p>
				<p>
						<font size="2">有人说，我虽然指定的是 8000 字节，但我存储时不存储满总可以了吧。技术上是允许的，但既然我们用不了那么多字节，为什么要写那么大呢？是不是考虑我们得用 text 字段，或者修改一下表结构。</font>
				</p>
				<p>
						<font size="2">如果一个数据行（一条记录）足够的小，一个页就可以存储更多的数据行（记录），SQL Server 查询时就不用翻太多的页，查询起来就会更快。<br /><br />引用地址：<a href="http://www.cftea.com/c/2006/10/TNRWRCF871NS33K4.asp">http://www.cftea.com/c/2006/10/TNRWRCF871NS33K4.asp</a></font>
				</p>
		</div>
<img src ="http://www.blogjava.net/NeonWay/aggbug/78863.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/NeonWay/" target="_blank">王彦锋的技术实践</a> 2006-11-03 10:20 <a href="http://www.blogjava.net/NeonWay/archive/2006/11/03/78863.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> 转－SQL Server开发人员应聘常被问的问题妙解汇总</title><link>http://www.blogjava.net/NeonWay/archive/2006/11/01/78415.html</link><dc:creator>王彦锋的技术实践</dc:creator><author>王彦锋的技术实践</author><pubDate>Wed, 01 Nov 2006 02:00:00 GMT</pubDate><guid>http://www.blogjava.net/NeonWay/archive/2006/11/01/78415.html</guid><wfw:comment>http://www.blogjava.net/NeonWay/comments/78415.html</wfw:comment><comments>http://www.blogjava.net/NeonWay/archive/2006/11/01/78415.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/NeonWay/comments/commentRss/78415.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/NeonWay/services/trackbacks/78415.html</trackback:ping><description><![CDATA[
		<div style="MARGIN-TOP: 1px; MARGIN-LEFT: 15px">
				<span class="dark_c" style="FONT-WEIGHT: bold; FONT-SIZE: 14pt">转－SQL Server开发人员应聘常被问的问题妙解汇总 </span>
		</div>
		<div style="MARGIN-TOP: 5px; MARGIN-LEFT: 15px">
				<b>关键字:</b> 工作        </div>
		<div style="MARGIN-TOP: 10px; MARGIN-LEFT: 15px">
				<table width="100%">
						<tbody>
								<tr>
										<td>
												<p>
														<br />目前在职场中很难找到非常合格的数据库开发人员。我的一个同事曾经说过:“SQL开发是一门语言，它很容易学，但是很难掌握。”</p>
												<p>　　在面试应聘的SQL Server数据库开发人员时，我运用了一套标准的基准技术问题。下面这些问题是我觉得能够真正有助于淘汰不合格应聘者的问题。它们按照从易到难的顺序排列。当您问到关于主键和外键的问题时，后面的问题都十分有难度，因为答案可能会更难解释和说明，尤其是在面试的情形下。</p>
												<p>　　您能向我简要叙述一下SQL Server 2000中使用的一些数据库对象吗?</p>
												<p>　　您希望听到的答案包括这样一些对象:表格、视图、用户定义的函数，以及存储过程;如果他们还能够提到像触发器这样的对象就更好了。如果应聘者不能回答这个基本的问题，那么这不是一个好兆头。</p>
												<p>　　什么是索引?SQL Server 2000里有什么类型的索引?</p>
												<p>　　任何有经验的数据库开发人员都应该能够很轻易地回答这个问题。一些经验不太多的开发人员能够回答这个问题，但是有些地方会说不清楚。</p>
												<p>　　简单地说，索引是一个数据结构，用来快速访问数据库表格或者视图里的数据。在SQL Server里，它们有两种形式:聚集索引和非聚集索引。聚集索引在索引的叶级保存数据。这意味着不论聚集索引里有表格的哪个(或哪些)字段，这些字段都会按顺序被保存在表格。由于存在这种排序，所以每个表格只会有一个聚集索引。非聚集索引在索引的叶级有一个行标识符。这个行标识符是一个指向磁盘上数据的指针。它允许每个表格有多个非聚集索引。</p>
												<p>　　NULL是什么意思?</p>
												<p>　　NULL(空)这个值是数据库世界里一个非常难缠的东西，所以有不少应聘者会在这个问题上跌跟头您也不要觉得意外。</p>
												<p>　　NULL这个值表示UNKNOWN(未知):它不表示“”(空字符串)。假设您的SQL Server数据库里有ANSI_NULLS，当然在默认情况下会有，对NULL这个值的任何比较都会生产一个NULL值。您不能把任何值与一个 UNKNOWN值进行比较，并在逻辑上希望获得一个答案。您必须使用IS NULL操作符。</p>
												<p>　　什么是主键?什么是外键?</p>
												<p>　　主键是表格里的(一个或多个)字段，只用来定义表格里的行;主键里的值总是唯一的。外键是一个用来建立两个表格之间关系的约束。这种关系一般都涉及一个表格里的主键字段与另外一个表格(尽管可能是同一个表格)里的一系列相连的字段。那么这些相连的字段就是外键。</p>
												<p>　　什么是触发器?SQL Server 2000有什么不同类型的触发器?</p>
												<p>　　让未来的数据库开发人员知道可用的触发器类型以及如何实现它们是非常有益的。</p>
												<p>　　触发器是一种专用类型的存储过程，它被捆绑到SQL Server 2000的表格或者视图上。在SQL Server 2000里，有INSTEAD-OF和AFTER两种触发器。INSTEAD-OF触发器是替代数据操控语言(Data Manipulation Language，DML)语句对表格执行语句的存储过程。例如，如果我有一个用于TableA的INSTEAD-OF-UPDATE触发器，同时对这个表格执行一个更新语句，那么INSTEAD-OF-UPDATE触发器里的代码会执行，而不是我执行的更新语句则不会执行操作。</p>
												<p>　　AFTER触发器要在DML语句在数据库里使用之后才执行。这些类型的触发器对于监视发生在数据库表格里的数据变化十分好用。</p>
												<p>　　您如何确一个带有名为Fld1字段的TableB表格里只具有Fld1字段里的那些值，而这些值同时在名为TableA的表格的Fld1字段里?</p>
												<p>　　这个与关系相关的问题有两个可能的答案。第一个答案(而且是您希望听到的答案)是使用外键限制。外键限制用来维护引用的完整性。它被用来确保表格里的字段只保存有已经在不同的(或者相同的)表格里的另一个字段里定义了的值。这个字段就是候选键(通常是另外一个表格的主键)。</p>
												<p>　　另外一种答案是触发器。触发器可以被用来保证以另外一种方式实现与限制相同的作用，但是它非常难设置与维护，而且性能一般都很糟糕。由于这个原因，微软建议开发人员使用外键限制而不是触发器来维护引用的完整性。</p>
												<p>对一个投入使用的在线事务处理表格有过多索引需要有什么样的性能考虑?</p>
												<p>　　您正在寻找进行与数据操控有关的应聘人员。对一个表格的索引越多，数据库引擎用来更新、插入或者删除数据所需要的时间就越多，因为在数据操控发生的时候索引也必须要维护。</p>
												<p>　　您可以用什么来确保表格里的字段只接受特定范围里的值?</p>
												<p>　　这个问题可以用多种方式来回答，但是只有一个答案是“好”答案。您希望听到的回答是Check限制，它在数据库表格里被定义，用来限制输入该列的值。</p>
												<p>　　触发器也可以被用来限制数据库表格里的字段能够接受的值，但是这种办法要求触发器在表格里被定义，这可能会在某些情况下影响到性能。因此，微软建议使用Check限制而不是其他的方式来限制域的完整性。</p>
												<p>　　返回参数和OUTPUT参数之间的区别是什么?</p>
												<p>　　如果应聘者能够正确地回答这个问题，那么他的机会就非常大了，因为这表明他们具有使用存储过程的经验。</p>
												<p>　　返回参数总是由存储过程返回，它用来表示存储过程是成功还是失败。返回参数总是INT数据类型。</p>
												<p>　　OUTPUT参数明确要求由开发人员来指定，它可以返回其他类型的数据，例如字符型和数值型的值。(可以用作输出参数的数据类型是有一些限制的。)您可以在一个存储过程里使用多个OUTPUT参数，而您只能够使用一个返回参数。</p>
												<p>　　什么是相关子查询?如何使用这些查询?</p>
												<p>　　经验更加丰富的开发人员将能够准确地描述这种类型的查询。</p>
												<p>　　相关子查询是一种包含子查询的特殊类型的查询。查询里包含的子查询会真正请求外部查询的值，从而形成一个类似于循环的状况。</p>
												<p>　　关于面试过程的思考</p>
												<p>　　这些问题只不过是确定一个SQL Server数据库开发人员是否合格的起点。根据应聘者对上面这些问题的回答情况，我可能会要求他们参加我的TSQL编程考试，这一般是一套根据不同情况进行的10到12个数据库查询。</p>
												<p>　　您需要自己决定将要雇用的开发人员具有什么样的专业技能。然后，需要通过自己的经验、判断以及在面试时对应聘者的感受(来做最终决定)。</p>
												<p>　　您在面试数据库开发人员时一般会问哪些问题呢?让我们一起来讨论一下吧。</p>
												<p>　　Tim Chapman是肯塔基州路易维尔市一家银行的SQL Server数据库管理员，他有超过7年的行业经验。他还通过了微软SQL Server 2000和SQL Server 2005的认证。 </p>
										</td>
								</tr>
						</tbody>
				</table>
		</div>引：<a href="http://jaaacky.javaeye.com/blog/31571">http://jaaacky.javaeye.com/blog/31571</a><img src ="http://www.blogjava.net/NeonWay/aggbug/78415.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/NeonWay/" target="_blank">王彦锋的技术实践</a> 2006-11-01 10:00 <a href="http://www.blogjava.net/NeonWay/archive/2006/11/01/78415.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>