﻿<?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-摆脱开始</title><link>http://www.blogjava.net/William/</link><description /><language>zh-cn</language><lastBuildDate>Tue, 12 May 2026 22:46:31 GMT</lastBuildDate><pubDate>Tue, 12 May 2026 22:46:31 GMT</pubDate><ttl>60</ttl><item><title>深入探讨Java的类加载机制</title><link>http://www.blogjava.net/William/archive/2006/08/25/65804.html</link><dc:creator>William</dc:creator><author>William</author><pubDate>Fri, 25 Aug 2006 07:05:00 GMT</pubDate><guid>http://www.blogjava.net/William/archive/2006/08/25/65804.html</guid><wfw:comment>http://www.blogjava.net/William/comments/65804.html</wfw:comment><comments>http://www.blogjava.net/William/archive/2006/08/25/65804.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.blogjava.net/William/comments/commentRss/65804.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/William/services/trackbacks/65804.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Java				语言是一种具有动态性的解释型编程语言，当指定程序运行的时候，				Java				虚拟机就将编译生成的				.				class				文件按照需求和一定的规则加载进内存，并组织成为一个完整的				Java				应用程序。				Java				语言把每个单独的类				Class				和接口				Implements...&nbsp;&nbsp;<a href='http://www.blogjava.net/William/archive/2006/08/25/65804.html'>阅读全文</a><img src ="http://www.blogjava.net/William/aggbug/65804.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/William/" target="_blank">William</a> 2006-08-25 15:05 <a href="http://www.blogjava.net/William/archive/2006/08/25/65804.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>中远公司JAVA面试题目</title><link>http://www.blogjava.net/William/archive/2006/07/22/59601.html</link><dc:creator>William</dc:creator><author>William</author><pubDate>Sat, 22 Jul 2006 15:33:00 GMT</pubDate><guid>http://www.blogjava.net/William/archive/2006/07/22/59601.html</guid><wfw:comment>http://www.blogjava.net/William/comments/59601.html</wfw:comment><comments>http://www.blogjava.net/William/archive/2006/07/22/59601.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.blogjava.net/William/comments/commentRss/59601.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/William/services/trackbacks/59601.html</trackback:ping><description><![CDATA[JAVA培训资料<br />一、Java语言<br />1、面向对象的三个基本特征<br />2、方法重载和方法重写的概念和区别<br /> 3、接口和内部类、抽象类的特性<br />4、文件读写的基本类<br />5、串行化的注意事项以及如何实现串行化<br /> 6、线程的基本概念、线程的基本状态以及状态之间的关系<br /> 7、线程的同步、如何实现线程的同步<br />8、几种常用的数据结构及内部实现原理。<br />9、Socket通信(TCP、UDP区别及Java实现方式)<br />10、Java的事件委托机制和垃圾回收机制<br />11、JDBC调用数据库的基本步骤<br />12、解析XML文件的几种方式和区别<br />13、Java四种基本权限的定义<br />14、Java的国际化<br />二、JSP<br /> 1、至少要能说出7个隐含对象以及他们的区别<br /> 2、forward 和redirect的区别<br /> 3、JSP的常用指令<br />三、servlet<br />1、什么情况下调用doGet()和doPost()？<br />2、servlet的init()方法和service()方法的区别<br />3、servlet的生命周期<br />4、如何现实servlet的单线程模式<br />5、servlet的配置<br />6、四种会话跟踪技术<br />四、EJB<br />1、EJB容器提供的服务<br />主要提供声明周期管理、代码产生、持续性管理、安全、事务管理、锁和并发行管理等服务。<br /> 2、EJB的角色和三个对象<br />EJB角色主要包括Bean开发者 应用组装者 部署者 系统管理员 EJB容器提供者 EJB服务器提供者<br />三个对象是Remote（Local）接口、Home（LocalHome）接口，Bean类<br />2、EJB的几种类型<br />会话（Session）Bean ，实体（Entity）Bean 消息驱动的（Message Driven）Bean<br />会话Bean又可分为有状态（Stateful）和无状态（Stateless）两种<br />实体Bean可分为Bean管理的持续性（BMP）和容器管理的持续性（CMP）两种<br /> 3、bean 实例的生命周期<br />对于Stateless Session Bean、Entity Bean、Message Driven Bean一般存在缓冲池管理，而对于Entity Bean和Statefull Session Bean存在Cache管理，通常包含创建实例，设置上下文、创建EJB Object（create）、业务方法调用、remove等过程，对于存在缓冲池管理的Bean，在create之后实例并不从内存清除，而是采用缓冲池调度机制不断重用实例，而对于存在Cache管理的Bean则通过激活和去激活机制保持Bean的状态并限制内存中实例数量。<br />4、激活机制<br />以Statefull Session Bean 为例：其Cache大小决定了内存中可以同时存在的Bean实例的数量，根据MRU或NRU算法，实例在激活和去激活状态之间迁移，激活机制是当客户端调用某个EJB实例业务方法时，如果对应EJB Object发现自己没有绑定对应的Bean实例则从其去激活Bean存储中（通过序列化机制存储实例）回复（激活）此实例。状态变迁前会调用对应的ejbActive和ejbPassivate方法。<br /> 5、remote接口和home接口主要作用<br />remote接口定义了业务方法，用于EJB客户端调用业务方法<br />;home接口是EJB工厂用于创建和移除查找EJB实例<br />6、客服端调用EJB对象的几个基本步骤<br />一、 设置JNDI服务工厂以及JNDI服务地址系统属性<br />二、 查找Home接口<br />三、 从Home接口调用Create方法创建Remote接口<br />四、 通过Remote接口调用其业务方法<br />五、数据库<br />1、存储过程的编写<br />2、基本的SQL语句<br />六、weblogic<br />1、 如何给weblogic指定大小的内存? <br />在启动Weblogic的脚本中（位于所在Domian对应服务器目录下的startServerName），增加set MEM_ARGS=-Xms32m -Xmx200m，可以调整最小内存为32M，最大200M<br />2、 如何设定的weblogic的热启动模式(开发模式)与产品发布模式?<br />可以在管理控制台中修改对应服务器的启动模式为开发或产品模式之一。或者修改服务的启动文件或者commenv文件，增加set PRODUCTION_MODE=true。<br />3、 如何启动时不需输入用户名与密码?<br />修改服务启动文件，增加 WLS_USER和WLS_PW项。也可以在boot.properties文件中增加加密过的用户名和密码.<br />4、 在weblogic管理制台中对一个应用域(或者说是一个网站,Domain)进行jms及ejb或连接池等相关信息进行配置后,实际保存在什么文件中?<br />保存在此Domain的config.xml文件中，它是服务器的核心配置文件。<br />5、 说说weblogic中一个Domain的缺省目录结构?比如要将一个简单的helloWorld.jsp放入何目录下,然的在浏览器上就可打入http://主机:端口号//helloword.jsp就可以看到运行结果了? 又比如这其中用到了一个自己写的javaBean该如何办?<br />Domain目录\服务器目录\applications，将应用目录放在此目录下将可以作为应用访问，如果是Web应用，应用目录需要满足Web应用目录要求，jsp文件可以直接放在应用目录中，Javabean需要放在应用目录的WEB-INF目录的classes目录中，设置服务器的缺省应用将可以实现在浏览器上无需输入应用名。<br />6、 如何查看在weblogic中已经发布的EJB?<br />可以使用管理控制台，在它的Deployment中可以查看所有已发布的EJB <br />7、 如何在weblogic中进行ssl配置与客户端的认证配置或说说j2ee(标准)进行ssl的配置<br />缺省安装中使用DemoIdentity.jks和DemoTrust.jks KeyStore实现SSL，需要配置服务器使用Enable SSL，配置其端口，在产品模式下需要从CA获取私有密钥和数字证书，创建identity和trust keystore，装载获得的密钥和数字证书。可以配置此SSL连接是单向还是双向的。<br /> 8、在weblogic中发布ejb需涉及到哪些配置文件<br />不同类型的EJB涉及的配置文件不同，都涉及到的配置文件包括ejb-jar.xml,weblogic-ejb-jar.xmlCMP实体Bean一般还需要weblogic-cmp-rdbms-jar.xml<br />9、EJB需直接实现它的业务接口或Home接口吗,请简述理由.<br />远程接口和Home接口不需要直接实现，他们的实现代码是由服务器产生的，程序运行中对应实现类会作为对应接口类型的实例被使用。<br />10、说说在weblogic中开发消息Bean时的persistent与non-persisten的差别<br />persistent方式的MDB可以保证消息传递的可靠性,也就是如果EJB容器出现问题而JMS服务器依然会将消息在此MDB可用的时候发送过来，而non－persistent方式的消息将被丢弃。<br />11、说说你所熟悉或听说过的j2ee中的几种常用模式?及对设计模式的一些看法<br />Session Facade Pattern：使用SessionBean访问EntityBean<br />Message Facade Pattern：实现异步调用<br />EJB Command Pattern：使用Command JavaBeans取代SessionBean，实现轻量级访问<br />Data Transfer Object Factory：通过DTO Factory简化EntityBean数据提供特性<br />Generic Attribute Access：通过AttibuteAccess接口简化EntityBean数据提供特性<br />Business Interface：通过远程（本地）接口和Bean类实现相同接口规范业务逻辑一致性<br />ＥＪＢ架构的设计好坏将直接影响系统的性能、可扩展性、可维护性、组件可重用性及开发效率。项目越复杂，项目队伍越庞大则越能体现良好设计的重要性<img src ="http://www.blogjava.net/William/aggbug/59601.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/William/" target="_blank">William</a> 2006-07-22 23:33 <a href="http://www.blogjava.net/William/archive/2006/07/22/59601.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java面试题</title><link>http://www.blogjava.net/William/archive/2006/07/22/59599.html</link><dc:creator>William</dc:creator><author>William</author><pubDate>Sat, 22 Jul 2006 15:28:00 GMT</pubDate><guid>http://www.blogjava.net/William/archive/2006/07/22/59599.html</guid><wfw:comment>http://www.blogjava.net/William/comments/59599.html</wfw:comment><comments>http://www.blogjava.net/William/archive/2006/07/22/59599.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/William/comments/commentRss/59599.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/William/services/trackbacks/59599.html</trackback:ping><description><![CDATA[1. <a href="http://tech.acnow.net/Html/Program/Java/" target="_blank">Java</a>中的异常处理机制的简单原理和应用。  <br /><br />当Java程序违反了Java的语义规则时，Java虚拟机就会将发生的错误表示为一个异常。违反语义规则包括2种情况。一种是Java类库内置的语义检查。例如数组下标越界,会引发IndexOutOfBoundsException;访问null的对象时会引发NullPointerException。另一种情况就是Java允许程序员扩展这种语义检查，程序员可以创建自己的异常，并自由选择在何时用throw关键字引发异常。所有的异常都是java.lang.Thowable的子类。  <br /><br />2. Java的接口和C++的虚类的相同和不同处。  <br /><br />由于Java不支持多继承，而有可能某个类或对象要使用分别在几个类或对象里面的方法或属性，现有的单继承机制就不能满足要求。与继承相比，接口有更高的灵活性，因为接口中没有任何实现代码。当一个类实现了接口以后，该类要实现接口里面所有的方法和属性，并且接口里面的属性在默认状态下面都是public static,所有方法默认情况下是public.一个类可以实现多个接口。  <br /><br />3. 垃圾回收的优点和原理。并考虑2种回收机制。  <br /><br />Java语言中一个显著的特点就是引入了垃圾回收机制，使c++程序员最头疼的内存管理的问题迎刃而解，它使得Java程序员在编写程序的时候不再需要考虑内存管理。由于有个垃圾回收机制，Java中的对象不再有“作用域”的概念，只有对象的引用才有“作用域”。垃圾回收可以有效的防止内存泄露，有效的使用可以使用的内存。垃圾回收器通常是作为一个单独的低级别的线程运行，不可预知的情况下对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收，程序员不能实时的调用垃圾回收器对某个对象或所有对象进行垃圾回收。回收机制有分代复制垃圾回收和标记垃圾回收，增量垃圾回收。  <br /><br />4.线程同步的方法。  <br /><br />wait():使一个线程处于等待状态，并且释放所持有的对象的lock。  <br /><br />sleep():使一个正在运行的线程处于睡眠状态，是一个静态方法，调用此方法要捕捉InterruptedException异常。  <br /><br />notify():唤醒一个处于等待状态的线程，注意的是在调用此方法的时候，并不能确切地唤醒某一个等待状态的线程，而是由JVM确定唤醒哪个线程，而且不是按优先级。  <br /><br />Allnotity():唤醒所有处入等待状态的线程，注意并不是给所有唤醒线程一个对象的锁，而是让它们竞争。  <br /><br />5. Error与Exception有什么区别？  <br /><br />Error表示系统级的错误和程序不必处理的异常，  <br /><br />Exception表示需要捕捉或者需要程序进行处理的异常。  <br />6. 在java中一个类被声明为final类型，表示了什么意思？  <br /><br />   表示该类不能被继承，是顶级类。 <br />7 heap和stack有什么区别。  <br />   栈是一种线形集合，其添加和删除元素的操作应在同一段完成。栈按照后进先出的方式进行处理。堆是栈的一个组成元素。  <br />8谈谈final, finally, finalize的区别。  <br />　　final—修饰符（关键字）如果一个类被声明为final，意味着它不能再派生出新的子类，不能作为父类被继承。因此一个类不能既被声明为 abstract的，又被声明为final的。将变量或方法声明为final，可以保证它们在使用中不被改变。被声明为final的变量必须在声明时给定初值，而在以后的引用中只能读取，不可修改。被声明为final的方法也同样只能使用，不能重载。  <br />　　finally—异常处理时提供 finally 块来执行任何清除操作。如果抛出一个异常，那么相匹配的 catch 子句就会执行，然后控制就会进入 finally 块（如果有的话）。  <br />　　finalize—方法名。<a href="http://tech.acnow.net/Html/Program/Java/" target="_blank">Java</a> 技术允许使用 finalize() 方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。它是在 Object 类中定义的，因此所有的类都继承了它。子类覆盖 finalize() 方法以整理系统资源或者执行其他清理工作。finalize() 方法是在垃圾收集器删除对象之前对这个对象调用的。 <br />9  Anonymous Inner Class (匿名内部类) 是否可以extends(继承)其它类，是否可以implements(实现)interface(接口)?  <br />　　匿名的内部类是没有名字的内部类。不能extends(继承) 其它类，但一个内部类可以作为一个接口，由另一个内部类实现。 <br />10  Static Nested Class 和 Inner Class的不同 <br />Nested Class （一般是C++的说法），Inner Class (一般是JAVA的说法)。Java内部类与C++嵌套类最大的不同就在于是否有指向外部的引用上。 <br />　　注： 静态内部类（Inner Class）意味着1创建一个static内部类的对象，不需要一个外部类对象，2不能从一个static内部类的一个对象访问一个外部类对象  
<p></p><p>11   &amp;和&amp;&amp;的区别。  <br />　　&amp;是位运算符。&amp;&amp;是布尔逻辑运算符。  <br />12  HashMap和Hashtable的区别。  <br />　　都属于Map接口的类，实现了将惟一键映射到特定的值上。  <br />　　HashMap 类没有分类或者排序。它允许一个 null 键和多个 null 值。  <br /></p><p></p><p>　　Hashtable 类似于 HashMap，但是不允许 null 键和 null 值。它也比 HashMap 慢，因为它是同步的。 <br />13 Collection 和 Collections的区别。  <br />    Collection是个java.util下的接口，它是各种集合结构的父接口。 <br />　　Collections是个java.util下的类，它包含有各种有关集合操作的静态方法。  <br />14 什么时候用assert。  <br />　　断言是一个包含布尔表达式的语句，在执行这个语句时假定该表达式为 true。  <br />如果表达式计算为 false，那么系统会报告一个 Assertionerror。它用于调试目的：  <br />assert(a &gt; 0); // throws an Assertionerror if a &lt;= 0  <br />断言可以有两种形式：  <br />assert Expression1 ;  <br />assert Expression1 : Expression2 ;  <br />　　Expression1 应该总是产生一个布尔值。  <br />　　Expression2 可以是得出一个值的任意表达式。这个值用于生成显示更多调试  <br />信息的 String 消息。  <br />　　断言在默认情况下是禁用的。要在编译时启用断言，需要使用 source 1.4 标记： <br />　　javac -source 1.4 Test.java  <br />　　要在运行时启用断言，可使用 -enableassertions 或者 -ea 标记。  <br />　　要在运行时选择禁用断言，可使用 -da 或者 -disableassertions 标记。  <br />　　要系统类中启用断言，可使用 -esa 或者 -dsa 标记。还可以在包的基础上启用或者禁用断言。  <br />　　可以在预计正常情况下不会到达的任何位置上放置断言。断言可以用于验证传递给私有方法的参数。不过，断言不应该用于验证传递给公有方法的参数，因为不管是否启用了断言，公有方法都必须检查其参数。不过，既可以在公有方法中，也可以在非公有方法中利用断言测试后置条件。另外，断言不应该以任何方式改变程序的状态。  <br />15 GC是什么? 为什么要有GC? (基础)。  <br />　　GC是垃圾收集器。<a href="http://tech.acnow.net/Html/Program/Java/" target="_blank">Java</a> 程序员不用担心内存管理，因为垃圾收集器会自动进行管理。要请求垃圾收集，可以调用下面的方法之一：  System.gc()  <br />Runtime.getRuntime().gc()  <br />16 String s = new String("xyz");创建了几个String Object?  <br />　　两个对象，一个是“xyz”,一个是指向“xyz”的引用对象s。  <br />17 Math.round(11.5)等於多少? Math.round(-11.5)等於多少?  <br />　　Math.round(11.5)返回（long）12，Math.round(-11.5)返回（long）-11;  <br />18 short s1 = 1; s1 = s1 + 1;有什么错? short s1 = 1; s1 += 1;有什么错?  <br />　　short s1 = 1; s1 = s1 + 1;有错，s1是short型，s1+1是int型,不能显式转化为short型。可修改为s1 =(short)(s1 + 1) 。short s1 = 1; s1 += 1正确。  <br />19 sleep() 和 wait() 有什么区别? 搞线程的最爱 <br />　　sleep()方法是使线程停止一段时间的方法。在sleep 时间间隔期满后，线程不一定立即恢复执行。这是因为在那个时刻，其它线程可能正在运行而且没有被调度为放弃执行，除非(a)“醒来”的线程具有更高的优先级 (b)正在运行的线程因为其它原因而阻塞。  <br />　　wait()是线程交互时，如果线程对一个同步对象x 发出一个wait()调用，该线程会暂停执行，被调对象进入等待状态，直到被唤醒或等待时间到。  <br />20 <a href="http://tech.acnow.net/Html/Program/Java/" target="_blank">Java</a>有没有goto?  <br />　　Goto—java中的保留字，现在没有在java中使用。  <br />21 数组有没有length()这个方法? String有没有length()这个方法？  <br />　　数组没有length()这个方法，有length的属性。  <br />　　String有有length()这个方法。  </p><p></p><p>22 Overload和Override的区别。Overloaded的方法是否可以改变返回值的类型?  <br />　　方法的重写Overriding和重载Overloading是Java多态性的不同表现。重写Overriding是父类与子类之间多态性的一种表现，重载Overloading是一个类中多态性的一种表现。如果在子类中定义某方法与其父类有相同的名称和参数，我们说该方法被重写 (Overriding)。子类的对象使用这个方法时，将调用子类中的定义，对它而言，父类中的定义如同被“屏蔽”了。如果在一个类中定义了多个同名的方法，它们或有不同的参数个数或有不同的参数类型，则称为方法的重载(Overloading)。  <br />Overloaded的方法是可以改变返回值的类型。 <br />23 Set里的元素是不能重复的，那么用什么方法来区分重复与否呢? 是用==还是e  <br />quals()? 它们有何区别?  <br />　　Set里的元素是不能重复的，那么用iterator()方法来区分重复与否。equals(  <br />)是判读两个Set是否相等。 equals()和==方法决定引用值是否指向同一对象equals()在类中被覆盖，为的是当两个分离的对象的内容和类型相配的话，返回真值。  <br /><br />24最常见到的runtime exception。  <br />ArithmeticException, ArrayStoreException, BufferOverflowException, BufferUnderflowException, CannotRedoException, CannotUndoException, ClassCastException, CMMException, ConcurrentModificationException, DOMException,EmptyStackException, IllegalArgumentException, IllegalMonitorStateException, IllegalPathStateException, IllegalStateException,ImagingOpException, IndexOutOfBoundsException, MissingResourceException, NegativeArraySizeException, NoSuchElementException, NullPointerException, ProfileDataException, ProviderException, RasterFORMatException, Secur  <br />ityException, SystemException, UndeclaredThrowableException, UnmodifiableSetException, UnsupportedOperationException  <br />25 error和exception有什么区别?  <br />　　error 表示恢复不是不可能但很困难的情况下的一种严重问题。比如说内存溢出。不可能指望程序能处理这样的情况。  <br />　　exception 表示一种设计或实现问题。也就是说，它表示如果程序运行正常，从不会发生的情况。  <br />26 List, Set, Map是否继承自Collection接口?  <br />     List，Set是  <br />     Map不是  <br />27 abstract class和interface有什么区别?  <br />　　声明方法的存在而不去实现它的类被叫做抽象类（abstract class），它用于要创建一个体现某些基本行为的类，并为该类声明方法，但不能在该类中实现该类的情况。不能创建abstract 类的实例。然而可以创建一个变量，其类型是一个抽象类，并让它指向具体子类的一个实例。不能有抽象构造函数或抽象静态方法。Abstract 类的子类为它们父类中的所有抽象方法提供实现，否则它们也是抽象类为。取而代之，在子类中实现该方法。知道其行为的其它类可以在类中实现这些方法。  <br />接口（interface）是抽象类的变体。在接口中，所有方法都是抽象的。多继承性可通过实现这样的接口而获得。接口中的所有方法都是抽象的，没有一个有程序体。接口只可以定义static final成员变量。接口的实现与子类相似，除了该实现类不能从接口定义中继承行为。当类实现特殊接口时，它定义（即将程序体给予）所有这种接口的方法。然后，它可以在实现了该接口的类的任何对象上调用接口的方法。由于有抽象类，它允许使用接口名作为引用变量的类型。通常的动态联编将生效。引用可以转换到接口类型或从接口类型转换，instanceof 运算符可以用来决定某对象的类是否实现了接口。  <br /><br />28 abstract的method是否可同时是static,是否可同时是native，是否可同时是synchronized?  <br />　　都不能  <br />29 接口是否可继承接口? 抽象类是否可实现(implements)接口? 抽象类是否可继承实体类(concrete class)?  <br />　　接口可以继承接口。抽象类可以实现(implements)接口，抽象类可继承实体类，但前提是实体类必须有明确的构造函数。  <br />30 启动一个线程是用run()还是start()?  <br />　　启动一个线程是调用start()方法，使线程所代表的虚拟处理机处于可运行状态，这意味着它可以由JVM调度并执行。这并不意味着线程就会立即运行。run()方法可以产生必须退出的标志来停止一个线程。  <br />31 构造器Constructor是否可被override?  <br />　　构造器Constructor不能被继承，因此不能重写Overriding，但可以被重载Overloading。  <br />32 是否可以继承String类?  <br />　　String类是final类故不可以继承。  <br />33 当一个线程进入一个对象的一个synchronized方法后，其它线程是否可进入此对象的其它方法?  <br />　　不能，一个对象的一个synchronized方法只能由一个线程访问。  <br />33  try {}里有一个return语句，那么紧跟在这个try后的finally {}里的code会不  <br />会被执行，什么时候被执行，在return前还是后?  <br />　　会执行，在return前执行。  <br />34 编程题: 用最有效率的方法算出2乘以8等於几?  <br />　　2 &lt;&lt; 3  <br />35 两个对象值相同(x.equals(y) == true)，但却可有不同的hash code，这句话对不对?  <br />　　不对，有相同的hash code。  <br />36  当一个对象被当作参数传递到一个方法后，此方法可改变这个对象的属性，并 可返回变化后的结果，那么这里到底是值传递还是引用传递?  <br />　　是值传递。<a href="http://tech.acnow.net/Html/Program/Java/" target="_blank">Java</a> 编程语言只由值传递参数。当一个对象实例作为一个参数被传递到方法中时，参数的值就是对该对象的引用。对象的内容可以在被调用的方法中改变，但对象的引用是永远不会改变的。  <br />37  swtich是否能作用在byte上，是否能作用在long上，是否能作用在String上?  <br />　　switch（expr1）中，expr1是一个整数表达式。因此传递给 switch 和 case 语句的参数应该是 int、 short、 char 或者 byte。long,string 都不能作用于swtich。  <br />38　Hashtable和HashMap  <br />　　Hashtable继承自Dictionary类，而HashMap是Java1.2引进的Map interface的一个实现  <br />　　HashMap允许将null作为一个entry的key或者value，而Hashtable不允许 <br />还有就是，HashMap把Hashtable的contains方法去掉了，改成containsvalue和containsKey。因为contains方法容易让人引起误解。 <br />　　最大的不同是，Hashtable的方法是Synchronize的，而HashMap不是，在 <br />多个线程访问Hashtable时，不需要自己为它的方法实现同步，而HashMap就必须为之提供外同步。  <br />Hashtable和HashMap采用的hash/rehash算法都大概一样，所以性能不会有很大的差异。 </p><img src ="http://www.blogjava.net/William/aggbug/59599.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/William/" target="_blank">William</a> 2006-07-22 23:28 <a href="http://www.blogjava.net/William/archive/2006/07/22/59599.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JAVA相关基础知识</title><link>http://www.blogjava.net/William/archive/2006/07/22/59597.html</link><dc:creator>William</dc:creator><author>William</author><pubDate>Sat, 22 Jul 2006 15:21:00 GMT</pubDate><guid>http://www.blogjava.net/William/archive/2006/07/22/59597.html</guid><wfw:comment>http://www.blogjava.net/William/comments/59597.html</wfw:comment><comments>http://www.blogjava.net/William/archive/2006/07/22/59597.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/William/comments/commentRss/59597.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/William/services/trackbacks/59597.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: JAVA相关基础知识1、面向对象的特征有哪些方面   1.抽象：抽象就是忽略一个主题中与当前目标无关的那些方面，以便更充分地注意与当前目标有关的方面。抽象并不打算了解全部问题，而只是选择其中的一部分，暂时不用部分细节。抽象包括两个方面，一是过程抽象，二是数据抽象。2.继承：继承是一种联结类的层次模型，并且允许和鼓励类的重用，它提供了一种明确表述共性的方法。对象的一个新类可以从现有的类中派生，这个过...&nbsp;&nbsp;<a href='http://www.blogjava.net/William/archive/2006/07/22/59597.html'>阅读全文</a><img src ="http://www.blogjava.net/William/aggbug/59597.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/William/" target="_blank">William</a> 2006-07-22 23:21 <a href="http://www.blogjava.net/William/archive/2006/07/22/59597.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java思想</title><link>http://www.blogjava.net/William/archive/2006/07/22/59592.html</link><dc:creator>William</dc:creator><author>William</author><pubDate>Sat, 22 Jul 2006 14:53:00 GMT</pubDate><guid>http://www.blogjava.net/William/archive/2006/07/22/59592.html</guid><wfw:comment>http://www.blogjava.net/William/comments/59592.html</wfw:comment><comments>http://www.blogjava.net/William/archive/2006/07/22/59592.html#Feedback</comments><slash:comments>13</slash:comments><wfw:commentRss>http://www.blogjava.net/William/comments/commentRss/59592.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/William/services/trackbacks/59592.html</trackback:ping><description><![CDATA[半年前，对JAVA的了解基本上为零，不知道J2EE是什么，不知道那么多的开源项目，不知道原来J2EE里面的概念是如此的多，这半年学习Java的经历让我自己对Java的认识经历了好几个阶段，可能这应该就是一个成长的过程，至少对于Java本身，可能是要毕业的缘故，现在对自己自身的能力的要求显得特别的苛刻，毕竟是要找工作了。<br />其实学习Java对于我来说也是很突然，由于来中兴通讯实习，工作用的语言就是Java，所以我就开始学习Java了，还好Java的语法跟C＋＋语言很像，所以入门Java个人感觉不是很难，而且开始看了几乎有几万行的Java代码，所以对于学习Java的环境还是很不错的环境吧，实习生的工作不是很繁忙，自由支配的时间还是比较多的，我基本上把所有的自由时间都放到的学习Java上，刚开始对Java有一个误解，以后跟学C＋＋语言一样，学会语言，再用好一个开发工具，基本上也就可以处理问题了，可是隐藏在Java后面的J2EE（现在叫Java EE了吧）确实让我大开的眼界。<br />刚开始学习Java可以说是热情高涨的，应为相对于C＋＋语言来说，Java的易用性以及特别好的面向对象思想的实现让我觉得，就是Java了。可是接触的多了，发现的也就多了，问题也就多了，从来没有一下子接触过那么多的概念，J2EE学习让我一下子感觉Java的庞大和复杂，好像一下子有点找不到北了，我该先学什么呢，学什么好呢，等等等等。。。。好像开始有点郁闷了，那个乱啊，有时候学着学着就学岔了，呵呵。<br />由于感觉自己现在像进入了一个迷宫，就感觉怎么J2EE东西怎么那么多啊，一个东西还没有学会呢，另外一个新技术就又出来了，你说这算个什么事啊，这不明摆着把我们这些Java程序员往死里整呢吗，一度都有放弃Java的念头，不过由于工作的关系了，不学不行，所以就继续了坚持，也就是因为这个坚持，让我对Java有了新的认识，对自己的学习方法也有了新的认识。<br />前一阵子由于感觉自己的学习特别乱，东一榔头西一棒的，不着边际，压根就学不到真正的东西，于是就给自己定了个学习计划，从现在开始的第一个月研究Hibernate，然后研究Struts，然后研究spring，最后总结一下自己的学习成果，用自己学的东西做一个基于Struts＋Hibernate＋spring的论坛出来，就算自己的学习成果了吧，然而不能说的计划没有用吧，虽然没有真正的坚持我的计划，不过就是应为这个计划让我对Java的学习有了新的看法。我们知道J2EE的企业架构的Web程序开发主要是这样发展过来的：首先就是基于JSP＋JavaBean，然后有了Struts，Spring，对于数据库的访问从刚开始的JDBC发展到现在ORM，确实让我们这些程序员的应用开发省了不少麻烦，不过有一个问题，这些架构的出现是为了什么呢？其实很简单，就是为了减轻我们的工作量，让我们更关注于应用。可是，我不知道别人是怎么看的，至少对于我来说，对于我一个初学Java不到半年的人来说，早早知道这些架构看来是没有什么好处的，学习的急功近利让我追逐的永远都是这些架构，这些为了减轻工作量的架构，而对于Java基础的学习则撂倒了一边，有点可悲，基础还没有牢固的情况下就去追求那些时髦的架构，再来看看我定的学习计划，好像有点可笑了，甚至是幼稚的。<br />现在我觉得有必要端正一下我的思想了，重新制定一个学习计划，毕竟10月分到了我还要凭着这玩意找工作呢。其实现在想想自己也的确犯了程序员最严重的错误：不研究最基本的东西，而去追逐那些花哨的技术，其实那些东西也都是这些最基本的东西开发出来的。所以从现在开始重新回到Java语言本身上来，好好研究Java语言的一些技巧，强烈地提高一下自己的Java基本功，免得回头跟别人热烈地讨论spring的时候说到语言本身的IO操作一脸茫然了。<br />说实话，我现在也想通了，就是说的俗一点，找工作的时候面试官的大部分问题应该是关于Java基本功的，面试题也基本上是关于Java语言以及数据结构等方面的，对于Hibernate，spring，我觉得他肯定不会问你某个配置文件怎么写吧，问得也是思想，比如ORM是什么概念，核心内容是什么等等，所以对于这些框架来说，我觉得会用就足够了，再说，基本概念掌握以后，学习这些架构才能真正学到精华，学到这些架构的思想，只有这样，我觉得才有可能真正成为一名架构师吧，呵呵，理想，奋斗ing。<br /><br />从基本学期，摆脱浮躁，摆脱开始。<img src ="http://www.blogjava.net/William/aggbug/59592.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/William/" target="_blank">William</a> 2006-07-22 22:53 <a href="http://www.blogjava.net/William/archive/2006/07/22/59592.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>浅谈Hibernate的flush机制(ZT)</title><link>http://www.blogjava.net/William/archive/2006/07/02/56206.html</link><dc:creator>William</dc:creator><author>William</author><pubDate>Sun, 02 Jul 2006 14:09:00 GMT</pubDate><guid>http://www.blogjava.net/William/archive/2006/07/02/56206.html</guid><wfw:comment>http://www.blogjava.net/William/comments/56206.html</wfw:comment><comments>http://www.blogjava.net/William/archive/2006/07/02/56206.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/William/comments/commentRss/56206.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/William/services/trackbacks/56206.html</trackback:ping><description><![CDATA[随着Hibernate在Java开发中的广泛应用，我们在使用Hibernate进行对象持久化操作中也遇到了各种各样的问题。这些问题往往都是我们对Hibernate缺乏了解所致，这里我讲个我从前遇到的问题及一些想法，希望能给大家一点借鉴。<br /> <br />       这是在一次事务提交时遇到的异常。<br />       an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session)<br />net.sf.hibernate.AssertionFailure: possible nonthreadsafe access to session<br />注：非possible non-threadsafe access to the session （那是另外的错误，类似但不一样）<br /> <br />       这个异常应该很多的朋友都遇到过，原因可能各不相同。但所有的异常都应该是在flush或者事务提交的过程中发生的。这一般由我们在事务开始至事务提交的过程中进行了不正确的操作导致，也会在多线程同时操作一个Session时发生，这里我们仅仅讨论单线程的情况，多线程除了线程同步外基本与此相同。<br /> <br />       至于具体是什么样的错误操作那？我给大家看一个例子（假设Hibernate配置正确，为保持代码简洁，不引入包及处理任何异常）<br />  <br /><br />SessionFactory sf = new Configuration().configure().buildSessionFactory() ;<br />Session s = sf.openSession();<br />Cat cat = new Cat();<br /><br />Transaction tran = s.beginTransaction(); (1)<br />s.save(cat); (2)（此处同样可以为update delete）<br />s.evict(cat); (3)<br />tran.commit(); (4)<br />s.close();(5)<br /><br /><br />       这就是引起此异常的典型错误。我当时就遇到了这个异常，检查代码时根本没感觉到这段代码出了问题，想当然的认为在Session上开始一个事务，通过Session将对象存入数据库，再将这个对象从Session上拆离，提交事务，这是一个很正常的流程。如果这里正常的话，那问题一定在别处。<br /> <br />        问题恰恰就在这里，我的想法也许没有错，但是一个错误的论据所引出的观点永远都不可能是正确的。因为我一直以为直接在对数据库进行操作，忘记了在我与数据库之间隔了一个Hibernate，Hibernate在为我们提供持久化服务的同时，也改变了我们对数据库的操作方式，这种方式与我们直接的数据库操作有着很多的不同，正因为我们对这种方式没有一个大致的了解造成了我们的应用并未得到预先设想的结果。<br /> <br />那Hibernate的持久化机制到底有什么不同那？简单的说，Hibernate在数据库层之上实现了一个缓存区，当应用save或者update一个对象时，Hibernate并未将这个对象实际的写入数据库中，而仅仅是在缓存中根据应用的行为做了登记，在真正需要将缓存中的数据flush入数据库时才执行先前登记的所有行为。<br /> <br />在实际执行的过程中，每个Session是通过几个映射和集合来维护所有与该Session建立了关联的对象以及应用对这些对象所进行的操作的，与我们这次讨论有关的有entityEntries（与Session相关联的对象的映射）、insertions（所有的插入操作集合）、deletions（删除操作集合）、updates（更新操作集合）。下面我就开始解释在最开始的例子中，Hibernate到底是怎样运作的。<br />(1)生成一个事务的对象，并标记当前的Session处于事务状态（注：此时并未启动数据库级事务）。<br />(2)应用使用s.save保存cat对象，这个时候Session将cat这个对象放入entityEntries，用来标记cat已经和当前的会话建立了关联，由于应用对cat做了保存的操作，Session还要在insertions中登记应用的这个插入行为（行为包括：对象引用、对象id、Session、持久化处理类）。<br />(3)s.evict(cat)将cat对象从s会话中拆离，这时s会从entityEntries中将cat这个对象移出。<br />(4)事务提交，需要将所有缓存flush入数据库，Session启动一个事务，并按照insert,update,……,delete的顺序提交所有之前登记的操作（注意：所有insert执行完毕后才会执行update，这里的特殊处理也可能会将你的程序搞得一团糟，如需要控制操作的执行顺序，要善于使用flush），现在cat不在entityEntries中，但在执行insert的行为时只需要访问insertions就足够了，所以此时不会有任何的异常。异常出现在插入后通知Session该对象已经插入完毕这个步骤上，这个步骤中需要将entityEntries中cat的existsInDatabase标志置为true，由于cat并不存在于entityEntries中，此时Hibernate就认为insertions和entityEntries可能因为线程安全的问题产生了不同步（也不知道Hibernate的开发者是否考虑到例子中的处理方式，如果没有的话，这也许算是一个bug吧），于是一个net.sf.hibernate.AssertionFailure就被抛出，程序终止。<br /> <br />我想现在大家应该明白例子中的程序到底哪里有问题了吧，我们的错误的认为s.save会立即的执行，而将cat对象过早的与Session拆离，造成了Session的insertions和entityEntries中内容的不同步。所以我们在做此类操作时一定要清楚Hibernate什么时候会将数据flush入数据库，在未flush之前不要将已进行操作的对象从Session上拆离。<br /> <br />对于这个错误的解决方法是，我们可以在(2)和(3)之间插入一个s.flush()强制Session将缓存中的数据flush入数据库（此时Hibernate会提前启动事务，将(2)中的save登记的insert语句登记在数据库事务中，并将所有操作集合清空），这样在(4)事务提交时insertions集合就已经是空的了，即使我们拆离了cat也不会有任何的异常了。<br />前面简单的介绍了一下Hibernate的flush机制和对我们程序可能带来的影响以及相应的解决方法，Hibernate的缓存机制还会在其他的方面给我们的程序带来一些意想不到的影响。看下面的例子：<br />  <br /><br />（name为cat表的主键）<br />Cat cat = new Cat();<br />cat.setName(“tom”);<br />s.save(cat);<br /><br />cat.setName(“mary”);<br />s.update(cat);(6)<br /><br />Cat littleCat = new Cat();<br />littleCat.setName(“tom”);<br />s.save(littleCat);<br /><br />s.flush();<br /><br /><br />这个例子看起来有什么问题？估计不了解Hibernate缓存机制的人多半会说没有问题，但它也一样不能按照我们的思路正常运行，在flush过程中会产生主键冲突，可能你想问：“在save(littleCat)之前不是已经更改cat.name并已经更新了么？为什么还会发生主键冲突那？”这里的原因就是我在解释第一个例子时所提到的缓存flush顺序的问题，Hibernate按照insert,update,……,delete的顺序提交所有登记的操作，所以你的s.update(cat)虽然在程序中出现在s.save(littleCat)之前，但是在flush的过程中，所有的save都将在update之前执行，这就造成了主键冲突的发生。<br /> <br />这个例子中的更改方法一样是在(6)之后加入s.flush()强制Session在保存littleCat之前更新cat的name。这样在第二次flush时就只会执行s.save(littleCat)这次登记的动作，这样就不会出现主键冲突的状况。<br /> <br />再看一个例子（很奇怪的例子，但是能够说明问题）<br /><br />Cat cat = new Cat();<br />cat.setName(“tom”);<br />s.save(cat); (7)<br />s.delete(cat);(8)<br /><br />cat.id=null;(9)<br />s.save(cat);(10)<br />s.flush();<br /><br /> <br />这个例子在运行时会产生异常net.sf.hibernate.HibernateException: identifier of an instance of Cat altered from 8b818e920a86f038010a86f03a9d0001 to null<br /> <br />这里例子也是有关于缓存的问题，但是原因稍有不同：<br />（7）和（2）的处理相同。<br />（8）Session会在deletions中登记这个删除动作，同时更新entityEntries中该对象的登记状态为DELETED。<br />（9）Cat类的标识符字段为id,将其置为null便于重新分配id并保存进数据库。<br />（10）此时Session会首先在entityEntries查找cat对象是否曾经与Session做过关联，因为cat只改变了属性值，引用并未改变，所以会取得状态为DELETED的那个登记对象。由于第二次保存的对象已经在当前Session中删除，save会强制Session将缓存flush才会继续，flush的过程中首先要执行最开始的save动作，在这个save中检查了cat这个对象的id是否与原来执行动作时的id相同。不幸的是，此时cat的id被赋为null，异常被抛出，程序终止（此处要注意，我们在以后的开发过程尽量不要在flush之前改变已经进行了操作的对象的id）。<br /> <br />这个例子中的错误也是由于缓存的延时更新造成的（当然，与不正规的使用Hibernate也有关系），处理方法有两种：<br />1、在（8）之后flush，这样就可以保证（10）处save将cat作为一个全新的对象进行保存。<br />2、删除（9），这样第二次save所引起的强制flush可以正常的执行，在数据库中插入cat对象后将其删除，然后继续第二次save重新插入cat对象，此时cat的id仍与从前一致。<br /> <br />这两种方法可以根据不同的需要来使用，呵呵，总觉得好像是很不正规的方式来解决问题，但是问题本身也不够正规，只希望能够在应用开发中给大家一些帮助，不对的地方也希望各位给与指正。<br /> <br />　　总的来说，由于Hibernate的flush处理机制，我们在一些复杂的对象更新和保存的过程中就要考虑数据库操作顺序的改变以及延时flush是否对程序的结果有影响。如果确实存在着影响，那就可以在需要保持这种操作顺序的位置加入flush强制Hibernate将缓存中记录的操作flush入数据库，这样看起来也许不太美观，但很有效。<br /><img src ="http://www.blogjava.net/William/aggbug/56206.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/William/" target="_blank">William</a> 2006-07-02 22:09 <a href="http://www.blogjava.net/William/archive/2006/07/02/56206.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JDBC事务优化</title><link>http://www.blogjava.net/William/archive/2006/06/30/56053.html</link><dc:creator>William</dc:creator><author>William</author><pubDate>Fri, 30 Jun 2006 14:44:00 GMT</pubDate><guid>http://www.blogjava.net/William/archive/2006/06/30/56053.html</guid><wfw:comment>http://www.blogjava.net/William/comments/56053.html</wfw:comment><comments>http://www.blogjava.net/William/archive/2006/06/30/56053.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/William/comments/commentRss/56053.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/William/services/trackbacks/56053.html</trackback:ping><description><![CDATA[
		<font size="4">JDBC事务优化 </font>
		<p>
				<font size="4">
						<span class="italicbodycopy">
								<em>作者：Jack Shirazi</em>
						</span>
				</font>
		</p>
		<p>
				<font size="4">
						<span class="italicbodycopy">
								<b>
										<em>开发通过ACID测试的应用程序</em>
								</b>
						</span>
				</font>
		</p>
		<p>
				<font size="4">事务使得开发人员的工作变得简单多了。通过在JDBC API和诸如Oracle9i的关系数据库中使用事务功能，在更新多用户应用程序时，你可以把数据遭破坏的可能性降到最低。然而，事务需要处理开销，与免费事务应用程序（更容易被破坏）相比较，它会降低系统的性能。那么，当使用事务时，什么才是保持性能的最好方法？ </font>
		</p>
		<p>
				<font size="4">
						<span class="bodycopy">最佳的性能调优建议是避免做那些没必要做的事情。事务处理是数据库的大量工作，而且数据库默认地维护多种资源以确保事务具有ACID（原子性，一致性，隔离性和持续性）属性（查看"<span class="bodylink"><a href="http://www.oracle.com/global/cn/oramag/oracle/02-jul/o42special_jdbc.html#ACID">ACID Transaction Properties</a></span></span>"工具栏获取详细信息）。这些数据库资源管理多个数据并发操作以及提交和回滚操作，从而保证ACID事务属性。如果你能减少数据库的此类操作，就将提高应用程序的性能。让我们看一些避免处理开销并提高事务性能的方法。</font>
		</p>
		<p>
				<font size="4">自动提交模式</font>
		</p>
		<p>
				<font size="4">最大限度减少事务开销的第一个方法是通过把多个操作移到一个单一事务中来合并事务。默认情况下，JDBC连接工作在自动提交模式下，这就意味着每个发送到数据库的操作都会作为独立事务自动执行。在这种情况下，每个Statement.execute（）方法调用都如同由一条BEGIN TRANSACTION命令开始，并由一条COMMIT命令结束。 </font>
		</p>
		<p>
				<font size="4">关闭自动提交模式并且明确定义事务需要进行大量额外的工作，因为你必须手动添加事务划分语句（COMMIT和ROLLBACK）。但是合并事务可以减少性能开销，特别是当你对你的系统进行伸缩时。（下面的<span class="bodycopy">"<span class="bodylink"><a href="http://www.oracle.com/global/cn/oramag/oracle/02-jul/o42special_jdbc.html#BATCH">批量更新</a></span>"</span>部分会涉及到合并更新事务的技术细节。）在重负荷系统中，事务开销意义重大。开销越低，系统的可伸缩性就越好。</font>
		</p>
		<p>
				<font size="4">简单地使用Connection.setAutoCommit(false)命令来关闭自动提交模式。 </font>
		</p>
		<p>
				<font size="4">JDBC API还提供了一个Connection.getAutoCommit()方法来返回当前的自动提交模式。当你关闭了自动提交模式，你将需要使用两个事务划分方法：Connection.commit()和Connection.rollback()。 </font>
		</p>
		<p>
				<font size="4">当人工控制事务时，需要遵循以下几条原则：使事务尽可能保持简短，不要在一个事务中包含很多操作使它们变得非常冗长。（使事务打开并保持行开锁状态，会影响其他事务并降低可伸缩性。）然而，如果几项操作可以一项接一项地执行，那么就把它们合并到一个事务中。 </font>
		</p>
		<p>
				<font size="4">合并操作可能需要在你的SQL语句中增加额外的条件逻辑，并且可能需要临时表。不管这个开销，合并事务会更加有效，因为数据库可以在一个步骤内获得所有需要的锁，并在一个步骤内释放它们。 </font>
		</p>
		<p>
				<font size="4">当自动提交模式没有关闭时，它所引起的更多事务会产生更多的通信开销，更多的锁定和释放时间，以及与其他会话发生冲突的更大可能性。</font>
		</p>
		<p>
				<font size="4">批量更新</font>
		</p>
		<p>
				<font size="4">批量更新简单地说就是在一个事务和一个数据库调用中将多个DML语句（例如插入、更新和删除）发送到数据库。JDBC通过Statement.addBatch()和Statement.executeBatch()方法支持这项功能。批量更新的技巧相当简单，在下文中会加以说明。记住关闭自动提交模式（确保一个批处理作为一个事务执行），并且当你完成后这一切后，明确提交批事务。</font>
		</p>
		<p>
				<span class="bodycopy">
						<span class="bodylink">
								<a href="http://www.oracle.com/oramag/oracle/02-jul/o42special_jdbc_l1.html" target="_top">
										<font size="4">清单 1</font>
								</a>
						</span>
				</span>
				<font size="4">中的示例使用了普通的JDBC Statement对象。另外，JDBC API提供了一个PreparedStatement类，它也可以用参数表示SQL语句。 </font>
		</p>
		<p>
				<font size="4">此外，当你使用PreparedStatement 对象来代替Statement对象时，Oracle的JDBC批处理实施就可以得到优化。在Oracle JDBC中，Statement对象不会在一次网络传输中传送所有成批的SQL语句。由于这个限制，当成批传送语句时可以使用PreparedStatement对象，因为PreparedStatement在一次批处理中会传送所有的语句。 </font>
		</p>
		<p>
				<span class="bodylink">
						<a href="http://www.oracle.com/oramag/oracle/02-jul/o42special_jdbc_l2.html" target="_top">
								<font size="4">清单 2</font>
						</a>
				</span>
				<font size="4">给出了使用参数语句和PreparedStatement对象的相同批处理技巧。 </font>
		</p>
		<p>
				<font size="4">借助于所有批处理语句相同的查询计划，在PreparedStatement对象中利用参数语句使数据库进一步优化批处理。如果没有参数设定，语句就会各不相同，因而数据库就不能重复使用查询计划。 </font>
		</p>
		<p>
				<font size="4">虽然这种方法经常可以提高性能，但应注意以下几点：处理开销与创建查询计划的联合将导致第一次执行SQL语句时会比使用普通Statement对象时运行得更慢，而随后准备好的执行语句将会快很多。（开发人员经常把首次PreparedStatement批处理移动到应用程序中对时间要求低的部分加以执行。）使用PreparedStatement对象将比使用Statement对象更有效，特别是当使用超过50条语句的大批量处理时。<br /><br />以上的示例使用了JDBC规范所定义的标准批处理模式。Oracle的JDBC实施提供了一种可选择的批处理模式，它使用了一种被称作OraclePreparedStatement.setExecuteBatch(int)的新方法。在这种模式下，预设的语句被自动保存在客户端，直到语句的数量与setExecuteBatch(int)中的参数所定义的"批量值"相等。这样一来，积累的语句在一次传送中被发送到数据库。Oracle所推荐的这种模式在某些情况下会比标准的批处理模式更快。当使用它的时候，调整批量值来优化你的应用程序中事务的性能。Oracle模式惟一需要注意的一点是：它不是标准的--它使用官方JDBC规范所不支持的扩展功能。<br /></font>
		</p>
		<p>
				<span class="parahead1">
						<a name="TRANS">
								<b>
										<font size="4">事务隔离级别</font>
								</b>
						</a>
				</span>
				<font size="4">
				</font>
		</p>
		<p>
				<font size="4">事务被定义为全有或全无操作。一个事务的ACID属性确保每件事情都发生在一个事务，如同在事务期间在数据库中没有发生其他操作。由此可见，对数据库来说，确保ACID属性有很多工作要做。</font>
		</p>
		<p>
				<font size="4">JDBC Connection界面定义了五种事务隔离级别（在下面说明）。并不是所有的数据库都支持所有的级别。例如，Oracle9i只支持TRANSACTION_READ_COMMITTED和TRANSACTION_ SERIALIZABLE这两个级别。</font>
		</p>
		<p>
				<font size="4">许多数据库，例如Oracle9i，提供了其他事务级别支持。这些级别不提供"真正的"事务，因为它们不完全符合ACID属性。然而，它们通过可接受的事务功能提供更好的性能，因此它们对很多操作类型是非常有用的。</font>
		</p>
		<p>
				<font size="4">在JDBC中定义的级别包括： </font>
		</p>
		<p>
				<font size="4">TRANSACTION_NONE。正式地讲，TRANSACTION_NONE不是一个有效的事务级别。根据java.sql Connection API文件，这个级别表示事务是不被支持的，因此理论上说你不能使用TRANSACTION_NONE作为一个自变量赋给Connection.setTransactionIsolation()方法。事实上，虽然一些数据库实施了这个事务级别，但是Oracle9i却没有实施。</font>
		</p>
		<p>
				<font size="4">TRANSACTION_READ_UNCOMMITTED。这是最快的完全有效的事务级别。它允许你读取其他还没有被提交到数据库的并发事务做出的修改。这个API文件指出，脏读取（dirty reads）、不可重复读取（non-repeatable reads）和错误读取（phantom reads）都可以在这个事务级别发生（参阅"</font>
				<a href="http://www.oracle.com/global/cn/oramag/oracle/02-jul/o42special_jdbc_2.html#NONACID">
						<font size="4">一些非ACID事务问题</font>
				</a>
				<font size="4">"部分）。这个级别意在支持ACID的"原子性（Atomic）"部分，在这个级别中，你的修改如果被提交，将被认为是同时发生的；如果被撤销，就被当作什么也没发生。Oracle9i不支持这个级别。</font>
		</p>
		<p>
				<font size="4">TRANSACTION_READ_UNCOMMITTED。这是最快的完全有效的事务级别。它允许你读取其他还没有被提交到数据库的并发事务做出的修改。这个API文件指出，脏读取（dirty reads）、不可重复读取（non-repeatable reads）和错误读取（phantom reads）都可以在这个事务级别发生（参阅"</font>
				<a href="http://www.oracle.com/global/cn/oramag/oracle/02-jul/o42special_jdbc_2.html#NONACID">
						<font size="4">一些非ACID事务问题</font>
				</a>
				<font size="4">"部分）。</font>
				<a href="http://www.oracle.com/global/cn/oramag/oracle/02-jul/o42special_jdbc_2.html#NONACID">
						<a>
								<font size="4">
										<span class="bodycopy">这个级别意在支持ACID的"原子性（Atomic）"部分，在这个级别中，你的修改如果被提交，将被认为是同时发生的；如果被撤销，就被当作什么也没发生。Oracle9i不支持这个级别。</span>
								</font>
						</a>
				</a>
		</p>
		<a href="http://www.oracle.com/global/cn/oramag/oracle/02-jul/o42special_jdbc_2.html#NONACID">
				<a>
						<p>
								<font size="4">TRANSACTION_READ_COMMITTED。这是继TRANSACTION_READ_UNCOMMITTED之后最快的完全有效的级别。在此级别中，你可以读取已经被提交到数据库中的其他并发事务所做出的修改。API文件指出，脏读取在这个级别中是被禁止的，但是不可重复读取和错误读取都可以发生。这个级别是Oracle9i默认的级别。 </font>
						</p>
						<p>
								<span class="bodycopy">
										<font size="4">TRANSACTION_REPEATABLE_READ。这个级别比TRANSACTION_SERIALIZABLE快，但是比其他的事务级别要慢。读取操作可以重复进行，这意味着两次读取同样的域应该总是得到同样的值，除非事务本身改变了这个值。API文件指出，脏读取和不可重复读取在这个事务级别中是被禁止的，但是错误读取可以发生。 </font>
								</span>
						</p>
						<p>
								<font size="4">
										<span class="bodycopy">从技术上讲，数据库通过在被读取或写入的行上加锁来实施这个级别，并且保持锁定状态直到事务结束。这就防止了这些行被修改或删除，但是不能防止额外的行被添加--因此，就可能产生错误读取。Oracle9i不支持这个级别。</span>
								</font>
						</p>
						<p>
								<font size="4">
										<span class="bodycopy">TRANSACTION_SERIALIZABLE。这是最慢的事务级别，但是它完全与ACID兼容。"单词可串行化（serializable）"指的就是ACID兼容，其中你的事务被认为在整体上已经发生，就如同其他所有已提交的事务在这个事务之前或之后全部发生。换句话说，事务被串行执行。</span>
								</font>
						</p>
				</a>
		</a>
		<table class="bodycopy" cellpadding="5" width="50%" align="right" bgcolor="#f7f7e7" border="1" vspace="5" hspace="5">
				<a>
						<font size="4">
						</font>
				</a>
				<tbody>
						<tr>
								<a>
										<font size="4">
										</font>
								</a>
								<td>
										<a>
												<a href="http://www.oracle.com/global/cn/oramag/oracle/02-jul/o42special_jdbc_2.html#NONACID">
														<center>
																<span class="parahead1">
																		<a name="ACID">
																				<strong>
																						<font size="4">ACID事务属性</font>
																				</strong>
																		</a>
																</span>
																<font size="4">
																</font>
														</center>
												</a>
										</a>
										<p>
												<a>
														<a href="http://www.oracle.com/global/cn/oramag/oracle/02-jul/o42special_jdbc_2.html#NONACID">
														</a>
												</a>
												<a>
														<span class="bodycopy">
																<span class="bodylink">
																		<a href="http://www.oracle.com/global/cn/oramag/oracle/02-jul/o42special_jdbc_2.html#TRANS">
																				<font size="4">ACID指的时数据库事务的基本属性：原子性，一致性，隔离性和持续性。所有的Oracle事务全部符合这些属性，虽然你可以在数据库系统中进行手动设置级别来加强各个属性（参阅"事务隔离级别"部分）。</font>
																		</a>
																</span>
														</span>
														<font size="4">
														</font>
												</a>
										</p>
										<a>
												<p>
														<span class="bodycopy">
																<font size="4">原子性指的是事务中的整个活动序列必须被全部完成或全部放弃。事务不能部分地完成。与隔离性相结合后（见表1），原子性指的是任意一个事务将查看任何其他同时或以原子形式发生的事务所采取的所有活动。</font>
														</span>
												</p>
												<p>
														<font size="4">一致性指的是事务既可以建立一个新的、有效的数据状态（在这种状态中可以进行所有的改动），它可以在操作失败的情况下，把所有的数据返回到事务发生之前已有的状态。 </font>
												</p>
												<p>
														<font size="4">隔离性指在一个事务中发生的所有活动对其他事务来说都是不可见的，直到该事务被提交。</font>
												</p>
												<p>
														<font size="4">持续性指的是事务成功做出并提交的所有改动是不变的，并且必须克服系统故障。比如说，如果发生了故障或者系统重新启动，数据在最后提交事务之后所存在的状态下是可用的。 </font>
												</p>
										</a>
								</td>
						</tr>
				</tbody>
		</table>
		<p>
				<font size="4">
				</font>
		</p>
		<p>
				<font size="4">脏读取、不可重复读取和错误读取在TRANSACTION_SERIALIZABLE级别是全部被禁止的。从技术上讲，数据库通过锁定在事务中使用的表来实施这个级别。Oracle9i支持这个级别（正如每个与符合ACID的数据库那样）。 </font>
		</p>
		<p>
				<font size="4">
						<span class="italicbodycopy">
								<b>
										<em>开发通过ACID测试的应用程序</em>
								</b>
						</span>
				</font>
		</p>
		<p>
				<font size="4">选择正确的级别</font>
		</p>
		<p>
				<span class="bodycopy">
						<font size="4">你可以通过使用Connection.setTransactionIsolation()方法设定一个连接的事务级别。类似地，你可以通过使用Connection.getTransactionIsolation()方法获得连接的当前事务级别。你可以通过使用DatabaseMetaData.supportsTransaction IsolationLevel()方法确定由你的数据库驱动程序所支持的事务级别，如</font>
						<a href="http://www.oracle.com/oramag/oracle/02-jul/o42special_jdbc_l3.html" target="_top">
								<font size="4">清单3</font>
						</a>
						<font size="4">所示。</font>
				</span>
				<font size="4">
				</font>
		</p>
		<p>
				<font size="4">事务级别越高，数量越多、限制性更强的锁就会被运用到数据库记录或者表中。同时，更多的锁被运用到数据库和它们的覆盖面越宽，任意两个事务冲突的可能性就越大。</font>
		</p>
		<p>
				<span class="bodycopy">
						<font size="4">如果有一个冲突（例如两个事务试图获取同一个锁），第一个事务必将会成功，然而第二个事务将被阻止直到第一个事务释放该锁（或者是尝试获取该锁的行为超时导致操作失败）。 </font>
				</span>
		</p>
		<p>
				<span class="bodycopy">
						<font size="4">更多的冲突发生时，事务的执行速度将会变慢，因为它们将花费更多的时间用于解决冲突（等待锁被释放）。 </font>
				</span>
		</p>
		<p>
				<font size="4">最大限度地增加应用程序的可伸缩性需要平衡地理解事务执行方法。一方面，你可以通过将在事务中所执行的操作数量减到最少来优化应用程序，从而减少了单个事务所花费的时间。但是这样就增加了事务的总数量，这可能增加冲突的风险。使用批量操作，你可以最大限度地减少所执行事务的数量。 </font>
		</p>
		<p>
				<font size="4">然而，这增加了单个事务的长度，也可能增加冲突的风险。在任意一种情况下，当你降低事务隔离级别时，事务使用的锁就越少，因此越不会引起性能的下降。这样做的风险是因为没有使用完全符合ACID的事务，从而损失了功能性。</font>
		</p>
		<p>
				<font size="4">如果你需要把事务执行时间减到最少的话，在你的整个应用程序中使用一个事务级别好像并不是很理想。在应用程序中查找读取查询，对于每个查询，考虑在下面"</font>
				<a href="http://www.oracle.com/global/cn/oramag/oracle/02-jul/o42special_jdbc_2.html#NONACID">
						<font size="4">一些非ACID事务问题</font>
				</a>
				<font size="4">"部分列出的任何问题是否会对给定数据的查询或数据更新模式产生负面影响。读取静态表，或者只被读取它们的同一事务所更新的表，可以安全地使用最低的事务级别。在那些不可能进行并发更新的事务中，你可以安全、高效地使用诸如TRANSACTION_ READ_COMMITTED这样的级别。 </font>
		</p>
		<p>
				<span class="parahead1">
						<a name="NONACID">
								<b>
										<font size="4">一些非ACID事务问题</font>
								</b>
						</a>
				</span>
				<font size="4">
				</font>
		</p>
		<p>
				<font size="4">当一个连接使用了不完全符合ACID的TRANSATION_SERIALIZABLE事务级别时，就会发生很多问题。下面的例子使用了一个名为table_sizes的表，它有两个字段，tablename和tablesize。这个例子还使用了两个事务，T1和T2，其中T1使用TRANSACTION_SERIALIZABLE级别。</font>
		</p>
		<p>
				<font size="4">脏读取。当一个事务能发现一行中有未提交的更改时，就发生了一次脏读取。如果另一个事务改变了一个值，你的事务可以读取那个改变的值，但其他的事务将回滚其事务，使这个值无效或成为脏值。例如，这里给出了当T2使用事务级别TRANSACTION_ READ_UNCOMMITTED时发生的情况，其中记录为tablename=users，tablesize=11。 </font>
		</p>
		<table cellpadding="5" width="50%" align="right" bgcolor="#f7f7e7" border="1" vspace="5" hspace="5">
				<tbody>
						<tr>
								<td colspan="4">
										<center>
												<span class="parahead1">
														<a name="T1">
																<strong>
																		<font size="4">表1：每个事务问题在每个允许的事务隔离级别中发生的可能性。</font>
																</strong>
														</a>
												</span>
												<font size="4">
												</font>
										</center>
								</td>
						</tr>
						<tr>
								<td>
										<font size="4">隔离级别 </font>
								</td>
								<td>
										<font size="4">脏读取 </font>
								</td>
								<td>
										<font size="4">不可重复读取</font>
								</td>
								<td>
										<font size="4">错误插入</font>
								</td>
						</tr>
						<tr>
								<td>
										<font size="4">读取未提交数据</font>
								</td>
								<td>
										<font size="4">是</font>
								</td>
								<td>
										<font size="4">是</font>
								</td>
								<td>
										<font size="4">是</font>
								</td>
						</tr>
						<tr>
								<td>
										<font size="4">读取已提交数据</font>
								</td>
								<td>
										<font size="4">否 </font>
								</td>
								<td>
										<font size="4">是</font>
								</td>
								<td>
										<font size="4">是</font>
								</td>
						</tr>
						<tr>
								<td>
										<font size="4">可重复读取 </font>
								</td>
								<td>
										<font size="4">否 </font>
								</td>
								<td>
										<font size="4">否 </font>
								</td>
								<td>
										<font size="4">是</font>
								</td>
						</tr>
						<tr>
								<td>
										<font size="4">可串行化 </font>
								</td>
								<td>
										<font size="4">否 </font>
								</td>
								<td>
										<font size="4">否 </font>
								</td>
								<td>
										<font size="4">否 </font>
								</td>
						</tr>
				</tbody>
		</table>
		<p>
				<font size="4">
				</font>
		</p>
		<p>
				<font size="4">
						<span class="bodycopy">
								<font face="Arial">1. </font>
						</span>T1和T2启动它们的事务。<br /><span class="bodycopy"><font face="Arial">2. </font></span>T1将记录更新为tablename=users，tablesize=12。<br /><span class="bodycopy"><font face="Arial">3. </font></span>T2读取T1未提交的修改，读取记录tablename=user，tablesize=12，因为T2的事务级别意味着未提交的修改有时可以被读取。<br /><span class="bodycopy"><font face="Arial">4. </font></span>T1回滚事务，因此tablename=users行中tablesize=11。<br /><span class="bodycopy"><font face="Arial">5. </font></span>T2仍有无效的记录值：记录tablename=users，tablesize=12。但是它可以通过脏表尺寸值工作，并且能在脏值的基础上成功地提交修改。</font>
		</p>
		<p>
				<font size="4">不可重复读取。当事务内一条记录在事务被两次读取而没有更新时，就发生了不可重复读取，而且，从两次读取中可以看到不同的结果。如果你的事务读取一个值，并且另一事务提交了对这个值的一次修改（或删除了这条记录），然后你的事务便可以读取这个修改后的值（或发现这条记录丢失），尽管你的事务还没有提交或回滚。例如，这里给出了当T2使用事务级别TRANSACTION_READ_COMMITTED时发生的情况，其中记录为tablename=users，tablesize=11。 </font>
		</p>
		<p>
				<font size="4">
						<span class="bodycopy">
								<font face="Arial">1. </font>
						</span>T1和T2启动它们的事务。<br /><span class="bodycopy"><font face="Arial">2. </font></span>T2读取记录tablename=users，tablesize=11。<br /><span class="bodycopy"><font face="Arial">3. </font></span>T1将记录更新为tablename=users，tablesize=12并提交修改。<br /><span class="bodycopy"><font face="Arial">4. </font></span>T2重新读取记录tablename=users，并且现在看到tablesize=12，因为T2的事务级别意味着其他事务提交的修改可以被看到，尽管T2还没有提交或回滚。</font>
		</p>
		<p>
				<font size="4">错误读取。当一个事务读取由另一个未提交的事务插入的一行时，就发生了错误读取。如果另一事务将一行插入到一个表里，当你的事务查询那个表时就能够读取那条新记录，即使其他的事务相继回滚。例如，这里给出了当T2使用事务级别TRANSACTION_REPEATABLE_READ时发生的情况，其中记录为tablename=users，tablesize=11。</font>
		</p>
		<p>
				<font size="4">
						<span class="bodycopy">
								<font face="Arial">1. </font>
						</span>T1和T2启动它们的事务。<br /><span class="bodycopy"><font face="Arial">2. </font></span>T2执行SELECT * FROM table_sizes WHERE tablesize&gt;10并读取一行，tablesize=11的行tablename=user。<br /><span class="bodycopy"><font face="Arial">3. </font></span>T1插入tablename=groups，tablesize=28的记录。<br /><span class="bodycopy"><font face="Arial">4. </font></span>T2再次执行SELECT * FROM table_sizes WHERE tablesize&gt;10并读取两条记录： tablename=users，tablesize=11和tablename=groups，tablesize=28。<br /><span class="bodycopy"><font face="Arial">5. </font></span>T1回滚事务，因此记录tablename=goups，tablesize=28不再存在于table_sizes表中。</font>
		</p>
		<p>
				<font size="4">根据T2被读取的数据集中所具有的一条额外错误记录，T2可以成功地提交数据。</font>
		</p>
		<p>
				<font size="4">在</font>
				<a href="http://www.oracle.com/global/cn/oramag/oracle/02-jul/o42special_jdbc_2.html#T1">
						<font size="4">表1</font>
				</a>
				<font size="4">中，你会发现在每一个所允许的事件隔离级别中发生事务问题的可能性列表。</font>
		</p>
		<p>
				<b>
						<font size="4">用户控制的事务</font>
				</b>
		</p>
		<p>
				<font size="4">在许多应用程序中，用户在一个事务结束以前，必须执行一个明确的动作（比如点击"确定"或"取消"）。这些情况会导致很多问题。例如，如果用户忘记了终止活动或者让活动保持未完成的状态，资源就一直在应用程序和数据库中保持开放状态，这可能会在并发的活动和锁定的资源之间产生冲突，降低了系统的性能。只有单一的用户应用程序或用户不共享资源的应用程序，才不受这个问题的影响。</font>
		</p>
		<p>
				<font size="4">让用户处于一个JDBC事务控制下的主要解决方法是使用优化的事务。这些事务为外部的JDBC事务更新收集信息，然后使用一种机制来检查更新没有与其他任一个可能在两者间已经被处理的更新发生冲突。</font>
		</p>
		<p>
				<font size="4">检查优化冲突的机制包括使用时间标记或更改计数器，从期望状态检查区别。例如，当应用程序从用户输入收集用于更新的数据时，数据可以作为一批包含时间标记的安全机制的SQL语句，被发送到数据库，以确保数据库中的原始数据与最初用于客户应用程序的数据相同。一个成功的事务更新记录，包括时间标记，显示了最近修改的数据。如果来自另一用户的更新使第一个用户的修改失效，那么时间标记就会改变，并且当前事务将需要被回滚而不是被提交。对于许多应用程序，中等程度的冲突事务是很少的，因此事务经常会成功完成。</font>
		</p>
		<p>
				<span class="bodycopy">
						<font face="Arial" size="4">当一个事务失败时，应用程序把所输入的数据提交给用户，使用户能够根据导致冲突的更改，做出必要的修改并且重新提交。 </font>
				</span>
		</p>
		<p>
				<b>
						<font size="4">其他方面的考虑</font>
				</b>
		</p>
		<p>
				<font size="4">将来，使用JDBC3.0特性的开发人员将在更多的方法来优化事务。例如，Oracle9i第2版实施了几个JDBC3.0特性，包括事务存储点（Savepoint）。存储点让你在一个事务中标记一个点并且回滚它，而不是回滚整个事务。 </font>
		</p>
		<p>
				<font size="4">虽然这听起来有些难以置信，但存储点确实能够极大地减少性能开销。它们对性能的实际影响将证明JDBC3.0会得到更广泛的支持，但不要过多使用存储点或者避免在任何关键性能代码部分将它们放在一起。如果你确实要使用它们，就必须确保尽可能地使用Connection.release Savepoint(Savepoint)方法释放它们的资源。</font>
		</p>
		<p>
				<font size="4">当前，JDBC2.0支持跨越多个连接的分布式事务，并且Oracle提供了一个为分布式事务设计的符合业界XA规范的Java Transaction API（JTA）模块。为分布式事务实施一个外部事务管理器的决定并不是一件小事情，然而，分布式事务比普通事务明显要慢，因为它需要额外的通讯工作来协调多个数据库资源之间的连接。从纯粹的性能观点来看，最好是在转移到分布式事务体系结构以前考虑多种可选择的设计方案。</font>
		</p>
		<p>
				<font size="4">
						<span class="italicbodycopy">
								<em>
										<font face="Arial">
												<b>Jack Shirazi</b> (<span class="bodylink"><a href="mailto:jack@JavaPerformanceTuning.com">jack@JavaPerformanceTuning.com</a></span>)是 <span class="bodylink"><a href="http://www.javaperformancetuning.com/" target="_top">JavaPerformanceTuning.com</a></span></font>
								</em>
						</span>（一个关于Java应用程序性能调优的所有方面信息的首家资源站点）的主管，也是《Java Performance Tuning》（O'Reilly and Associates出版）一书的作者。</font>
		</p>
		<br />
<img src ="http://www.blogjava.net/William/aggbug/56053.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/William/" target="_blank">William</a> 2006-06-30 22:44 <a href="http://www.blogjava.net/William/archive/2006/06/30/56053.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JDBC事务级别</title><link>http://www.blogjava.net/William/archive/2006/06/30/56047.html</link><dc:creator>William</dc:creator><author>William</author><pubDate>Fri, 30 Jun 2006 14:08:00 GMT</pubDate><guid>http://www.blogjava.net/William/archive/2006/06/30/56047.html</guid><wfw:comment>http://www.blogjava.net/William/comments/56047.html</wfw:comment><comments>http://www.blogjava.net/William/archive/2006/06/30/56047.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/William/comments/commentRss/56047.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/William/services/trackbacks/56047.html</trackback:ping><description><![CDATA[TRANSACTION_NONE 说明不支持事务。<br /><br /><br />TRANSACTION_READ_UNCOMMITTED 说明在提交前一个事务可以看到另一个事务的变化。这样脏读、不可重复的读和虚读都是允许的。<br /><br /><br />TRANSACTION_READ_COMMITTED 说明读取未提交的数据是不允许的。这个级别仍然允许不可重复的读和虚读产生。<br /><br /><br />TRANSACTION_REPEATABLE_READ 说明事务保证能够再次读取相同的数据而不会失败，但虚读仍然会出现。<br /><br /><br />TRANSACTION_SERIALIZABLE 是最高的事务级别，它防止脏读、不可重复的读和虚读。 <img src ="http://www.blogjava.net/William/aggbug/56047.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/William/" target="_blank">William</a> 2006-06-30 22:08 <a href="http://www.blogjava.net/William/archive/2006/06/30/56047.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Hibernate的事务和并发(ZT)</title><link>http://www.blogjava.net/William/archive/2006/06/30/56034.html</link><dc:creator>William</dc:creator><author>William</author><pubDate>Fri, 30 Jun 2006 13:33:00 GMT</pubDate><guid>http://www.blogjava.net/William/archive/2006/06/30/56034.html</guid><wfw:comment>http://www.blogjava.net/William/comments/56034.html</wfw:comment><comments>http://www.blogjava.net/William/archive/2006/06/30/56034.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/William/comments/commentRss/56034.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/William/services/trackbacks/56034.html</trackback:ping><description><![CDATA[Hibernate的事务和并发控制很容易掌握。Hibernate直接使用JDBC连接和JTA资源，不添加任何附加锁定 行为。我们强烈推荐你花点时间了解JDBC编程，ANSI SQL查询语言和你使用 的数据库系统的事务隔离规范。Hibernate只添加自动版本管理，而不会锁 定内存中的对象，也不会改变数据库事务的隔离级别。基本上，使用 Hibernate就好像直接使用JDBC(或者JTA/CMT)来访问你的数据库资源。<br /><br /><div align="center"></div> 除了自动版本管理，针对行级悲观锁定，Hibernate也提供了辅助的API，它使用了 SELECT FOR UPDATE的SQL语法。本章后面会讨论这个API。 <br />我们从Configuration层、SessionFactory层, 和 Session层开始讨论Hibernate的并行控制、<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>事务和应用 程序的长事务。 <br /><br /><strong>12.1.Session和事务范围(transaction scopes)</strong><br />一个SessionFactory对象的创建代价很昂贵，它是线程<a href="http://www.gamvan.com/server/" target="_blank">安全</a>的对象，它被设计成可以 为所有的应用程序线程所共享。它只创建一次，通常是在应用程序启动的时候，由一个 Configuraion的实例来创建。 <br />一个Session的对象是轻型的，非线程<a href="http://www.gamvan.com/server/" target="_blank">安全</a>的，对于单个业务进程，单个的 工作单元而言，它只被使用一次，然后就丢弃。只有在需要的时候，Session 才会获取一个JDBC的Connection（或一个Datasource） 对象。所以你可以放心的打开和关闭Session，甚至当你并不确定一个特定的请 求是否需要数据访问时，你也可以这样做。(一旦你实现下面提到的使用了请求拦截的模式，这就 变得很重要了。 <br />此外我们还要考虑<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>事务。<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>事务应该尽可能的短，降低<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>锁定造成的资源争用。 <a href="http://www.gamvan.com/database/" target="_blank">数据库</a>长事务会导致你的应用程序无法扩展到高的并发负载。 <br />一个操作单元(Unit of work)的范围是多大？单个的Hibernate Session能跨越多个 <a href="http://www.gamvan.com/database/" target="_blank">数据库</a>事务吗？还是一个Session的作用范围对应一个<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>事务的范围？应该何时打开 Session，何时关闭Session？，你又如何划分<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>事务的边界呢？ <br /><br /><strong>12.1.1.操作单元(Unit of work)</strong><br />首先，别再用session-per-operation这种反模式了，也就是说，在单个线程中， 不要因为一次简单的<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>调用，就打开和关闭一次Session！<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>事务也是如此。 应用程序中的<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>调用是按照计划好的次序，分组为原子的操作单元。（注意，这也意味着，应用程 序中，在单个的SQL语句发送之后，自动事务提交(auto-commit)模式失效了。这种模式专门为SQL控制台操作设计的。 Hibernate禁止立即自动事务提交模式，或者期望应用服务器禁止立即自动事务提交模式。） <br />在多用户的client/server应用程序中，最常用的模式是 每个请求一个会话(session-per-request)。 在这种模式下，来自客户端的请求被发送到服务器端（即Hibernate持久化层运行的地方），一 个新的Hibernate Session被打开，并且执行这个操作单元中所有的<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>操作。 一旦操作完成（同时发送到客户端的响应也准备就绪），session被同步，然后关闭。你也可以使用单 个<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>事务来处理客户端请求，在你打开Session之后启动事务，在你关闭 Session之前提交事务。会话和请求之间的关系是一对一的关系，这种模式对 于大多数应用程序来说是很棒的。 <br />真正的挑战在于如何去实现这种模式：不仅Session和事务必须被正确的开始和结束， 而且他们也必须能被数据访问操作访问。用拦截器来实现操作单元的划分，该拦截器在客户端请求达到服 务器端的时候开始，在服务器端发送响应（即，ServletFilter）之前结束。我们推荐 使用一个ThreadLocal 变量，把 Session绑定到处理客户端请求的线 程上去。这种方式可以让运行在该线程上的所有程序代码轻松的访问Session（就像访问一 个静态变量那样）。你也可以在一个ThreadLocal 变量中保持事务上下文环境，不过这依赖 于你所选择的<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>事务划分机制。这种实现模式被称之为 ThreadLocal Session和 Open Session in View。你可以很容易的扩展本文前面章节展示的 HibernateUtil 辅助类来实现这种模式。当然，你必胝业揭恢质迪掷菇仄鞯姆椒ǎ?且可以把拦截器集成到你的应用环境中。请参考Hibernate<a href="http://www.gamvan.com/web/" target="_blank">网站</a>上面的提示和例子。 <br /><br /><strong>12.1.2.应用程序事务(Application transactions)</strong><br />session-per-request模式不仅仅是一个可以用来设计操作单元的有用概念。很多业务处理流程都需 要一系列完整的和用户之间的交互，即用户对<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>的交叉访问。在基于web的应用和企业 应用中，跨用户交互的<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>事务是无法接受的。考虑下面的例子： <br />在界面的第一屏，打开对话框，用户所看到的数据是被一个特定的 Session 和数据 库事务载入(load)的。用户可以随意修改对话框中的数据对象。 <br />5分钟后，用户点击“保存”，期望所做出的修改被持久化；同时他也期望自己是唯一修改这个信息的人，不会出现 修改冲突。 <br />从用户的角度来看，我们把这个操作单元称为应用程序长事务（application transaction）。 在你的应用程序中，可以有很多种方法来实现它。 <br />头一个幼稚的做法是，在用户思考的过程中，保持Session和<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>事务是打开的， 保持<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>锁定，以阻止并发修改，从而保证<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>事务隔离级别和原子操作。这种方式当然是一个反模式， 因为<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>锁定的维持会导致应用程序无法扩展并发用户的数目。 <br />很明显，我们必须使用多个<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>事务来实现一个应用程序事务。在这个例子中，维护业务处理流程的 事务隔离变成了应用程序层的部分责任。单个应用程序事务通常跨越多个<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>事务。如果仅仅只有一 个<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>事务（最后的那个事务）保存更新过的数据，而所有其他事务只是单纯的读取数据（例如在一 个跨越多个请求/响应周期的向导风格的对话框中），那么应用程序事务将保证其原子性。这种方式比听 起来还要容易实现，特别是当你使用了Hibernate的下述特性的时候： <br />自动版本化 - Hibernate能够自动进行乐观并发控制 ，如果在用户思考 的过程中发生并发修改冲突，Hibernate能够自动检测到。 <br />脱管对象（Detached Objects）- 如果你决定采用前面已经讨论过的 session-per-request模式，所有载入的实例在用户思考的过程 中都处于与Session脱离的状态。Hibernate允许你把与Session脱离的对象重新关联到Session 上，并且对修改进行持久化，这种模式被称为 session-per-request-with-detached-objects。自动版本化被用来隔离并发修改。 <br />长生命周期的Session （Long Session）- Hibernate 的Session 可以在<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>事务提交之后和底层的JDBC连接断开，当一个新的客户端请求到来的时候，它又重新连接上底层的 JDBC连接。这种模式被称之为session-per-application-transaction，这种情况可 能会造成不必要的Session和JDBC连接的重新关联。自动版本化被用来隔离并发修改。 <br />session-per-request-with-detached-objects 和 session-per-application-transaction 各有优缺点，我们在本章后面乐观并发 控制那部分再进行讨论。 <br /><br /><strong>12.1.3.关注对象标识(Considering object identity)</strong><br />应用程序可能在两个不同的Session中并发访问同一持久化状态，但是， 一个持久化类的实例无法在两个 Session中共享。因此有两种不同的标识语义： <br /><a href="http://www.gamvan.com/database/" target="_blank">数据库</a>标识 <br />foo.getId().equals( bar.getId() ) <br />JVM 标识 <br />foo==bar <br />对于那些关联到 特定Session （也就是在单个Session的范围内）上的对象来说，这 两种标识的语义是等价的，与<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>标识对应的JVM标识是由Hibernate来保 证的。不过，当应用程序在两个不同的session中并发访问具有同一持久化标 识的业务对象实例的时候，这个业务对象的两个实例事实上是不相同的（从 JVM识别来看）。这种冲突可以通过在同步和提交的时候使用自动版本化和乐 观锁定方法来解决。 <br />这种方式把关于并发的头疼问题留给了Hibernate和<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>；由于在单个线程内，操作单元中的对象识别不 需要代价昂贵的锁定或其他意义上的同步，因此它同时可以提供最好的可伸缩性。只要在单个线程只持有一个 Session，应用程序就不需要同步任何业务对象。在Session 的范围内，应用程序可以放心的使用==进行对象比较。 <br />不过，应用程序在Session的外面使用==进行对象比较可能会 导致无法预期的结果。在一些无法预料的场合，例如，如果你把两个脱管对象实例放进同一个 Set的时候，就可能发生。这两个对象实例可能有同一个<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>标识（也就是说， 他们代表了表的同一行数据），从JVM标识的定义上来说，对脱管的对象而言，Hibernate无法保证他们 的的JVM标识一致。开发人员必须覆盖持久化类的equals()方法和 hashCode() 方法，从而实现自定义的对象相等语义。警告：不要使用<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>标识 来实现对象相等，应该使用业务键值，由唯一的，通常不变的属性组成。当一个瞬时对象被持久化的时 候，它的<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>标识会发生改变。如果一个瞬时对象（通常也包括脱管对象实例）被放入一 个Set，改变它的hashcode会导致与这个Set的关系中断。虽 然业务键值的属性不象<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>主键那样稳定不变，但是你只需要保证在同一个Set 中的对象属性的稳定性就足够了。请到Hibernate<a href="http://www.gamvan.com/web/" target="_blank">网站</a>去寻求这个问题更多的详细的讨论。请注意，这不是一 个有关Hibernate的问题，而仅仅是一个关于Java对象标识和判等行为如何实现的问题。 <br /><br /><strong>12.1.4.常见问题</strong><br />决不要使用反模式session-per-user-session或者 session-per-application（当然，这个规定几乎没有例外）。请注意， 下述一些问题可能也会出现在我们推荐的模式中，在你作出某个设计决定之前，请务必理解该模式的应用前提。 <br />Session 是一个非线程<a href="http://www.gamvan.com/server/" target="_blank">安全</a>的类。如果一个Session 实例允许共享的话，那些支持并发运行的东东，例如HTTP request，session beans,或者是 Swing workers，将会导致出现资源争用（race condition）。如果在HttpSession中有 Hibernate 的Session的话（稍后讨论），你应该考虑同步访问你的Http session。 否则，只要用户足够快的点击浏览器的“刷新”，就会导致两个并发运行线程使用同一个 Session。 <br />一个由Hibernate抛出的异常意味着你必须立即回滚<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>事务，并立即关闭Session （稍后会展开讨论）。如果你的Session绑定到一个应用程序上，你必 须停止该应用程序。回滚<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>事务并不会把你的业务对象退回到事务启动时候的状态。这 意味着<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>状态和业务对象状态不同步。通常情况下，这不是什么问题，因为异常是不可 恢复的,你必须在回滚之后重新开始执行。 <br />Session 缓存了处于持久化状态的每个对象（Hibernate会监视和检查脏数据）。 这意味着，如果你让Session打开很长一段时间，或是仅仅载入了过多的数据， Session占用的内存会一直增长，直到抛出OutOfMemoryException异常。这个 问题的一个解决方法是调用clear() 和evict()来管理 Session的缓存，但是如果你需要大批量数据操作的话，最好考虑 使用存储过程。在第14章 批量处理（Batch processing）中有一些解决方案。在用户会话期间一直保持 Session打开也意味着出现脏数据的可能性很高。 <br /><br /><strong>12.2.<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>事务声明</strong><br /><a href="http://www.gamvan.com/database/" target="_blank">数据库</a>（或者系统）事务的声明总是必须的。在<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>事务之外，就无法和<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>通讯（这可能会让那些习惯于 自动提交事务模式的开发人员感到迷惑）。永远使用清晰的事务声明，即使只读操作也是如此。进行 显式的事务声明并不总是需要的，这取决于你的事务隔离级别和<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>的能力，但不管怎么说，声明事务总归有益无害。 <br />一个Hibernate应用程序可以运行在非托管环境中（也就是独立运行的应用程序，简单Web应用程序， 或者Swing图形桌面应用程序），也可以运行在托管的J2EE环境中。在一个非托管环境中，Hibernate 通常自己负责管理<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>连接池。应用程序开发人员必须手工设置事务声明，换句话说，就是手工启 动，提交，或者回滚<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>事务。一个托管的环境通常提供了容器管理事务，例如事务装配通过可声 明的方式定义在EJB session beans的部署描述符中。可编程式事务声明不再需要，即使是 Session 的同步也可以自动完成。 <br />让持久层具备可移植性是人们的理想。Hibernate提供了一套称为Transaction的封装API， 用来把你的部署环境中的本地事务管理系统转换到Hibernate事务上。这个API是可选的，但是我们强烈 推荐你使用，除非你用CMT session bean。 <br />通常情况下，结束 Session 包含了四个不同的阶段: <br />同步session(flush,刷出到磁盘） <br />提交事务 <br />关闭session <br />处理异常 <br />session的同步(flush,刷出）前面已经讨论过了，我们现在进一步考察在托管和非托管环境下的事务声明和异常处理。 <br /><br /><strong>12.2.1.非托管环境</strong><br />如果Hibernat持久层运行在一个非托管环境中，<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>连接通常由Hibernate的连接池机制 来处理。 <br /><table style="BORDER-TOP-WIDTH: 1px; BORDER-LEFT-WIDTH: 1px; BORDER-BOTTOM-WIDTH: 1px; BORDER-RIGHT-WIDTH: 1px" bordercolor="#e0e0e0" cellspacing="1" cellpadding="4" width="95%" align="center" border="1" table-layout:="" fixed=""><br /><tbody><br /><tr><br /><td style="HEIGHT: 25px; WORD-WRAP: break-word" valign="top" bgcolor="#f6f6f6"><font style="COLOR: #b0b0b0">代码内容</font><br />session/transaction处理方式如下所示： <br />//Non-managed environment idiom <br />Session sess = factory.openSession(); <br />Transaction tx = null; <br />try { <br />tx = sess.beginTransaction(); <br /><br />// do some work <br />... <br /><br />tx.commit(); <br />} <br />catch (RuntimeException e) { <br />if (tx != null) tx.rollback(); <br />throw e; // or display error message <br />} <br />finally { <br />sess.close(); <br />} </td></tr></tbody></table><br />你不需要显式flush() Session - 对commit()的调用会自动触发session的同步。 <br />调用 close() 标志session的结束。 close()方法重要的暗示是，session释放了JDBC连接。 <br />这段Java代码是可移植的，可以在非托管环境和JTA环境中运行。 <br />你很可能从未在一个标准的应用程序的业务代码中见过这样的用法；致命的（系统）异常应该总是 在应用程序“顶层”被捕获。换句话说，执行Hibernate调用的代码（在持久层）和处理 RuntimeException异常的代码（通常只能清理和退出应用程序）应该在不同 的应用程序逻辑层。这对于你设计自己的软件系统来说是一个挑战，只要有可能，你就应该使用 J2EE/EJB容器服务。异常处理将在本章稍后进行讨论。 <br />请注意，你应该选择 org.hibernate.transaction.JDBCTransactionFactory (这是默认选项). <br /><br /><strong>12.2.2.使用JTA</strong><br />如果你的持久层运行在一个应用服务器中（例如，在EJB session beans的后面），Hibernate获取 的每个数据源连接将自动成为全局JTA事务的一部分。Hibernate提供了两种策略进行JTA集成。 <br />如果你使用bean管理事务（BMT），可以通过使用Hibernate的 Transaction API来告诉 应用服务器启动和结束BMT事务。因此，事务管理代码和在非托管环境下是一样的。 <br /><br /><table style="BORDER-TOP-WIDTH: 1px; BORDER-LEFT-WIDTH: 1px; BORDER-BOTTOM-WIDTH: 1px; BORDER-RIGHT-WIDTH: 1px" bordercolor="#e0e0e0" cellspacing="1" cellpadding="4" width="95%" align="center" border="1" table-layout:="" fixed=""><br /><tbody><br /><tr><br /><td style="HEIGHT: 25px; WORD-WRAP: break-word" valign="top" bgcolor="#f6f6f6"><font style="COLOR: #b0b0b0">代码内容</font><br />// BMT idiom <br />Session sess = factory.openSession(); <br />Transaction tx = null; <br />try { <br />tx = sess.beginTransaction(); <br /><br />// do some work <br />... <br /><br />tx.commit(); <br />} <br />catch (RuntimeException e) { <br />if (tx != null) tx.rollback(); <br />throw e; // or display error message <br />} <br />finally { <br />sess.close(); <br />} </td></tr></tbody></table>在CMT方式下，事务声明是在session bean的部署描述符中，而不需要编程。 除非你设置了属性hibernate.transaction.flush_before_completion和 hibernate.transaction.auto_close_session为true， 否则你必须自己同步和关闭Session。Hibernate可以为你自动同步和关闭 Session。你唯一要做的就是当发生异常时进行事务回滚。幸运的是， 在一个CMT bean中，事务回滚甚至可以由容器自动进行，因为由session bean方法抛出的未处理的 RuntimeException异常可以通知容器设置全局事务回滚。这意味着 在CMT中，你完全无需使用Hibernate的Transaction API 。 <br />请注意，当你配置Hibernate事务工厂的时候，在一个BMT session bean中，你应该选择 org.hibernate.transaction.JTATransactionFactory，在一个 CMT session bean中选择org.hibernate.transaction.CMTTransactionFactory。 记住，同时也要设置org.hibernate.transaction.manager_lookup_class。 <br />如果你使用CMT环境，并且让容器自动同步和关闭session，你可能也希望在你代码的不同部分使用 同一个session。一般来说，在一个非托管环境中，你可以使用一个ThreadLocal 变量来持有这个session，但是单个EJB方法调用可能会在不同的线程中执行（举例来说，一个session bean调用另一个session bean）。如果你不想在应用代码中被传递Session对 象实例的问题困扰的话，那么SessionFactory 提供的 getCurrentSession()方法就很适合你，该方法返回一个绑定到JTA事务 上下文环境中的session实例。这也是把Hibernate集成到一个应用程序中的最简单的方法！这个“当 前的”session总是可以自动同步和自动关闭（不考虑上述的属性设置）。我们的session/transaction 管理代码减少到如下所示： <br /><br /><table style="BORDER-TOP-WIDTH: 1px; BORDER-LEFT-WIDTH: 1px; BORDER-BOTTOM-WIDTH: 1px; BORDER-RIGHT-WIDTH: 1px" bordercolor="#e0e0e0" cellspacing="1" cellpadding="4" width="95%" align="center" border="1" table-layout:="" fixed=""><br /><tbody><br /><tr><br /><td style="HEIGHT: 25px; WORD-WRAP: break-word" valign="top" bgcolor="#f6f6f6"><font style="COLOR: #b0b0b0">代码内容</font><br />// CMT idiom <br />Session sess = factory.getCurrentSession(); <br /><br />// do some work <br />... </td></tr></tbody></table><br />换句话来说，在一个托管环境下，你要做的所有的事情就是调用 SessionFactory.getCurrentSession()，然后进行你的数据访问，把其余的工作 交给容器来做。事务在你的session bean的部署描述符中以可声明的方式来设置。session的生命周期完全 由Hibernate来管理。 <br />对after_statement连接释放方式有一个警告。因为JTA规范的一个很愚蠢的限制，Hibernate不可能自动清理任何未关闭的ScrollableResults 或者Iterator，它们是由scroll()或iterate()产生的。你must通过在finally块中，显式调用ScrollableResults.close()或者Hibernate.close(Iterator)方法来释放底层<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>游标。(当然，大部分程序完全可以很容易的避免在CMT代码中出现scroll()或iterate()。) <br /><br /><strong>12.2.3.异常处理</strong><br />如果 Session 抛出异常 (包括任何SQLException), 你应该立即回滚<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>事务，调用 Session.close() ，丢弃该 Session实例。Session的某些方法可能会导致session 处于不一致的状态。所有由Hibernate抛出的异常都视为不可以恢复的。确保在 finally 代码块中调用close()方法，以关闭掉 Session。 <br />HibernateException是一个非检查期异常（这不同于Hibernate老的版本）， 它封装了Hibernate持久层可能出现的大多数错误。我们的观点是，不应该强迫应用程序开发人员 在底层捕获无法恢复的异常。在大多数软件系统中，非检查期异常和致命异常都是在相应方法调用 的堆栈的顶层被处理的（也就是说，在软件上面的逻辑层），并且提供一个错误信息给应用软件的用户 （或者采取其他某些相应的操作）。请注意，Hibernate也有可能抛出其他并不属于 HibernateException的非检查期异常。这些异常同样也是无法恢复的，应该 采取某些相应的操作去处理。 <br />在和<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>进行交互时，Hibernate把捕获的SQLException封装为Hibernate的 JDBCException。事实上，Hibernate尝试把异常转换为更有实际含义 的JDBCException异常的子类。底层的SQLException可以 通过JDBCException.getCause()来得到。Hibernate通过使用关联到 SessionFactory上的SQLExceptionConverter来 把SQLException转换为一个对应的JDBCException 异常的子类。默认情况下，SQLExceptionConverter可以通过配置dialect 选项指定；此外，也可以使用用户自定义的实现类（参考javadocs SQLExceptionConverterFactory类来了解详情）。标准的 JDBCException子类型是： <br />JDBCConnectionException - 指明底层的JDBC通讯出现错误 <br />SQLGrammarException - 指明发送的SQL语句的语法或者格式错误 <br />ConstraintViolationException - 指明某种类型的约束违例错误 <br />LockAcquisitionException - 指明了在执行请求操作时，获取 所需的锁级别时出现的错误。 <br />GenericJDBCException - 不属于任何其他种类的原生异常 <br /><br /><strong>12.3.乐观并发控制(Optimistic concurrency control)</strong><br />唯一能够同时保持高并发和高可伸缩性的方法就是使用带版本化的乐观并发控制。版本检查使用版本号、 或者时间戳来检测更新冲突（并且防止更新丢失）。Hibernate为使用乐观并发控制的代码提供了三种可 能的方法，应用程序在编写这些代码时，可以采用它们。我们已经在前面应用程序长事务那部分展示了 乐观并发控制的应用场景，此外，在单个<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>事务范围内，版本检查也提供了防止更新丢失的好处。 <br />12.3.1.应用程序级别的版本检查(Application version checking) <br />未能充分利用Hibernate功能的实现代码中，每次和<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>交互都需要一个新的 Session，而且开发人员必须在显示数据之前从<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>中重 新载入所有的持久化对象实例。这种方式迫使应用程序自己实现版本检查来确保 应用程序事务的隔离，从数据访问的角度来说是最低效的。这种使用方式和 entity EJB最相似。 <br />// foo is an instance loaded by a previous Session <br />session = factory.openSession(); <br />Transaction t = session.beginTransaction(); <br />int oldVersion = foo.getVersion(); <br />session.load( foo, foo.getKey() ); // load the current state <br />if ( oldVersion!=foo.getVersion ) throw new StaleObjectStateException(); <br />foo.setProperty("bar"); <br />t.commit(); <br />session.close(); <br />version 属性使用 来映射，如果对象 是脏数据，在同步的时候，Hibernate会自动增加版本号。 <br />当然，如果你的应用是在一个低数据并发环境下，并不需要版本检查的话，你照样可以使用 这种方式，只不过跳过版本检查就是了。在这种情况下，最晚提交生效 （last commit wins）就是你的应用程序长事务的默认处理策略。 请记住这种策略可能会让应用软件的用户感到困惑，因为他们有可能会碰上更新丢失掉却没 有出错信息，或者需要合并更改冲突的情况。 <br />很明显，手工进行版本检查只适合于某些软件规模非常小的应用场景，对于大多数软件应用场景 来说并不现实。通常情况下，不仅是单个对象实例需要进行版本检查，整个被修改过的关 联对象图也都需要进行版本检查。作为标准设计范例，Hibernate使用长生命周期 Session的方式，或者脱管对象实例的方式来提供自动版本检查。 <br /><br /><strong>12.3.2.长生命周期session和自动版本化 </strong><br />单个 Session实例和它所关联的所有持久化对象实例都被用于整个 应用程序事务。Hibernate在同步的时候进行对象实例的版本检查，如果检测到并发修 改则抛出异常。由开发人员来决定是否需要捕获和处理这个异常（通常的抉择是给用户 提供一个合并更改，或者在无脏数据情况下重新进行业务操作的机会）。 <br />在等待用户交互的时候， Session 断开底层的JDBC连接。这种方式 以<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>访问的角度来说是最高效的方式。应用程序不需要关心版本检查或脱管对象实例 的重新关联，在每个<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>事务中，应用程序也不需要载入读取对象实例。 <br /><br /><table style="BORDER-TOP-WIDTH: 1px; BORDER-LEFT-WIDTH: 1px; BORDER-BOTTOM-WIDTH: 1px; BORDER-RIGHT-WIDTH: 1px" bordercolor="#e0e0e0" cellspacing="1" cellpadding="4" width="95%" align="center" border="1" table-layout:="" fixed=""><br /><tbody><br /><tr><br /><td style="HEIGHT: 25px; WORD-WRAP: break-word" valign="top" bgcolor="#f6f6f6"><font style="COLOR: #b0b0b0">代码内容</font><br />// foo is an instance loaded earlier by the Session <br />session.reconnect(); // Obtain a new JDBC connection <br />Transaction t = session.beginTransaction(); <br />foo.setProperty("bar"); <br />t.commit(); // End database transaction, flushing the change and checking the version <br />session.disconnect(); // Return JDBC connection </td></tr></tbody></table><br />foo 对象始终和载入它的Session相关联。 Session.reconnect()获取一个新的<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>连接（或者 你可以提供一个），并且继续当前的session。Session.disconnect() 方法把session与JDBC连接断开，把<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>连接返回到连接池（除非是你自己提供的数据 库连接）。在Session重新连接上<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>连接之后，你可以对任何可能被其他事务更新过 的对象调用Session.lock()，设置LockMode.READ 锁定模式，这样你就可以对那些你不准备更新的数据进行强制版本检查。此外，你并不需要 锁定那些你准备更新的数据。 <br />假若对disconnect()和reconnect()的显式调用发生得太频繁了，你可以使用hibernate.connection.release_mode来代替。 <br />如果在用户思考的过程中，Session因为太大了而不能保存，那么这种模式是有 问题的。举例来说，一个HttpSession应该尽可能的小。由于 Session是一级缓存，并且保持了所有被载入过的对象，因此 我们只应该在那些少量的request/response情况下使用这种策略。而且在这种情况下， Session 里面很快就会有脏数据出现，因此请牢牢记住这一建议。 <br />此外，也请注意，你应该让与<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>连接断开的Session对持久层保持 关闭状态。换句话说，使用有状态的EJB session bean来持有Session， 而不要把它传递到web层（甚至把它序列化到一个单独的层），保存在HttpSession中。 <br /><br /><strong>12.3.3.脱管对象(deatched object)和自动版本化 </strong><br />这种方式下，与持久化存储的每次交互都发生在一个新的Session中。 然而，同一持久化对象实例可以在多次与<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>的交互中重用。应用程序操纵脱管对象实例 的状态，这个脱管对象实例最初是在另一个Session 中载入的，然后 调用 Session.update()，Session.saveOrUpdate(), 或者 Session.merge() 来重新关联该对象实例。 <br /><br /><table style="BORDER-TOP-WIDTH: 1px; BORDER-LEFT-WIDTH: 1px; BORDER-BOTTOM-WIDTH: 1px; BORDER-RIGHT-WIDTH: 1px" bordercolor="#e0e0e0" cellspacing="1" cellpadding="4" width="95%" align="center" border="1" table-layout:="" fixed=""><br /><tbody><br /><tr><br /><td style="HEIGHT: 25px; WORD-WRAP: break-word" valign="top" bgcolor="#f6f6f6"><font style="COLOR: #b0b0b0">代码内容</font><br />// foo is an instance loaded by a previous Session <br />foo.setProperty("bar"); <br />session = factory.openSession(); <br />Transaction t = session.beginTransaction(); <br />session.saveOrUpdate(foo); // Use merge() if "foo" might have been loaded already <br />t.commit(); <br />session.close(); </td></tr></tbody></table><br />Hibernate会再一次在同步的时候检查对象实例的版本，如果发生更新冲突，就抛出异常。 <br />如果你确信对象没有被修改过，你也可以调用lock() 来设置 LockMode.READ（绕过所有的缓存，执行版本检查），从而取 代 update()操作。 <br /><br /><strong>12.3.4.定制自动版本化行为</strong><br />对于特定的属性和集合，通过为它们设置映射属性optimistic-lock的值 为false，来禁止Hibernate的版本自动增加。这样的话，如果该属性 脏数据，Hibernate将不再增加版本号。 <br />遗留系统的<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>Schema通常是静态的，不可修改的。或者，其他应用程序也可能访问同一数据 库，根本无法得知如何处理版本号，甚至时间戳。在以上的所有场景中，实现版本化不能依靠 <a href="http://www.gamvan.com/database/" target="_blank">数据库</a>表的某个特定列。在的映射中设置 optimistic-lock="all"可以在没有版本或者时间戳属性映射的情况下实现 版本检查，此时Hibernate将比较一行记录的每个字段的状态。请注意，只有当Hibernate能够比 较新旧状态的情况下，这种方式才能生效，也就是说， 你必须使用单个长生命周期Session模式，而不能使用 session-per-request-with-detached-objects模式。 <br />有些情况下，只要更改不发生交错，并发修改也是允许的。当你在 的映射中设置optimistic-lock="dirty"，Hibernate在同步的时候将只比较有脏 数据的字段。 <br />在以上所有场景中，不管是专门设置一个版本/时间戳列，还是进行全部字段/脏数据字段比较， Hibernate都会针对每个实体对象发送一条UPDATE（带有相应的 WHERE语句 ）的SQL语句来执行版本检查和数据更新。如果你对关联实体 设置级联关系使用传播性持久化（transitive persistence），那么Hibernate可能会执行不必 要的update语句。这通常不是个问题，但是<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>里面对on update点火 的触发器可能在脱管对象没有任何更改的情况下被触发。因此，你可以在 的映射中，通过设置select-before-update="true" 来定制这一行为，强制Hibernate SELECT这个对象实例，从而保证， 在更新记录之前，对象的确是被修改过。 <br /><br /><strong>12.4.悲观锁定(Pessimistic Locking)</strong><br />用户其实并不需要花很多精力去担心锁定策略的问题。通常情况下，只要为JDBC连接指定一下隔 离级别，然后让<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>去搞定一切就够了。然而，高级用户有时候希望进行一个排它的悲观锁定， 或者在一个新的事务启动的时候，重新进行锁定。 <br />Hibernate总是使用<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>的锁定机制，从不在内存中锁定对象！ <br />类LockMode 定义了Hibernate所需的不同的锁定级别。一个锁定 可以通过以下的机制来设置: <br />当Hibernate更新或者插入一行记录的时候，锁定级别自动设置为LockMode.WRITE。 <br />当用户显式的使用<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>支持的SQL格式SELECT ... FOR UPDATE 发送SQL的时候，锁定级别设置为LockMode.UPGRADE <br />当用户显式的使用Oracle<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>的SQL语句SELECT ... FOR UPDATE NOWAIT 的时候，锁定级别设置LockMode.UPGRADE_NOWAIT <br />当Hibernate在“可重复读”或者是“序列化”<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>隔离级别下读取数据的时候，锁定模式 自动设置为LockMode.READ。这种模式也可以通过用户显式指定进行设置。 <br />LockMode.NONE 代表无需锁定。在Transaction结束时， 所有的对象都切换到该模式上来。与session相关联的对象通过调用update() 或者saveOrUpdate()脱离该模式。 <br />"显式的用户指定"可以通过以下几种方式之一来表示: <br />调用 Session.load()的时候指定锁定模式(LockMode)。 <br />调用Session.lock()。 <br />调用Query.setLockMode()。 <br />如果在UPGRADE或者UPGRADE_NOWAIT锁定模式下调 用Session.load()，并且要读取的对象尚未被session载入过，那么对象 通过SELECT ... FOR UPDATE这样的SQL语句被载入。如果为一个对象调用 load()方法时，该对象已经在另一个较少限制的锁定模式下被载入了，那 么Hibernate就对该对象调用lock() 方法。 <br />如果指定的锁定模式是READ, UPGRADE 或 UPGRADE_NOWAIT，那么Session.lock()就 执行版本号检查。（在UPGRADE 或者UPGRADE_NOWAIT 锁定模式下，执行SELECT ... FOR UPDATE这样的SQL语句。） <br />如果<a href="http://www.gamvan.com/database/" target="_blank">数据库</a>不支持用户设置的锁定模式，Hibernate将使用适当的替代模式（而不是扔出异常）。 这一点可以确保应用程序的可移植性。 <br /><img src ="http://www.blogjava.net/William/aggbug/56034.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/William/" target="_blank">William</a> 2006-06-30 21:33 <a href="http://www.blogjava.net/William/archive/2006/06/30/56034.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于EasyDBO中事务管理的一些个人观点</title><link>http://www.blogjava.net/William/archive/2006/06/30/56031.html</link><dc:creator>William</dc:creator><author>William</author><pubDate>Fri, 30 Jun 2006 13:19:00 GMT</pubDate><guid>http://www.blogjava.net/William/archive/2006/06/30/56031.html</guid><wfw:comment>http://www.blogjava.net/William/comments/56031.html</wfw:comment><comments>http://www.blogjava.net/William/archive/2006/06/30/56031.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/William/comments/commentRss/56031.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/William/services/trackbacks/56031.html</trackback:ping><description><![CDATA[对数据库的操作无外乎CRUD，不管是多大规模的数据库，贯穿数据库操作的全部过程的就是数据库事务的管理。<br />一般情况下，我们对数据库的操作基本上都是基于默认的事务管理，就是说每次执行后都是自动提交，当然，<br />大部分的情况下这种操作可以正确执行响应的业务要求，但是对于一些业务数据要求比较严格的系统来说，处<br />理好程序中事务将是一个非常重要的内容。<br /><br />对于目前EasyDBO中事务处理的一些看法，不知道对不对，对这个研究不是很深入。<br /><br />首先我们可以看到EasyDBO是线程安全的，但是EasyJDBEngine是非线程安全的，每次实例话一个EasyDBO的时候，<br />都是重新实例化一个EasyJDBEngine。目前程序中通过EasyJDBEngine和DatabaseDAO实现的事务的支持，但是觉得<br />有一些地方欠妥，首先利用这个函数private void UpdateBatch(Vector vector)实现手动事务的提交，个人觉得<br />是没有真正利用到JDBC的事务管理功能，该函数只是简单的将所有的操作放到一起来统一执行，当一次要求执行的<br />操作特别多的时候未免效率低下了。<br /><br />另外一点我觉得在事务管理方面没有真正利用JDBC的事务管理，JDBC事务管理实际上是在JDBC Connection中实现的，<br />事务周期仅限于Connection的生命周期内（JTA才真正实现的夸Connection的事务管理），我们来看一个添加的函数<br /><br />    public boolean add(DBObject obj) { // 添加一个对象<br />       DatabaseDAO dba = null; // 引入数据库操作工具类<br />       String sql = sqlQuery.getInsertSql(obj);<br />       if (sql.equals("")) {<br />           logger.error("空的sql语句无法执行!");<br />           return false;<br />       }<br />       boolean ret = false;<br />       try {<br />           dba = getDatabaseDAO();<br />           if (DBOConfig.getInstance().isShow_sql()) {<br />               System.out.println("EasyDBO:" + sql);<br />           }<br />           dba.prepare(sql);<br />           if (obj.getValue() != null) {<br />               dba.setQueryParams(obj.getValue().values());<br />           }<br />           // todo : add piginzoo 加入事务支持<br />           if (autoCommit == false) {<br />               vec.add(dba.getPreparedStatement());<br />           } else {<br />               // rs = dba.preparedQuery();<br />               ret = dba.isPreparedUpdate();<br />               // dba.close();<br />           }<br />       } catch (Exception e) {<br />           logger.error("添加数据错误：" + e + ":sql=" + sql);<br />           e.printStackTrace();<br />           if (dba != null) {<br />               try {<br />                   dba.rollback();<br />                   // dba.close();<br />               } catch (SQLException sqle) {<br />               }<br />           }<br /><br />       } finally {<br />           try {<br />               dba.close();<br />           } catch (Exception e) {<br />               logger.error("释放数据库资源错误：" + e);<br />           }<br />       }<br />   <br />       return ret;<br />   }<br />   <br />       <br />       可以发现在一次的添加结束以后，连接被关掉了dba.close();虽然连接没有被真正的关掉，只是返回了连接池当中；<br />       但是你肯定不能确定如果同一次执行多个操作的时候，能够利用的同一个Connection，所以说JDBC的事务也就无从<br />       说起了。<br /><br />       当然我看到现在程序在实际应用中可能还没有真正利用到这个手动事务管理的功能，但是我觉得这样设计事务好像<br />       有点效率上的问题，当然最重要的是没有真正利用到JDBC 的事务管理功能。 <br /><img src ="http://www.blogjava.net/William/aggbug/56031.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/William/" target="_blank">William</a> 2006-06-30 21:19 <a href="http://www.blogjava.net/William/archive/2006/06/30/56031.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>