<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>fengzl</title>
    <description></description>
    <link>http://fengzl.javaeye.com</link>
    <language>UTF-8</language>
    <copyright>Copyright 2003-2008, JavaEye.com</copyright>
    <docs>http://blogs.law.harvard.edu/tech/rss</docs>
    <generator>JavaEye - 做最棒的软件开发交流社区</generator>
      <item>
        <title>用MySQL-Proxy实现读写分离</title>
        <author>fengzl</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://fengzl.javaeye.com">fengzl</a>&nbsp;
          链接：<a href="http://fengzl.javaeye.com/blog/205866" style="color:red;">http://fengzl.javaeye.com/blog/205866</a>&nbsp;
          发表时间: 2008年06月19日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <a href="http://www.infoq.com/cn/news/2007/10/mysqlproxyrwsplitting" target="_blank">http://www.infoq.com/cn/news/2007/10/mysqlproxyrwsplitting</a>
          <br/>
          <span style="color:red;">
            <a href="http://fengzl.javaeye.com/blog/205866#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 19 Jun 2008 16:26:00 +0800</pubDate>
        <link>http://fengzl.javaeye.com/blog/205866</link>
        <guid>http://fengzl.javaeye.com/blog/205866</guid>
      </item>
      <item>
        <title>Slony</title>
        <author>fengzl</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://fengzl.javaeye.com">fengzl</a>&nbsp;
          链接：<a href="http://fengzl.javaeye.com/blog/205823" style="color:red;">http://fengzl.javaeye.com/blog/205823</a>&nbsp;
          发表时间: 2008年06月19日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <a href="http://blog.csdn.net/iori13/archive/2007/07/31/1719380.aspx" target="_blank">http://blog.csdn.net/iori13/archive/2007/07/31/1719380.aspx</a><br />http://hi.baidu.com/top2top/blog/item/547ea8ece523d139279791b4.html<br />--------------------------------------------------------------------------<br />参考文档<br />http://bbs.chinaunix.net/viewthr ... page%3D1#pid6997819<br /><br />http://topic.csdn.net/t/20050617/12/4089010.html<br />postgresql 数据同步<br />slony1是基于postgresql的异步通知机制做的复制技术， 其同步速度非常快。  采用这个复制技术来做备份，呵呵，除了配置稍微复杂点，非常好用！   <br />   <br />1. 总体需求<br />1.1. 当前情况<br />随着软件系统复杂度的提高，分布式部署成为软件部署流行的方式。对于系统的结构，<br />程序和数据是支撑系统的两大要素。程序的分布式部署已经有很多很好的方案，在这里<br />我说一下数据的分布式部署。数据的分布式部署其实就是数据库的分布式部署。<br />1.2. 系统环境<br />在这里，我以下面的环境详细讲部署的过程。<br />主数据库服务器 (master)<br />RHEL3<br />ip 192.168.0.200<br />mask 255.255.255.0<br /><br />从数据库服务器 (slave)<br />RHEL3<br />ip 192.168.30.201<br />mask 255.255.255.0<br />需要保证两台机器互连互通.<br />请到指定目的地下载指定的软件包:<br />db postgresql 8.2.4.tar.gz(http://www.postgresql.org/download/)<br />slony1 slony1-1.2.6.tar(http://www.postgresql.org/download/)<br />以上的网址是一个入口地址，请选择合适的正确的源程序包。<br />1.3. 系统安装<br />1.3.1 主数据库服务器<br />1.3.1.1 安装数据库<br />Groupadd postgres<br />Useradd postgres –g postgres –d /home/postgres<br />解压,命令 tar -xvzf postgresql 8.2.4.tar.gz<br />进入对应的 postgresql-8.2.4 目录 ,命令 ：cd postgresql-8.2.4<br />./configure  --prefix=/usr/local/pgsql –localstatedir=/home/postgres/data<br />gmake,命令: gmake<br />注意，是 gmake<br />安装，命令 gmake install<br />为 postgres 用户增加环境参数如下：<br />Vi /home/postgres/.bash_profile<br />PGLIB=/usr/local/pgsql/lib<br />PGDATA=/test/spescso/data<br />PATH=$PATH:/usr/local/pgsql/bin<br />MANPATH=$MANPATH:/usr/local/pgsql/man<br />export PGLIB PGDATA PATH MANPATH<br />chown postgres.postgres /usr/local/pgsql –R<br />Su – postgres<br />Mkdir data<br />使用 postgres 创建 数据库集群<br />/usr/local/pgsql/bin/initdb -E UTF-8 /home/postgres/data/<br />/usr/local/pgsql/bin/createuser -a -d ssuser<br />/usr/local/pgsql/bin/createlang plpgsql template1<br />使用 postgres 创建 log 目录<br />mkdir /test/spescso/data/log<br />修改 /test/spescso/data/postgresql.conf 文件<br />主要配置日志文件<br />log_destination = 'stderr'<br />redirect_stderr = true<br />log_directory = '/test/spescso/data/log/'<br />log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'<br />修改 /test/spescso/data/pg_hba.conf,主要搞定双机相互认证问题，否则不能相互访问<br /># TYPE DATABASE USER CIDR-ADDRESS METHOD<br /># "local" is for Unix domain socket connections smilieid="44" border="0" alt="" />);<br /><br />在主数据库机器上依次建立 testslave1,testslave2.<br />在从数据库机器上 5431 端口建立 testslave3<br />保证testslave1/testslave2/testsalve3中建立和test中相同的表tb_depart/tb_manager/tb_user……..<br /><br />1.4 配置同步<br />1.4.1. 主机配置<br />编写 configmaster sheul 脚本文件,设置其可执行属性 ,命令 chmod a+x+w+r configmaster,文件内容如下：<br />#!/bin/bash<br />basebin=/usr/local/pgsql/bin<br />#集群名称<br />CLUSTER=slony_test1<br /># 参与同步的数据库名称,master db 是test,其他三个是slave<br />DBSERVER=test<br />DBSLAVE1=testslave1<br />DBSLAVE2=testslave2<br />DBSLAVE3=testslave3<br /># 参与同步的机器地址<br />HOSTSERVER=192.168.0.200<br />HOSTSLAVE1=192.168.0.200<br />HOSTSLAVE2=192.168.0.200<br />HOSTSLAVE3=192.168.0.201<br /># 参与同步的数据库用户名<br />DBSERVER_USER=ssuser<br />DBSLAVE1_USER=ssuser<br />DBSLAVE2_USER=ssuser<br />DBSLAVE3_USER=ssuser<br /># 同步发布配置,以下是命令 slonik 的参数<br />$basebin/slonik&lt;&lt;_EOF_<br />cluster name=$CLUSTER;<br /># 定义复制节点<br />node 1 admin conninfo='dbname=$DBSERVER host=$HOSTSERVER user=$DBSERVER_USER port=5432';<br />node 2 admin conninfo='dbname=$DBSLAVE1 host=$HOSTSLAVE1 user=$DBSLAVE1_USER port=5432';<br />node 3 admin conninfo='dbname=$DBSLAVE2 host=$HOSTSLAVE2 user=$DBSLAVE2_USER port=5432';<br />node 4 admin conninfo='dbname=$DBSLAVE3 host=$HOSTSLAVE3 user=$DBSLAVE3_USER port=5431';<br /># 初始化集群,id从1开始<br />init cluster ( id=1, comment='Node 1' );<br /># 设置参与同步的数据表<br />#先创建一个复制集,id也是从1开始<br />#向自己的复制集种添加表,每个需要复制的表一条set命令<br />#id从1开始,逐次递加,步进为1；<br />#fully qualified name是表的全称：模式名.表名<br />#这里的复制集id需要和前面创建的复制集id一致<br />create set ( id=1, origin=1, comment='All test tables' );<br />set add table ( set id=1, origin=1,id=1, fully qualified name='public.tb_depart',comment='Table tb_depart' );<br />set add table ( set id=1, origin=1,id=2, fully qualified name='public.tb_user',comment='Table tb_user' );<br />set add table ( set id=1, origin=1,id=3, fully qualified name='public.tb_manager',comment='Table tb_manager' );<br />#假如某个表没有主键,但是有唯一键字,那么可以用key关键字<br />#指定其为复制键字,如下面的key参数<br />#set add table ( set id = 1, origin = 1,id = 4, fully qualified name = 'public.history',key = "column",comment = 'Table history' );<br />#对于没有唯一列的表,需要这样处理,这一句放在 create set 的前面<br />#table add key (node id = 1, fully qualified name = 'public.history');<br /># 这样设置结果集<br />#set add table (set id=1, origin=1, id=4, fully qualified name = 'public.history', comment='history table', key = serial);<br /># 设置存储节点<br />store node ( id=2, comment='Node 2' );<br />store node ( id=3, comment='Node 3' );<br />store node ( id=4, comment='Node 4' );<br /># 设置存储路径<br />store path ( server=1, client=2,conninfo='dbname=$DBSERVER host=$HOSTSERVER user=$DBSERVER_USER port=5432');<br />store path ( server=2, client=1,conninfo='dbname=$DBSLAVE1 host=$HOSTSLAVE1 user=$DBSLAVE1_USER port=5432');<br />store path ( server=1, client=3,conninfo='dbname=$DBSERVER host=$HOSTSERVER user=$DBSERVER_USER port=5432');<br />store path ( server=3, client=1,conninfo='dbname=$DBSLAVE2 host=$HOSTSLAVE2 user=$DBSLAVE2_USER port=5432');<br />store path ( server=1, client=4,conninfo='dbname=$DBSERVER host=$HOSTSERVER user=$DBSERVER_USER port=5432');<br />store path ( server=4, client=1,conninfo='dbname=$DBSLAVE3 host=$HOSTSLAVE3 user=$DBSLAVE3_USER port=5431');<br />#设置侦听事件和订阅方向,复制中角色,主节点是原始提供者,从节点是接受者<br />store listen ( origin=1, provider=1, receiver=2 );<br />store listen ( origin=2, provider=2, receiver=1 );<br />store listen ( origin=1, provider=1, receiver=3 );<br />store listen ( origin=3, provider=3, receiver=1 );<br />store listen ( origin=1, provider=1, receiveR=4 );<br />store listen ( origin=4, provider=4, receiver=1 );<br />_EOF_<br />1.4.2. 提交数据集合<br />编写 commitdata shell 脚本文件，赋予可执行权限，内容如下：<br />#!/bin/bash<br />basebin=/usr/local/pgsql/bin<br />CLUSTER=slony_test1<br />DBSERVER=test<br />DBSLAVE1=testslave1<br />DBSLAVE2=testslave2<br />DBSLAVE3=testslave3<br />HOSTSERVER=192.168.0.200<br />HOSTSLAVE1=192.168.0.200<br />HOSTSLAVE2=192.168.0.200<br />HOSTSLAVE3=192.168.0.201<br />DBSERVER_USER=ssuser<br />DBSLAVE1_USER=ssuser<br />DBSLAVE2_USER=ssuser<br />DBSLAVE3_USER=ssuser<br />$basebin/slonik&lt;&lt;_EOF_<br />cluster name=$CLUSTER;<br />#提供连接参数<br />node 1 admin conninfo='dbname=$DBSERVER host=$HOSTSERVER user=$DBSERVER_USER port=5432';<br />node 2 admin conninfo='dbname=$DBSLAVE1 host=$HOSTSLAVE1 user=$DBSLAVE1_USER port=5432';<br />node 3 admin conninfo='dbname=$DBSLAVE2 host=$HOSTSLAVE2 user=$DBSLAVE2_USER port=5432';<br />node 4 admin conninfo='dbname=$DBSLAVE3 host=$HOSTSLAVE3 user=$DBSLAVE3_USER port=5431';<br /># 提交订阅复制集<br />subscribe set ( id=1, provider=1, receiver=2, forward=no);<br />subscribe set ( id=1, provider=1, receiver=3, forward=no);<br />subscribe set ( id=1, provider=1, receiver=4, forward=no);<br />_EOF_<br />1.4.3. 配置同步过程<br />在主机上执行配置命令 ./configmaster<br />在主机上开启slon 后台进程,启动主数据库复制,命令<br />/usr/local/pgsql/bin/slon slony_test1 "dbname=test host=192.168.0.200 user=ssuser port=5432" &<br />在主机上开启slon 后台进程,启动第一个从数据库复制,命令<br />/usr/local/pgsql/bin/slon slony_test1 "dbname=testslave1 host=192.168.0.200 user=ssuser port=5432" &<br />在主机上开启slon 后台进程,启动第二个从数据库复制,命令<br />/usr/local/pgsql/bin/slon slony_test1 "dbname=testslave2 host=192.168.0.200 user=ssuser port=5432" &<br />在从机上开启slon 后台进程,启动第三个从数据库复制,命令<br />/usr/local/pgsql/bin/slon slony_test1 "dbname=testslave3 host=192.168.0.201 user=ssuser port=5431" &<br />在主机上执行提交命令 ./commitdata<br />-------------------------------------------------------------------------------
          <br/>
          <span style="color:red;">
            <a href="http://fengzl.javaeye.com/blog/205823#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 19 Jun 2008 15:07:55 +0800</pubDate>
        <link>http://fengzl.javaeye.com/blog/205823</link>
        <guid>http://fengzl.javaeye.com/blog/205823</guid>
      </item>
      <item>
        <title>LD 字符串比较算法</title>
        <author>fengzl</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://fengzl.javaeye.com">fengzl</a>&nbsp;
          链接：<a href="http://fengzl.javaeye.com/blog/172145" style="color:red;">http://fengzl.javaeye.com/blog/172145</a>&nbsp;
          发表时间: 2008年03月15日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          编程算法<br />比较字符串（String）相似程度的算法。<br />LD(s1, s2)=把s1变成s2需要的{增加，删除，替换}操作之和。<br /><br />汉字的特殊处理：http://www.cnitblog.com/ictfly/archive/2005/12/27/5828.aspx<br /><br />&lt;html><br />&lt;head><br />&lt;title>求两个字符串的相似度,Levenshtein Distance算法实现&lt;/title><br />&lt;/head><br />&lt;body><br />&lt;h3>求两个字符串的相似度,Levenshtein Distance算法实现&lt;/h3><br />字符串1:&lt;input id="s" type="text" value="小谢天空">&lt;br><br />字符串2:&lt;input id="t" type="text" value="小谢的天空">&lt;br><br />&lt;input type="button" value="计算" onclick="test()">&lt;br><br />&lt;div id="r">&lt;/div><br />&lt;/body><br />&lt;/html><br />&lt;script><br />//求两个字符串的相似度,返回差别字符数,Levenshtein Distance算法实现<br />function Levenshtein_Distance(s,t){<br /> var n=s.length;// length of s<br /> var m=t.length;// length of t<br /> var d=[];// matrix<br /> var i;// iterates through s<br /> var j;// iterates through t<br /> var s_i;// ith character of s<br /> var t_j;// jth character of t<br /> var cost;// cost<br /><br /> // Step 1<br /><br /> if (n == 0) return m;<br /> if (m == 0) return n;<br /><br /> // Step 2<br /><br /> for (i = 0; i &lt;= n; i++) {<br />  d[i]=[];<br />  d[i][0] = i;<br /> }<br /><br /> for (j = 0; j &lt;= m; j++) {<br />  d[0][j] = j;<br /> }<br /><br /> // Step 3<br /><br /> for (i = 1; i &lt;= n; i++) {<br /><br />  s_i = s.charAt (i - 1);<br />  <br />  // Step 4<br />  <br />  for (j = 1; j &lt;= m; j++) {<br /><br />   t_j = t.charAt (j - 1);<br /><br />   // Step 5<br /><br />   if (s_i == t_j) {<br />    cost = 0;<br />   }else{<br />    cost = 1;<br />   }<br /><br />   // Step 6<br />   <br />   d[i][j] = Minimum (d[i-1][j]+1, d[i][j-1]+1, d[i-1][j-1] + cost);<br />  }<br /> }<br /><br /> // Step 7<br /><br /> return d[n][m];<br />}<br /><br />//求两个字符串的相似度,返回相似度百分比<br />function Levenshtein_Distance_Percent(s,t){<br /> var l=s.length>t.length?s.length:t.length;<br /> var d=Levenshtein_Distance(s,t);<br /> return (1-d/l).toFixed(4);<br />}<br /><br />//求三个数字中的最小值<br />function Minimum(a,b,c){<br /> return a&lt;b?(a&lt;c?a:c):(b&lt;c?b:c);<br />}<br /><br />function test(){<br /> var s=document.getElementById("s");<br /> var t=document.getElementById("t");<br /> var r=document.getElementById("r");<br /> var l=Levenshtein_Distance_Percent(s.value,t.value);<br /> r.innerHTML='字符串1:'+s.value+'&lt;br>字符串2:'+t.value+'&lt;br>'+'相似度:'+l+'&lt;br>算法参考:&lt;a href="http://www.merriampark.com/ld.htm" target="_blank">http://www.merriampark.com/ld.htm&lt;/a>';<br />}<br />&lt;/script>
          <br/>
          <span style="color:red;">
            <a href="http://fengzl.javaeye.com/blog/172145#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sat, 15 Mar 2008 14:41:31 +0800</pubDate>
        <link>http://fengzl.javaeye.com/blog/172145</link>
        <guid>http://fengzl.javaeye.com/blog/172145</guid>
      </item>
      <item>
        <title>自己实现的观察者模式</title>
        <author>fengzl</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://fengzl.javaeye.com">fengzl</a>&nbsp;
          链接：<a href="http://fengzl.javaeye.com/blog/169371" style="color:red;">http://fengzl.javaeye.com/blog/169371</a>&nbsp;
          发表时间: 2008年03月08日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          JAVA标准库实现<br /><pre name="code" class="java">
package com.moshi.observer;

import java.util.Date;
import java.util.Observable;

public class Product extends Observable  {
	
	private Long id;
	
	private String name;
	
	private Date endDate;
	
	private Integer price;
	
	

	public Product(Long id, String name, Integer price, Date date) {
		super();
		// TODO Auto-generated constructor stub
		endDate = date;
		this.id = id;
		this.name = name;
		this.price = price;
	}

	public Date getEndDate() {
		return endDate;
	}

	public void setEndDate(Date endDate) {
		this.endDate = endDate;
	}

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Integer getPrice() {
		return price;
	}

	public void setPrice(Integer price) {
		this.setChanged();//修改价格通知 第一次
		notifyObservers(this.price);
		this.price = price;
		setChanged();//修改价格通知 第二次
		notifyObservers(price);
	}
	
	

}

</pre><br /><br /><pre name="code" class="java">package com.moshi.observer;

import java.util.Observable;
import java.util.Observer;

public class ProductObserver implements Observer {

	public void update(Observable arg, Object obj) {
		// TODO Auto-generated method stub
		System.out.println(arg.getClass().toString());
		if(obj instanceof Integer){
			System.out.println("产品:" + ((Product)arg).getName() +",最新价格修改为" + obj.toString());
		}

	}

}
</pre><br /><pre name="code" class="java">
package com.moshi.observer;

public class Test {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Product product = new Product(1L, "鞋", 1,new java.util.Date());
		product.addObserver(new ProductObserver());
		product.setPrice(2);
	}

}

</pre><br /><br />我自己的实现，其实原理很简单<br /><pre name="code" class="java">package org.moshi.observer;

import java.util.Observable;

/**
 * 观察对象的基础接口，所有观察者统一实现
 * @author Administrator
 *
 */
public interface ObsInterface {
	
	public void update(Object obj1, Object obj);

}

</pre><br /><br /><pre name="code" class="java">
package org.moshi.observer;

import java.util.ArrayList;
import java.util.List;

/**
 * 继承该对象便拥有了被观察的能力
 * @author Administrator
 *
 */
public class ObserverBean {
	
	private boolean able = false;
	
	private List&lt;ObsInterface> observers = new ArrayList&lt;ObsInterface>();
	
	public void addObserver(ObsInterface o){
		observers.add(o);
	}
	
	public void setChanged(){
		this.able = true;
	}
	
	public void notifyObservers(Object obj1,Object obj2){
		if(this.able){
			for(ObsInterface o: observers){
				o.update(obj1,obj2);
			}
		}
		this.able = false;
	}
	
	public void notifyObservers(Object obj2){
		notifyObservers(this,obj2);
	}

}

</pre><br /><pre name="code" class="java">
package org.moshi.observer;

import java.util.Date;

public class Product extends ObserverBean{
	private Long id;
	
	private String name;
	
	private Date endDate;
	
	private Integer price;
	
	

	public Product(Long id, String name, Integer price, Date date) {
		super();
		// TODO Auto-generated constructor stub
		endDate = date;
		this.id = id;
		this.name = name;
		this.price = price;
	}

	public Date getEndDate() {
		return endDate;
	}

	public void setEndDate(Date endDate) {
		this.endDate = endDate;
	}

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Integer getPrice() {
		return price;
	}

	public void setPrice(Integer price) {
		this.setChanged();//修改价格通知
		notifyObservers(this.price);
		this.price = price;
		this.setChanged();//修改价格通知
		notifyObservers(2);
	}
}

</pre><br /><pre name="code" class="java">package org.moshi.observer;

import org.moshi.observer.Product;
/**
*实现观察者接口
*/
public class ProductObserver implements ObsInterface {

	public void update(Object obj1, Object obj) {
		// TODO Auto-generated method stub
		System.out.println(obj1.getClass().toString());
		if(obj instanceof Integer){
			System.out.println("产品:" + ((Product)obj1).getName() +",最新价格修改为" + obj.toString());
		}		

	}

}</pre>
          <br/>
          <span style="color:red;">
            <a href="http://fengzl.javaeye.com/blog/169371#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sat, 08 Mar 2008 16:17:42 +0800</pubDate>
        <link>http://fengzl.javaeye.com/blog/169371</link>
        <guid>http://fengzl.javaeye.com/blog/169371</guid>
      </item>
      <item>
        <title>JMS初探</title>
        <author>fengzl</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://fengzl.javaeye.com">fengzl</a>&nbsp;
          链接：<a href="http://fengzl.javaeye.com/blog/169303" style="color:red;">http://fengzl.javaeye.com/blog/169303</a>&nbsp;
          发表时间: 2008年03月08日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          lib:<br />jms1.1.jar<br />activemq-all-5.0.jar<br /><br />首先启动 activemq.bat或者执行以下代码启动一个broker<br /><pre name="code" class="java">
import org.apache.activemq.broker.BrokerService;

/**
 * This example demonstrates how to run an embedded broker inside your Java code
 * 
 * @version $Revision: 565003 $
 */
public final class EmbeddedBroker {

    private EmbeddedBroker() {
    }

    public static void main(String[] args) throws Exception {
        BrokerService broker = new BrokerService();
        broker.setUseJmx(true);
        broker.addConnector("tcp://localhost:61616");
        broker.start();

        // now lets wait forever to avoid the JVM terminating immediately
        Object lock = new Object();
        synchronized (lock) {
            lock.wait();
        }
    }
}

</pre><br /><br />消费端<br /><pre name="code" class="java">
package com.jms;

import javax.jms.Connection;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.Session;
import javax.jms.TextMessage;

import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;

public class ConsumerTool implements MessageListener {
	
	 private String user = ActiveMQConnection.DEFAULT_USER;

	 private String password = ActiveMQConnection.DEFAULT_PASSWORD;

	 private String url = ActiveMQConnection.DEFAULT_BROKER_URL;

	 private String subject = "TOOL.DEFAULT";

	 private Destination destination = null;

	 private Connection connection = null;

	 private Session session = null;

	 private MessageConsumer consumer = null;

	 //初始化
	 private void initialize() throws JMSException, Exception{
		 ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(user, password, url);
		 connection = connectionFactory.createConnection();
		 session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
		 destination = session.createQueue(subject);
		 consumer = session.createConsumer(destination);
	 }

	 //消费消息
	 public void consumeMessage() throws JMSException, Exception{
		 initialize();
		 connection.start();

		 System.out.println("Consumer:->Begin listening...");
		 //开始监听
		 consumer.setMessageListener(this);
		 //Message message = consumer.receive();
	 }

	 //关闭连接
	 public void close() throws JMSException{
		 System.out.println("Consumer:->Closing connection");
		 if (consumer != null)	 consumer.close();
		 if (session != null)	 session.close();
		 if (connection != null)	 connection.close();
	 }	

	public void onMessage(Message message) {
		try{
			if (message instanceof TextMessage){
				TextMessage txtMsg = (TextMessage) message;
				String msg = txtMsg.getText();
				System.out.println("Consumer:->Received: " + msg);
			} else{
				System.out.println("Consumer:->Received: " + message);
			}
		} catch (JMSException e){
			e.printStackTrace();
		}
	}

}

</pre><br /><br />producter:<br /><pre name="code" class="java">
package com.jms;

import javax.jms.Connection;
import javax.jms.DeliveryMode;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;

import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;

public class ProducerTool {
	
	 private String user = ActiveMQConnection.DEFAULT_USER;

	 private String password = ActiveMQConnection.DEFAULT_PASSWORD;

	 private String url = ActiveMQConnection.DEFAULT_BROKER_URL;

	 private String subject = "TOOL.DEFAULT";

	 private Destination destination = null;

	 private Connection connection = null;

	 private Session session = null;

	 private MessageProducer producer = null;	

	 //初始化
	 private void initialize() throws JMSException, Exception{
		 ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(user, password, url);
		 connection = connectionFactory.createConnection();
		 session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
		 destination = session.createQueue(subject);
		 producer = session.createProducer(destination);
		 producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
	 }
	 
	 //发送消息
	 public void produceMessage(String message) throws JMSException, Exception{
		 initialize();
		 TextMessage msg = session.createTextMessage(message);
		 connection.start();
		 System.out.println("Producer:->Sending message: " + message);
		 producer.send(msg);
		 System.out.println("Producer:->Message sent complete!");
	 }
	 
	 //关闭连接
	 public void close() throws JMSException{
		 System.out.println("Producer:->Closing connection");
		 if (producer != null) producer.close();
		 if (session != null) session.close();
		 if (connection != null) connection.close();
	 }	 

}

</pre><br /><br />下面是一个Broker实现，不属于同一个例子！<br /><pre name="code" class="java">
package com.jms;

import java.io.IOException;
import java.util.Hashtable;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MessageBroker extends HttpServlet
{
	private ConnectionFactory confactory=null;
	private Connection jmsCon=null;
    private Destination dest=null;
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException 
	{
		this.doPost(req, resp);
	}

	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException 
	{
		Session session;
		try 
		{
			String deptId=null;
			String deptName=null;
			deptId=req.getParameter("deptId");
			deptName=req.getParameter("deptName");
			session = this.jmsCon.createSession(false, Session.CLIENT_ACKNOWLEDGE);
			MessageProducer msgProducer=session.createProducer(dest);
			Message textMsg=session.createTextMessage();
			textMsg.setStringProperty("deptId", deptId);
			textMsg.setStringProperty("deptName", deptName);
			msgProducer.send(textMsg);
			session.close();
			resp.sendRedirect("http://localhost/JmsTestWeb2/");
		}
		catch (Exception e)
        {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
    /**
     * 
     */
	public void init(ServletConfig config) throws ServletException 
	{
		super.init(config);
		try 
		{
			this.confactory=this.getConnectionFactoryFromLdap();
			this.dest=this.getDestinationFromLdap();
			this.jmsCon=this.confactory.createConnection();
			/** 开启一个会话来创建一个消息消费者，异步监听到来的消息。*/
			Session session=jmsCon.createSession(false, Session.CLIENT_ACKNOWLEDGE);
			MessageConsumer msgConsumer=session.createConsumer(this.dest);
			MessageListenerForOrgMsg msgListener=new MessageListenerForOrgMsg();
			msgConsumer.setMessageListener(msgListener);
			/** 开启另一个会话来创建另外一个消息消费者，异步监听到来的消息。*/			 
			Session session2=jmsCon.createSession(false, Session.CLIENT_ACKNOWLEDGE);
			MessageConsumer msgConsumer2=session2.createConsumer(this.dest);
			MessageListenerForOrgMsg2 msgListener2=new MessageListenerForOrgMsg2();
			msgConsumer2.setMessageListener(msgListener2);
			this.jmsCon.start();	         
		} 
		catch (Exception e) 
		{
			e.printStackTrace();
		}
	}
	/**
	 * 
	 * @return
	 * @throws NamingException
	 */
	private ConnectionFactory getConnectionFactoryFromLdap() throws NamingException
	{
		   String account="uid=admin,ou=administrators,ou=topologymanagement,o=netscaperoot";//操作LDAP的帐户。默认就是Admin。
	       String password="111111" ;//帐户Admin的密码。
	       String root="ou=jmsstore,dc=xindongfang,dc=com"; //所操作的WLS域。也就是LDAP的根节点的DC
	       Hashtable env = new Hashtable();
	       env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");//必须这样写，无论用什么LDAP服务器。
	       env.put(Context.PROVIDER_URL, "ldap://192.168.0.15:2922/" + root);//LDAP服务器的地址:端口。对WLS端口就是7001
	       env.put(Context.SECURITY_AUTHENTICATION, "simple");//授权界别，可以有三种授权级别，但是如果设为另外两种都无法登录，我也不知道为啥，但是只能设成这个值"none"。
	       env.put(Context.SECURITY_PRINCIPAL, account );//载入登陆帐户和登录密码
	       env.put(Context.SECURITY_CREDENTIALS, password);
	       InitialContext ctx=null;
	       ConnectionFactory conFacotry=null;
	       try
	       {
	    	   ctx = new InitialContext(env);//初始化上下文
	    	   conFacotry=(ConnectionFactory)ctx.lookup("cn=topicconfac");
	    	   System.out.println("get Connection factory success");
	    	   return conFacotry;
	       }
	      
	       finally
	       {
	    	   if (ctx!=null) ctx.close();   
	       }
	  }
	 /**
	  * 
	  * @return
	  * @throws NamingException
	  */
	  private Destination getDestinationFromLdap() throws NamingException
	  {
		   String account="uid=admin,ou=administrators,ou=topologymanagement,o=netscaperoot";//操作LDAP的帐户。默认就是Admin。
	       String password="111111" ;//帐户Admin的密码。
	       String root="ou=jmsstore,dc=xindongfang,dc=com"; //所操作的WLS域。也就是LDAP的根节点的DC
	       Hashtable env = new Hashtable();
	       env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");//必须这样写，无论用什么LDAP服务器。
	       env.put(Context.PROVIDER_URL, "ldap://192.168.0.15:2922/" + root);//LDAP服务器的地址:端口。对WLS端口就是7001
	       env.put(Context.SECURITY_AUTHENTICATION, "simple");//授权类别，可以有三种授权级别，但是如果设为另外两种都无法登录，我也不知道为啥，但是只能设成这个值"none"。
	       env.put(Context.SECURITY_PRINCIPAL, account );//载入登陆帐户和登录密码
	       env.put(Context.SECURITY_CREDENTIALS, password);
	       InitialContext ctx=null;
	       Destination dst=null;
	       try
	       {
	    	   ctx = new InitialContext(env);//初始化上下文
	    	   dst=(Destination)ctx.lookup("cn=orgmsg");
	    	   System.out.println("get destination success");
	    	   return dst;   
	       }
	       finally
	       {
	    	   if (ctx!=null) ctx.close();
	       }
	   }

	public void destroy() 
	{
		if (this.jmsCon!=null)
		{
			try 
			{
			   this.jmsCon.close();
			} 
			catch (JMSException e) 
			{
   			   e.printStackTrace();
			}
		}
		super.destroy();
	}
}

</pre>
          <br/>
          <span style="color:red;">
            <a href="http://fengzl.javaeye.com/blog/169303#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sat, 08 Mar 2008 11:10:46 +0800</pubDate>
        <link>http://fengzl.javaeye.com/blog/169303</link>
        <guid>http://fengzl.javaeye.com/blog/169303</guid>
      </item>
      <item>
        <title>JAVA多线程</title>
        <author>fengzl</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://fengzl.javaeye.com">fengzl</a>&nbsp;
          链接：<a href="http://fengzl.javaeye.com/blog/164346" style="color:red;">http://fengzl.javaeye.com/blog/164346</a>&nbsp;
          发表时间: 2008年02月22日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <a href="http://lavasoft.blog.51cto.com/62575/27069" target="_blank">http://lavasoft.blog.51cto.com/62575/27069</a><br /><br />处理比较好时间的操作的另一种方法就是多线程,把耗时的操作仍个另一个线程去做,被开启的线程可能在主线程之后完成,不能保证操作的完整性,但是可以节省主线程的反应时间<br /><br />Java多线程编程总结<br /> <br />一、认识多任务、多进程、单线程、多线程<br />要认识多线程就要从操作系统的原理说起。<br /> <br />以前古老的DOS操作系统（V 6.22）是单任务的，还没有线程的概念，系统在每次只能做一件事情。比如你在copy东西的时候不能rename文件名。为了提高系统的利用效率，采用批处理来批量执行任务。<br /> <br />现在的操作系统都是多任务操作系统，每个运行的任务就是操作系统所做的一件事情，比如你在听歌的同时还在用MSN和好友聊天。听歌和聊天就是两个任务，这个两个任务是“同时”进行的。一个任务一般对应一个进程，也可能包含好几个进程。比如运行的MSN就对应一个MSN的进程，如果你用的是 windows系统，你就可以在任务管理器中看到操作系统正在运行的进程信息。<br /> <br />一般来说，当运行一个应用程序的时候，就启动了一个进程，当然有些会启动多个进程。启动进程的时候，操作系统会为进程分配资源，其中最主要的资源是内存空间，因为程序是在内存中运行的。在进程中，有些程序流程块是可以乱序执行的，并且这个代码块可以同时被多次执行。实际上，这样的代码块就是线程体。线程是进程中乱序执行的代码流程。当多个线程同时运行的时候，这样的执行模式成为并发执行。<br /> <br />多线程的目的是为了最大限度的利用CPU资源。<br /> <br />Java编写程序都运行在在Java虚拟机（JVM）中，在JVM的内部，程序的多任务是通过线程来实现的。每用java命令启动一个java 应用程序，就会启动一个JVM进程。在同一个JVM进程中，有且只有一个进程，就是它自己。在这个JVM环境中，所有程序代码的运行都是以线程来运行。<br /> <br />一般常见的Java应用程序都是单线程的。比如，用java命令运行一个最简单的HelloWorld的Java应用程序时，就启动了一个 JVM进程，JVM找到程序程序的入口点main()，然后运行main()方法，这样就产生了一个线程，这个线程称之为主线程。当main方法结束后，主线程运行完成。JVM进程也随即退出 。<br /> <br />对于一个进程中的多个线程来说，多个线程共享进程的内存块，当有新的线程产生的时候，操作系统不分配新的内存，而是让新线程共享原有的进程块的内存。因此，线程间的通信很容易，速度也很快。不同的进程因为处于不同的内存块，因此进程之间的通信相对困难。<br /> <br />实际上，操作的系统的多进程实现了多任务并发执行，程序的多线程实现了进程的并发执行。多任务、多进程、多线程的前提都是要求操作系统提供多任务、多进程、多线程的支持。<br /> <br />在Java程序中，JVM负责线程的调度。线程调度是值按照特定的机制为多个线程分配CPU的使用权。<br />调度的模式有两种：分时调度和抢占式调度。分时调度是所有线程轮流获得CPU使用权，并平均分配每个线程占用CPU的时间；抢占式调度是根据线程的优先级别来获取CPU的使用权。JVM的线程调度模式采用了抢占式模式。<br /> <br />所谓的“并发执行”、“同时”其实都不是真正意义上的“同时”。众所周知，CPU都有个时钟频率，表示每秒中能执行cpu指令的次数。在每个时钟周期内，CPU实际上只能去执行一条（也有可能多条）指令。操作系统将进程线程进行管理，轮流（没有固定的顺序）分配每个进程很短的一段是时间（不一定是均分），然后在每个线程内部，程序代码自己处理该进程内部线程的时间分配，多个线程之间相互的切换去执行，这个切换时间也是非常短的。因此多任务、多进程、多线程都是操作系统给人的一种宏观感受，从微观角度看，程序的运行是异步执行的。<br /> <br />用一句话做总结：虽然操作系统是多线程的，但CPU每一时刻只能做一件事，和人的大脑是一样的，呵呵。<br /> <br /> <br />二、Java与多线程<br /> <br />Java语言的多线程需要操作系统的支持。<br /> <br />Java 虚拟机允许应用程序并发地运行多个执行线程。Java语言提供了多线程编程的扩展点，并给出了功能强大的线程控制API。<br /> <br />在Java中，多线程的实现有两种方式：<br />扩展java.lang.Thread类<br />实现java.lang.Runnable接口<br /> <br /> <br />每个线程都有一个优先级，高优先级线程的执行优先于低优先级线程。每个线程都可以或不可以标记为一个守护程序。当某个线程中运行的代码创建一个新 Thread 对象时，该新线程的初始优先级被设定为创建线程的优先级，并且当且仅当创建线程是守护线程时，新线程才是守护程序。<br /> <br />当 Java 虚拟机启动时，通常都会有单个非守护线程（它通常会调用某个指定类的 main 方法）。Java 虚拟机会继续执行线程，直到下列任一情况出现时为止：<br /> <br />调用了 Runtime 类的 exit 方法，并且安全管理器允许退出操作发生。<br />非守护线程的所有线程都已停止运行，无论是通过从对 run 方法的调用中返回，还是通过抛出一个传播到 run 方法之外的异常。<br /> <br /> <br />三、扩展java.lang.Thread类<br /> <br />/**<br /> * File Name:   TestMitiThread.java<br /> * Created by:  IntelliJ IDEA.<br /> * Copyright:   Copyright (c) 2003-2006<br /> * Company:     Lavasoft( http://lavasoft.blog.51cto.com/)<br /> * Author:      leizhimin<br /> * Modifier:    leizhimin<br /> * Date Time:   2007-5-17 10:03:12<br /> * Readme:      通过扩展Thread类实现多线程<br /> */<br />public class TestMitiThread {<br />    public static void main(String[] rags) {<br />        System.out.println(Thread.currentThread().getName() + " 线程运行开始!");<br />        new MitiSay("A").start();<br />        new MitiSay("B").start();<br />        System.out.println(Thread.currentThread().getName() + " 线程运行结束!");<br />    }<br />}<br /> <br />class MitiSay extends Thread {<br />    public MitiSay(String threadName) {<br />        super(threadName);<br />    }<br /> <br />    public void run() {<br />        System.out.println(getName() + " 线程运行开始!");<br />        for (int i = 0; i &lt; 10; i++) {<br />            System.out.println(i + " " + getName());<br />            try {<br />                sleep((int) Math.random() * 10);<br />            } catch (InterruptedException e) {<br />                e.printStackTrace();<br />            }<br />        }<br />        System.out.println(getName() + " 线程运行结束!");<br />    }<br />}<br /> <br />运行结果：<br /> <br />main 线程运行开始!<br />main 线程运行结束!<br />A 线程运行开始!<br />0 A<br />1 A<br />B 线程运行开始!<br />2 A<br />0 B<br />3 A<br />4 A<br />1 B<br />5 A<br />6 A<br />7 A<br />8 A<br />9 A<br />A 线程运行结束!<br />2 B<br />3 B<br />4 B<br />5 B<br />6 B<br />7 B<br />8 B<br />9 B<br />B 线程运行结束!<br /> <br />说明：<br />程序启动运行main时候，java虚拟机启动一个进程，主线程main在main()调用时候被创建。随着调用MitiSay的两个对象的start方法，另外两个线程也启动了，这样，整个应用就在多线程下运行。<br /> <br />在一个方法中调用Thread.currentThread().getName()方法，可以获取当前线程的名字。在mian方法中调用该方法，获取的是主线程的名字。<br /> <br />注意：start()方法的调用后并不是立即执行多线程代码，而是使得该线程变为可运行态（Runnable），什么时候运行是由操作系统决定的。<br />从程序运行的结果可以发现，多线程程序是乱序执行。因此，只有乱序执行的代码才有必要设计为多线程。<br />Thread.sleep()方法调用目的是不让当前线程独自霸占该进程所获取的CPU资源，以留出一定时间给其他线程执行的机会。<br />实际上所有的多线程代码执行顺序都是不确定的，每次执行的结果都是随机的。<br /> <br /> <br />四、实现java.lang.Runnable接口<br /> <br />/**<br /> * File Name:   TestMitiThread1.java<br /> * Created by:  IntelliJ IDEA.<br /> * Copyright:   Copyright (c) 2003-2006<br /> * Company:     Lavasoft( http://lavasoft.blog.51cto.com/)<br /> * Author:      leizhimin<br /> * Modifier:    leizhimin<br /> * Date Time:   2007-5-17 10:48:03<br /> * Readme:      通过实现 Runnable 接口实现多线程<br /> */<br />public class TestMitiThread1 implements Runnable {<br /> <br />    public static void main(String[] args) {<br />        System.out.println(Thread.currentThread().getName() + " 线程运行开始!");<br />        TestMitiThread1 test = new TestMitiThread1();<br />        Thread thread1 = new Thread(test);<br />        Thread thread2 = new Thread(test);<br />        thread1.start();<br />        thread2.start();<br />        System.out.println(Thread.currentThread().getName() + " 线程运行结束!");<br />    }<br /> <br />    public void run() {<br />        System.out.println(Thread.currentThread().getName() + " 线程运行开始!");<br />        for (int i = 0; i &lt; 10; i++) {<br />            System.out.println(i + " " + Thread.currentThread().getName());<br />            try {<br />                Thread.sleep((int) Math.random() * 10);<br />            } catch (InterruptedException e) {<br />                e.printStackTrace();<br />            }<br />        }<br />        System.out.println(Thread.currentThread().getName() + " 线程运行结束!");<br />    }<br />}<br /> <br />运行结果：<br /> <br />main 线程运行开始!<br />Thread-0 线程运行开始!<br />main 线程运行结束!<br />0 Thread-0<br />Thread-1 线程运行开始!<br />0 Thread-1<br />1 Thread-1<br />1 Thread-0<br />2 Thread-0<br />2 Thread-1<br />3 Thread-0<br />3 Thread-1<br />4 Thread-0<br />4 Thread-1<br />5 Thread-0<br />6 Thread-0<br />5 Thread-1<br />7 Thread-0<br />8 Thread-0<br />6 Thread-1<br />9 Thread-0<br />7 Thread-1<br />Thread-0 线程运行结束!<br />8 Thread-1<br />9 Thread-1<br />Thread-1 线程运行结束!<br /> <br />说明：<br />TestMitiThread1类通过实现Runnable接口，使得该类有了多线程类的特征。run（）方法是多线程程序的一个约定。所有的多线程代码都在run方法里面。Thread类实际上也是实现了Runnable接口的类。<br />在启动的多线程的时候，需要先通过Thread类的构造方法Thread(Runnable target) 构造出对象，然后调用Thread对象的start()方法来运行多线程代码。<br />实际上所有的多线程代码都是通过运行Thread的start()方法来运行的。因此，不管是扩展Thread类还是实现Runnable接口来实现多线程，最终还是通过Thread的对象的API来控制线程的，熟悉Thread类的API是进行多线程编程的基础。<br /> <br />五、读解Thread类API<br /> <br />static int MAX_PRIORITY<br />          线程可以具有的最高优先级。<br />static int MIN_PRIORITY<br />          线程可以具有的最低优先级。<br />static int NORM_PRIORITY<br />          分配给线程的默认优先级。<br /> <br />构造方法摘要<br />Thread(Runnable target)<br />          分配新的 Thread 对象。<br />Thread(String name)<br />          分配新的 Thread 对象。<br /> <br /> <br />方法摘要<br />static Thread currentThread()<br />          返回对当前正在执行的线程对象的引用。<br /> ClassLoader getContextClassLoader()<br />          返回该线程的上下文 ClassLoader。<br /> long getId()<br />          返回该线程的标识符。<br /> String getName()<br />          返回该线程的名称。<br /> int getPriority()<br />          返回线程的优先级。<br /> Thread.State getState()<br />          返回该线程的状态。<br /> ThreadGroup getThreadGroup()<br />          返回该线程所属的线程组。<br />static boolean holdsLock(Object obj)<br />          当且仅当当前线程在指定的对象上保持监视器锁时，才返回 true。<br /> void interrupt()<br />          中断线程。<br />static boolean interrupted()<br />          测试当前线程是否已经中断。<br /> boolean isAlive()<br />          测试线程是否处于活动状态。<br /> boolean isDaemon()<br />          测试该线程是否为守护线程。<br /> boolean isInterrupted()<br />          测试线程是否已经中断。<br /> void join()<br />          等待该线程终止。<br /> void join(long millis)<br />          等待该线程终止的时间最长为 millis 毫秒。<br /> void join(long millis, int nanos)<br />          等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒。<br /> void resume()<br />          已过时。 该方法只与 suspend() 一起使用，但 suspend() 已经遭到反对，因为它具有死锁倾向。有关更多信息，请参阅为何 Thread.stop、Thread.suspend 和 Thread.resume 遭到反对？。<br /> void run()<br />          如果该线程是使用独立的 Runnable 运行对象构造的，则调用该 Runnable 对象的 run 方法；否则，该方法不执行任何操作并返回。<br /> void setContextClassLoader(ClassLoader cl)<br />          设置该线程的上下文 ClassLoader。<br /> void setDaemon(boolean on)<br />          将该线程标记为守护线程或用户线程。<br />static void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)<br />          设置当线程由于未捕获到异常而突然终止，并且没有为该线程定义其他处理程序时所调用的默认处理程序。<br /> void setName(String name)<br />          改变线程名称，使之与参数 name 相同。<br /> void setPriority(int newPriority)<br />          更改线程的优先级。<br /> void setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)<br />          设置该线程由于未捕获到异常而突然终止时调用的处理程序。<br />static void sleep(long millis)<br />          在指定的毫秒数内让当前正在执行的线程休眠（暂停执行）。<br />static void sleep(long millis, int nanos)<br />          在指定的毫秒数加指定的纳秒数内让当前正在执行的线程休眠（暂停执行）。<br /> void start()<br />          使该线程开始执行；Java 虚拟机调用该线程的 run 方法。<br /> void stop()<br />          已过时。 该方法具有固有的不安全性。用 Thread.stop 来终止线程将释放它已经锁定的所有监视器（作为沿堆栈向上传播的未检查 ThreadDeath 异常的一个自然后果）。如果以前受这些监视器保护的任何对象都处于一种不一致的状态，则损坏的对象将对其他线程可见，这有可能导致任意的行为。stop 的许多使用都应由只修改某些变量以指示目标线程应该停止运行的代码来取代。目标线程应定期检查该变量，并且如果该变量指示它要停止运行，则从其运行方法依次返回。如果目标线程等待很长时间（例如基于一个条件变量），则应使用 interrupt 方法来中断该等待。有关更多信息，请参阅《为何不赞成使用 Thread.stop、Thread.suspend 和 Thread.resume？》。<br /> void stop(Throwable obj)<br />          已过时。 该方法具有固有的不安全性。请参阅 stop() 以获得详细信息。该方法的附加危险是它可用于生成目标线程未准备处理的异常（包括若没有该方法该线程不太可能抛出的已检查的异常）。有关更多信息，请参阅为何 Thread.stop、Thread.suspend 和 Thread.resume 遭到反对？。<br /> void suspend()<br />          已过时。该方法已经遭到反对，因为它具有固有的死锁倾向。如果目标线程挂起时在保护关键系统资源的监视器上保持有锁，则在目标线程重新开始以前任何线程都不能访问该资源。如果重新开始目标线程的线程想在调用 resume 之前锁定该监视器，则会发生死锁。这类死锁通常会证明自己是“冻结”的进程。有关更多信息，请参阅为何 Thread.stop、Thread.suspend 和 Thread.resume 遭到反对？。<br /> String toString()<br />          返回该线程的字符串表示形式，包括线程名称、优先级和线程组。<br />static void yield()<br />          暂停当前正在执行的线程对象，并执行其他线程。<br /> <br /> <br />六、线程的状态转换图<br /> <img src="http://lavasoft.blog.51cto.com/attachment/200705/200705181179465004843.png" /><br />线程在一定条件下，状态会发生变化。线程变化的状态转换图如下：<br /> <br />1、新建状态（New）：新创建了一个线程对象。<br />2、就绪状态（Runnable）：线程对象创建后，其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中，变得可运行，等待获取CPU的使用权。<br />3、运行状态（Running）：就绪状态的线程获取了CPU，执行程序代码。<br />4、阻塞状态（Blocked）：阻塞状态是线程因为某种原因放弃CPU使用权，暂时停止运行。直到线程进入就绪状态，才有机会转到运行状态。阻塞的情况分三种：<br />（一）、等待阻塞：运行的线程执行wait()方法，JVM会把该线程放入等待池中。<br />（二）、同步阻塞：运行的线程在获取对象的同步锁时，若该同步锁被别的线程占用，则JVM会把该线程放入锁池中。<br />（三）、其他阻塞：运行的线程执行sleep()或join()方法，或者发出了I/O请求时，JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时，线程重新转入就绪状态。<br />5、死亡状态（Dead）：线程执行完了或者因异常退出了run()方法，该线程结束生命周期。<br /> <br /> <br />七、线程的调度<br />1、调整线程优先级：Java线程有优先级，优先级高的线程会获得较多的运行机会。<br /> <br />Java线程的优先级用整数表示，取值范围是1~10，Thread类有以下三个静态常量：<br />static int MAX_PRIORITY<br />          线程可以具有的最高优先级，取值为10。<br />static int MIN_PRIORITY<br />          线程可以具有的最低优先级，取值为1。<br />static int NORM_PRIORITY<br />          分配给线程的默认优先级，取值为5。<br /> <br />Thread类的setPriority()和getPriority()方法分别用来设置和获取线程的优先级。<br /> <br />每个线程都有默认的优先级。主线程的默认优先级为Thread.NORM_PRIORITY。<br />线程的优先级有继承关系，比如A线程中创建了B线程，那么B将和A具有相同的优先级。<br />JVM提供了10个线程优先级，但与常见的操作系统都不能很好的映射。如果希望程序能移植到各个操作系统中，应该仅仅使用Thread类有以下三个静态常量作为优先级，这样能保证同样的优先级采用了同样的调度方式。<br /> <br />2、线程睡眠：Thread.sleep(long millis)方法，使线程转到阻塞状态。millis参数设定睡眠的时间，以毫秒为单位。当睡眠结束后，就转为就绪（Runnable）状态。sleep()平台移植性好。<br /> <br />3、线程等待：Object类中的wait()方法，导致当前的线程等待，直到其他线程调用此对象的 notify() 方法或 notifyAll() 唤醒方法。这个两个唤醒方法也是Object类中的方法，行为等价于调用 wait(0) 一样。<br /> <br />4、线程让步：Thread.yield() 方法，暂停当前正在执行的线程对象，把执行机会让给相同或者更高优先级的线程。<br /> <br />5、线程加入：join()方法，等待其他线程终止。在当前线程中调用另一个线程的join()方法，则当前线程转入阻塞状态，直到另一个进程运行结束，当前线程再由阻塞转为就绪状态。<br /> <br />6、线程唤醒：Object类中的notify()方法，唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待，则会选择唤醒其中一个线程。选择是任意性的，并在对实现做出决定时发生。线程通过调用其中一个 wait 方法，在对象的监视器上等待。直到当前的线程放弃此对象上的锁定，才能继续执行被唤醒的线程。被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争；例如，唤醒的线程在作为锁定此对象的下一个线程方面没有可靠的特权或劣势。类似的方法还有一个notifyAll()，唤醒在此对象监视器上等待的所有线程。<br /> <br />注意：Thread中suspend()和resume()两个方法在JDK1.5中已经废除，不再介绍。因为有死锁倾向。<br /> <br />7、常见线程名词解释<br />主线程：JVM调用程序mian()所产生的线程。<br />当前线程：这个是容易混淆的概念。一般指通过Thread.currentThread()来获取的进程。<br />后台线程：指为其他线程提供服务的线程，也称为守护线程。JVM的垃圾回收线程就是一个后台线程。<br />前台线程：是指接受后台线程服务的线程，其实前台后台线程是联系在一起，就像傀儡和幕后操纵者一样的关系。傀儡是前台线程、幕后操纵者是后台线程。由前台线程创建的线程默认也是前台线程。可以通过isDaemon()和setDaemon()方法来判断和设置一个线程是否为后台线程。<br /> <br />八、线程同步<br /> <br /> <br />九、
          <br/>
          <span style="color:red;">
            <a href="http://fengzl.javaeye.com/blog/164346#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Fri, 22 Feb 2008 19:31:03 +0800</pubDate>
        <link>http://fengzl.javaeye.com/blog/164346</link>
        <guid>http://fengzl.javaeye.com/blog/164346</guid>
      </item>
      <item>
        <title>TransactionManager</title>
        <author>fengzl</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://fengzl.javaeye.com">fengzl</a>&nbsp;
          链接：<a href="http://fengzl.javaeye.com/blog/164282" style="color:red;">http://fengzl.javaeye.com/blog/164282</a>&nbsp;
          发表时间: 2008年02月22日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          您有没有想过，为什么会有六种事务划分属性（NotSupported、Required、Supports、RequiresNew、Mandatory和Never）？这六种都是由容器托管事务 (CMT)的bean来支持的，但如果使用的是bean托管事务(BMT)，EJB规范所能提供的功能难道就只有通过UserTransaction接口启动和提交/回滚事务吗？显然，CMT模型好像更强大，比如，BMT不能使当前事务挂起然后恢复，这就意味着在BMT bean中无法仿真RequiresNew和NotSupported划分，至少是在使用UserTransaction接口时。<br /><br />　　虽然EJB规范并没有解释为什么会存在以上所提到的不对称情况，但是在BMT模型中依然有一种用来使事务挂起然后恢复的合法方式。如果曾经研究过javax.transaction包的内容，您可能会注意到，与UserTransaction接口一起的还有一个TransactionManager接口，它看起来就像一个扩展的UserTransaction：同样的方法——begin()、commit()和rollback()，再加上suspend()和resume()。<br /><br />　　如果能从EJB中得到一个TransactionManager实现，我们就可以实现编程式地使事务挂起然后恢复的目标。虽然J2EE 1.3和EJB 2.0规范都未提到TransactionManager的可用性，但它们也都没有明确表示禁止使用它。此外，对于CMT事务划分，容器是从内部使用Java Transaction API (JTA)，因此，我们几乎可以100%地肯定：TransactionManager是存在的，惟一的问题只是在代码中获得对它的引用。<br /><br />　　在这篇文章中，我们将了解如何利用几个流行的容器来获得一个TransactionManager，以及如何用它来扩展bean托管事务的功能，使它们和容器托管事务一样强大。我们也将简述一些涉及使用这些高级功能的风险，在文章的结尾，我们还将探讨如何在流行的Spring框架中使用TransactionManager。<br />在各种J2EE服务器中获得TransactionManager的引用<br /><br />　　J2EE和EJB规范没有描述任何获得TransactionManager引用的标准方法，每个J2EE容器供应商可以随意将其放置在任何地方，甚至不需提供任何机制，就可以从应用程序代码中对它进行访问。但在实践中，如今所有的容器都有获取它的机制。以下是一些如何从最流行的J2EE容器获得TransactionManager引用的例子。<br />抛出一个UserTransaction (WebLogic、Orion、OC4J)<br /><br />　　任何一个兼容J2EE的容器都必须使UserTransaction对象在JNDI中的java:comp/UserTransaction下可用。因为UserTransaction接口是TransactionManager的子集，所以一些J2EE容器供应商选择为它们提供一种通用的实现。WebLogic 8、Orion 2和Oracle的OC4J EJB3预览版都是这种方法的例子。在这些容器中，只要从JNDI中获得一个UserTransaction对象，再把它转到TransactionManager，就可以获得对TransactionManager的引用。这可能是最简单的一种情况。<br /><br /><pre name="code" class="java">private TransactionManager getFromUserTransaction() 
        throws Exception {
    InitialContext ctx = new InitialContext();
    UserTransaction ut = (UserTransaction)
        ctx.lookup("java:comp/UserTransaction");
    if (ut instanceof TransactionManager) {
        log("UserTransaction also TransactionManager");
        return (TransactionManager)ut;
    }
    return null;
}</pre><br /><br />直接从JNDI中获取TransactionManager (JBoss、WebLogic)<br /><br />　　在JBoss 3和WebLogic 8中，可从JNDI获取TransactionManager（虽然名称不一样），因此可以通过简单的查找而获得：<br /><br /><pre name="code" class="java">private TransactionManager getFromJNDI() 
        throws Exception {
    InitialContext ctx = new InitialContext();
    try {
        // WebLogic
        return (TransactionManager)
            ctx.lookup("javax.transaction.TransactionManager");
     }
     catch (Exception e) {  }

    try {
        // JBoss
        return (TransactionManager)
            ctx.lookup("java:/TransactionManager");
    }
    catch (Exception e) { }
    return null;
}</pre><br /><br />从一个定制的工厂获取TransactionManager (Websphere)<br /><br />　　在WebSphere 4/5/6中，TransactionManager的引用要从工厂类中获取。但是，麻烦的是，工厂类的名称随WebSphere版本的不同而有所改变。<br /><br /><pre name="code" class="java">public TransactionManager getFromWebsphereFactory() 
        throws Exception {
     try {
        // WebSphere 5.1 or 6.0
        return 
            com.ibm.ws.Transaction.TransactionManagerFactory
                .getTransactionManager();
    }
    catch (ClassNotFoundException ex) {}
        
    try {
        // WebSphere 5.0
        return 
            com.ibm.ejs.jts.jta.TransactionManagerFactory
                .getTransactionManager();
    }
    catch (ClassNotFoundException ex) {}
        
    try {
        // WebSphere 4.0
        com.ibm.ejs.jts.jta.JTSXA..getTransactionManager();
    }
    catch (ClassNotFoundException ex) { }

    return null;
}</pre><br /><br />　　在WebLogic 7/8/9中，对TransactionManager的引用可以通过在Weblogic 7的TxHelper中定义的静态方法getTransactionManager()而获得。该类在WebLogic 8中被否决了，而用TransactionHelper取而代之。<br /><br /><pre name="code" class="java">public TransactionManager getFromWebLogicFactory() 
    throws Exception {
     try {
        // WebLogic 8/9
        return 
            weblogic.transaction.TransactionHelper
                .getTransactionManager();
    }
    catch (ClassNotFoundException ex) {}
        
    try {
        // WebLogic 7
        return 
            weblogic.transaction.TxHelper
                .getTransactionManager();
    }
    catch (ClassNotFoundException ex) {}
        
    return null;
}</pre><br /><br />使用TransactionManager<br /><br />　　一旦成功地获得TransactionManager引用，就可以用它来挂起和恢复事务，正如以下的示例代码所示。<br /><br />...<br /><pre name="code" class="java">// obtain UserTransaction object and start transaction
InitialContext ctx = new InitialContext();
UserTransaction userTransaction = (UserTransaction)
    ctx.lookup("java:comp/UserTransaction");

// start first transaction
userTransaction.begin();
            
// obtain TransactionManager 
// using one of the methods described above
TransactionManager tm = getTransactionManager();

// suspend transaction
// suspend() returns reference to suspended
// Transaction object which later should be passed
// to resume()
Transaction transaction = tm.suspend();
                
// here you can do something outside of transaction
// or start new transaction, 
// do something and then commit or rollback
userTransaction.begin();
    
// commit subtransaction
userTransaction.commit();

// resume suspended transaction
tm.resume(transaction);

// commit first transaction
userTransaction.commit();
...</pre><br /><br />正如您所看到的，在TransactionManager接口的帮助下，可以对UserTransaction所提供的标准功能进行扩展，在BMT代码中实现与CMT bean相同的灵活性水平。<br /><br />　　需要知道的是，当事务被挂起时，并不意味着事务的计时器停止了。换言之，如果把事务的超时设定为30秒，而事务已挂起了20秒并恢复了，那么该事务只剩10秒就要到达超时了。事务的挂起会解除事务与正在运行的线程之间的关联，然后resume()调用会再次将其关连，而不影响事务超时计时器。<br />已知问题<br /><br />　　因为J2EE规范并不要求TransactionManager在J2EE容器中的可用性以及功能（虽然从底层的JTA基础架构中我们知道它应该是存在的），所以有些应用服务器中存在一些问题。例如，在WebLogic 7、8以及9（beta版）中有种特别奇怪的现象：假如一个事务被标记为回滚（通过调用UserTransaction.setRollbackOnly ()），然后被挂起，当被挂起的事务尝试恢复时，它将会出现如下的提示：<br /><br />　　javax.transaction.InvalidTransactionException: Attempt to resume an inactive transaction<br /><br />　　以下代码说明了该行为：<br /><br /><pre name="code" class="java">...
// obtain UserTransaction object and start transaction
InitialContext ctx = new InitialContext();
UserTransaction userTransaction = (UserTransaction)
    ctx.lookup("java:comp/UserTransaction");
userTransaction.begin();

// mark for rollback
userTransaction.setRollbackOnly();
            
TransactionManager tm = getTransactionManager();

// suspend transaction
Transaction transaction = tm.suspend();
                
// resume suspended transaction
// this call will fail with InvalidTransactionException
//  in WebLogic
tm.resume(transaction);</pre><br />...<br /><br />　　幸运的是，对于该问题，有一个应急方案。WebLogic的TransactionManager实现连同标准的resume （Transaction transaction）方法，有一个可用于替代的forceResume方法。以下的代码展示了一种在WebLogic中运行代码时需要用到的模式。注意，在这种情况下，应该把对TransactionManager的引用转换到WebLogic的定制实现接口（WebLogic 7中的weblogic.transaction.TransactionManager或WebLogic 8以及更高版本中的weblogic.transaction.ClientTransactionManager）。<br /><br />...<br />// obtain UserTransaction object and start transaction<br /><pre name="code" class="java">InitialContext ctx = new InitialContext();
UserTransaction userTransaction = (UserTransaction)
    ctx.lookup("java:comp/UserTransaction");
userTransaction.begin();

// mark for rollback
userTransaction.setRollbackOnly();
            
TransactionManager tm = getTransactionManager();

// suspend transaction
Transaction transaction = tm.suspend();
                
// resume suspended transaction
try {
    // first try standard JTA call
    tm.resume(transaction);
}
catch (InvalidTransactionException e) {
    // standard method failed, try forceResume()
    if (tm instanceof 
        weblogic.transaction.ClientTransactionManager) {
        // WebLogic 8 and above
        ((weblogic.transaction.ClientTransactionManager)tm)
            .forceResume(transaction);
    }    
    else if (tm instanceof 
        weblogic.transaction.TransactionManager) {
        // WebLogic 7
        ((weblogic.transaction.TransactionManager)tm)
            .forceResume(transaction)
    }
    else {
        // cannot resume
        throw e;
    }
}</pre><br />...<br /><br />TransactionManager在Spring Framework中的用法<br /><br />　　在流行的Spring framework中，以上所描述的技术都被广泛地用于事务代理。应该注意的是，每次使用PROPAGATION_REQUIRES_NEW或PROPAGATION_NOT_SUPPORTED、事务属性以及JtaTransactionManager来配置Spring的TransactionProxyFactoryBean时， Spring就会用JTA的TransactionManager来挂起和恢复事务。Spring太智能了，但有时太智能了反而使我无法接受——甚至没有指定，它就会发现容器中的TransactionManager。例如，当在一个Spring应用程序上下文中定义 JtaTransactionManager时，就可以为UserTransaction提供一个JNDI名，而如果UserTransaction也实现了它，它就将“自动检测”TransactionManager。如上所示，这对WebLogic、Orion和Oracle OC4J都适用。<br /><br />　　有时，这可能并不是我们所想要的，尤其是当您想要严格遵循J2EE/EJB规范，并确保跨所有J2EE容器的完全可移植性的时候。正如我们所看到的，有时候编程式的事务挂起和恢复可能存在一些问题。虽然Spring知道如何绕过这些问题，至少是对于上面所描述的问题，即，在Welogic中，在挂起之前将事务标记为回滚。在这种情况下，可以在配置JtaTransactionManager时把 autodetectTransactionManager属性设定为false。如果这么做了，那么任何使用 PROPAGATION_REQUIRES_NEW或PROPAGATION_NOT_SUPPORTED事务属性的尝试都会抛出 TransactionSuspensionNotSupportedException而失败。但PROPAGATIO-REQUIRED、 PROPAGATION-SUPPORTS、PROPAGATION-MANDATORY以及PROPAGATION-NEVER应该会正常运行。这对应于JTA Usertransaction所提供的功能，而且在任一个兼容J2EE的容器中都起作用。<br /><br />　　以下是Spring应用程序上下文中的事务管理器定义，其中禁用了TransactionManager自动检测：<br /><pre name="code" class="xml">
&lt;bean id="transactionManager" class=
"org.springframework.transaction.jta.JtaTransactionManager">
  &lt;property name="userTransactionName">
    &lt;value>javax.transaction.UserTransaction&lt;/value>
  &lt;/property>
  &lt;property name="autodetectTransactionManager">
    &lt;value>false&lt;/value>
  &lt;/property>
&lt;/bean></pre><br /><br />结束语<br /><br />　　J2EE规范不要求对JIA TransactionManager接口的支持。但由于J2EE使用JTA作为它的底层事务基础架构，所以几乎所有的J2EE服务器都把它公开为 J2EE的扩展。存在一些已知的兼容性问题，而在某些情况下，可以用特定于容器的代码来绕过这些问题。但是，如果需要实现编程式事务挂起，则J2EE中的 TransactionManager是一个强大的特性。只需首先在所选择的J2EE服务器中检测一下它是否可用就可以了。
          <br/>
          <span style="color:red;">
            <a href="http://fengzl.javaeye.com/blog/164282#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Fri, 22 Feb 2008 16:21:43 +0800</pubDate>
        <link>http://fengzl.javaeye.com/blog/164282</link>
        <guid>http://fengzl.javaeye.com/blog/164282</guid>
      </item>
      <item>
        <title>JNDI配置原理详解</title>
        <author>fengzl</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://fengzl.javaeye.com">fengzl</a>&nbsp;
          链接：<a href="http://fengzl.javaeye.com/blog/164215" style="color:red;">http://fengzl.javaeye.com/blog/164215</a>&nbsp;
          发表时间: 2008年02月22日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          最近写书，写到JNDI，到处查资料，发现所有的中文资料都对JNDI解释一通，配置代码也是copy的，调了半天也没调通，最后到SUN的网站参考了一下他的JNDI tutorial，终于基本上彻底明白了<br /><br />和多数java服务一样，SUN对JNDI也只提供接口，使用JNDI只需要用到JNDI接口而不必关心具体实现：<br /><br /><pre name="code" class="java">private static Object jndiLookup() throws Exception {
  InitialContext ctx = new InitialContext();
  return ctx.lookup("java:comp/env/systemStartTime");
}</pre><br /><br />上述代码在J2EE服务器环境下工作得很好，但是在main()中就会报一个NoInitialContextException，许多文章会说你创建InitialContext的时候还要传一个Hashtable或者Properties，像这样：<br /><pre name="code" class="java">
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
env.put(Context.PROVIDER_URL,"t3://localhost:7001");
InitialContext ctx = new InitialContext(env);
</pre><br />这个在WebLogic环境下是对的，但是换到JBoss呢？再用JBoss的例子？<br /><br />其实之所以有NoInitialContextException是因为无法从System.properties中获得必要的JNDI参数，在服务器环境下，服务器启动时就把这些参数放到System.properties中了，于是直接new InitialContext()就搞定了，不要搞env那么麻烦，搞了env你的代码还无法移植，弄不好管理员设置服务器用的不是标准端口还照样抛异常。<br /><br />但是在单机环境下，可没有JNDI服务在运行，那就手动启动一个JNDI服务。我在JDK 5的rt.jar中一共找到了4种SUN自带的JNDI实现：<br /><br />LDAP，CORBA，RMI，DNS。<br /><br />这4种JNDI要正常运行还需要底层的相应服务。一般我们没有LDAP或CORBA服务器，也就无法启动这两种JNDI服务，DNS用于查域名的，以后再研究，唯一可以在main()中启动的就是基于RMI的JNDI服务。<br /><br />现在我们就在main()中启动基于RMI的JNDI服务并且绑一个Date对象到JNDI上：<br /><br /><pre name="code" class="java">LocateRegistry.createRegistry(1099);
System.setProperty(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");
System.setProperty(Context.PROVIDER_URL, "rmi://localhost:1099");
InitialContext ctx = new InitialContext();
class RemoteDate extends Date implements Remote {};
ctx.bind("java:comp/env/systemStartTime", new RemoteDate());
ctx.close();</pre><br /><br />注意，我直接把JNDI的相关参数放入了System.properties中，这样，后面的代码如果要查JNDI，直接new InitialContext()就可以了，否则，你又得写Hashtable env = ...<br /><br />在RMI中绑JNDI的限制是，绑定的对象必须是Remote类型，所以就自己扩展一个。<br /><br />其实JNDI还有两个Context.SECURITY_PRINCIPAL和Context.SECURITY_CREDENTIAL，如果访问JNDI需要用户名和口令，这两个也要提供，不过一般用不上。<br /><br />在后面的代码中查询就简单了：<br /><br /><pre name="code" class="java">InitialContext ctx = new InitialContext();
Date startTime = (Date) ctx.lookup("java:comp/env/systemStartTime");</pre><br /><br />在SUN 的JNDI tutorial中的例子用的com.sun.jndi.fscontext.RefFSContextFactory类，但是我死活在JDK 5中没有找到这个类，也就是NoClassDefFoundError，他也不说用的哪个扩展包，我也懒得找了。
          <br/>
          <span style="color:red;">
            <a href="http://fengzl.javaeye.com/blog/164215#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Fri, 22 Feb 2008 14:20:48 +0800</pubDate>
        <link>http://fengzl.javaeye.com/blog/164215</link>
        <guid>http://fengzl.javaeye.com/blog/164215</guid>
      </item>
      <item>
        <title>事务服务</title>
        <author>fengzl</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://fengzl.javaeye.com">fengzl</a>&nbsp;
          链接：<a href="http://fengzl.javaeye.com/blog/163750" style="color:red;">http://fengzl.javaeye.com/blog/163750</a>&nbsp;
          发表时间: 2008年02月21日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          　　JTA 是事务服务的 J2EE 解决方案。本质上，它是描述事务接口（比如 UserTransaction 接口，开发人<br /><br />员直接使用该接口或者通过 J2EE 容器使用该接口来确保业务逻辑能够可靠地运行）的 J2EE 模型的一部分<br /><br />。JTA 具有的三个主要的接口分别是 UserTransaction 接口、TransactionManager 接口和 Transaction 接<br /><br />口。这些接口共享公共的事务操作，例如 commit() 和 rollback()， 但是也包含特殊的事务操作，例如<br /><br />suspend()，resume() 和 enlist()，它们只出现在特定的接口上，以便在实现中允许一定程度的访问控制。<br /><br />例如，UserTransaction 能够执行事务划分和基本的事务操作，而 TransactionManager 能够执行上下文管<br /><br />理。<br /><br />http://www.it55.com/html/xueyuan/chengxukaifa/JAVAjiaocheng/20070719/102483_6.html<br /><br />JTA UserTransaction与JTA TransactionManager比较<br /><br />让我们来看一下Spring对JTA支持的细节。虽然并非经常需要考虑这个细节但了解相关的细节还有必要的。对简单的用例如前面章节的示例，标准的JtaTransactionManager定义已经足够了，<br />缺省的Spring JtaTransactionManager设置会从标准JNDI位置（J2EE规范所定义的java:comp/UserTransaction）获取 JTA的javax.transaction.UserTransaction对象。这对大部分标准J2EE环境来说已经足够了。<br /><br />然而，缺省的JtaTransactionManager不能执行事务暂停（也就是说不支持PROPAGATION_REQUIRES_NEW和 PROPAGATION_NOT_SUPPORTED）。原因就在于标准的JTA UserTransaction接口不支持事务的暂停和恢复，而只支持开始和完成新的事务。<br /><br />为了实现事务的暂停，需要一个javax.transaction.TransactionManager实例，他提供了JTA定义的标准的暂停和恢复方法。不幸的是，J2EE没有为JTA TransactionManager定义标准的JNDI位置！因此，我们需要使用厂商自己的定位机制。<br /><br />清单6：<br />&lt;bean id="transactionManager"<br />   class="org.springframework.transaction.jta.JtaTransactionManager"><br />     &lt;property name="transactionManagerName"><br />        &lt;value>vendorSpecificJndiLocation&lt;/value><br />     &lt;/property><br />&lt;/bean><br /><br />J2EE本质上没有考虑将JTA TransactionManager接口作为公共API的一部分。JTA规范自身定义了将TransactionManager接口作为容器集成的想法。虽然这是可以理解的，但是JTA TransactionManager的标准JNDI位置还是可以增加一定的价值，特别是对轻量级容器如Spring,这样任何J2EE服务器就可以用统一的方式来定位JTA TransactionManager了。<br /><br />不仅Spring的JtaTransactionManager可以从访问中获益，O/R映射工具如Hibernate, Apache OJB, and Kodo JDO也能得到好处，因为他们需要在JTA环境中执行缓存同步的能力（释放缓存意味着JTA事务的完成）。这种注册事务同步的能力只有JTA TransactionManager接口才能提供，而UserTransaction是处理不了的。因此，这些工具都需要实现自己的 TransactionManager定位器。<br /><br />为JTA TransactionManager定义标准的JNDI位置是许多底层软件供应商最期望J2EE实现的功能。如果J2EE5.0的规范制定团队能够认识到这个特性的重要性就太好了。幸运地是，高级J2EE服务器如WebLogic Server已经考虑将JTA TransactionManager作为公共的API包含在扩展功能中。
          <br/>
          <span style="color:red;">
            <a href="http://fengzl.javaeye.com/blog/163750#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 21 Feb 2008 00:05:45 +0800</pubDate>
        <link>http://fengzl.javaeye.com/blog/163750</link>
        <guid>http://fengzl.javaeye.com/blog/163750</guid>
      </item>
      <item>
        <title>UserTransaction </title>
        <author>fengzl</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://fengzl.javaeye.com">fengzl</a>&nbsp;
          链接：<a href="http://fengzl.javaeye.com/blog/163551" style="color:red;">http://fengzl.javaeye.com/blog/163551</a>&nbsp;
          发表时间: 2008年02月20日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <pre name="code" class="java">
public class UserTransaction {
    private static Map threadDbconnectionMap = new HashMap();
    public void begin() {
        dataSource.getConnection().setAutoCommit(false);
        threadDbConnectionMap.put(Thread.currentThread(), dataSource.getConnection());    
        dataSource.getConnection().startTransaction();
    }

    public void commit() {
        dataSource.getConnection().commitTransaction();
        threadDbConnectionMap.remove(Thread.currentThread());        
    }
}

public class DataSource {
    private static Map threadDbConnectionMap = new HashMap();    
    public Connection getConnection() {
       if(threadDbConnectionMap.get(Thread.currentThread()) == null) {
          threadDbConnectionMap.put(Thread.currentThread(), DBCONNECTION_POOL.getConnection);
       }
          return (Connection)threadDbConnectionMap.get(      Thread.currentThread());
    }
}
</pre><br />UserTransaction的机制就是建立一个currentThread和一个 DBconnection的map，使得在同一个thread下的所有db operation使用同一个connection，这样通过背后的同一个connection的commit或rollback来保证 transaction的atomic<br /><br /><pre name="code" class="java">
public class DBConnectionPool {
	// singleton Pattern
	public Connection fetchConnection() ;
	public void releaseConnection(Connection conn);
}

public class ThreadConnectionMap {
	// singleton pattern.
	private Map threadConnectionMap = new HashMap();
	
	public Connection getConnection() {
		if(threadConnectionMap.get(Thread.currentThread()) != null)	{
			return (Connection)threadConnectionMap.get(Thread.currentThread());	
		}
		return DBConnectionPool.fetchConnection();
	}
	
	public Connection getConnectionInTx() {
		return (Connection)threadConnectionMap.get(Thread.currentThread());	
	}
	
	public void releaseConnectionInTx() {
		Connection con = getConnectionInTx();
		threadConnectionMap.remove(Thread.currentThread());
		DBConnectionPool.releaseConnection(con);
	}
	
	public Connection newConnectionInTx() {
		if(inTransaction()) {
			throw new TransactionException("Transaction already started!");	
		}
		Connection conn = DBConnectionPool.fetchConnection();
		threadConnectionMap.put(Thread.currentThread(), conn);
		return conn;
	}
	
	public boolean inTransaction() {
		return threadConnectionMap.get(Thread.currentThread()) != null; 	
	}
	
	..............
}

public class VendorDataSource() implements javax.sql.DataSource {
	public Connection getConnection() {
		return ThreadConnectionMap.getConnection();	
	}
	
	..........
}

public class VendorUserTransaction implements javax.transaction.UserTransaction {
	public void begin() {
		Connection con = ThreadConnectionMap.newConnectionInTx();
		con.setAutoCommit(false);
	}
	public void commit() {
		Connection con = ThreadConnectionMap.getConnectionInTx();
		if(con == null) {
			throw new TransactionException("cannot commit transaction, UserTransaction not started!");	
		}
		
		con.commit();
		ThreadConnectionMap.releaseConnectionInTx();
	}
	public void rollback(){
		Connection con = ThreadConnectionMap.getConnectionInTx();
		if(con == null) {
			throw new TransactionException("cannot rollback transaction, UserTransaction not started!");	
		}
		
		con.rollback();
		ThreadConnectionMap.releaseConnectionInTx();
	}
	
	public boolean inTransaction() {
		return ThreadConnectionMap.inTransaction();
	}
	....		
}
</pre><br /><br />以上是没有考虑XA（distributed transaction）和连接多个数据库情况的TransactionManager的实现，非常简陋，只是用于说明JTA的原理<br /><br /><span style="color: red">1。由于你只是使用了web server，没有采用application server，你的application又需要提供Transaction的control，唯一的办法是提供你自己的Transaction Manager，而不是使用UserTransaction（正如你所说的，web server不提供UserTransaction）。UserTransaction也是一种TransactionManager，由J2EE Container提供。<br /><br />2。你的“dbConnection.setAutoCommit(true)”这个调用是错误的。如果你调用该语句，dbConnection就会每次execute一句sql以后自动commit了，这样你的程序中本身需要放在同一个transaction中执行的代码分散在了好几个不同的transaction中执行了，就会导致partial update的问题，data consistency就没有办法保证。<br /><br />3。“死锁”的问题应该是你程序中的问题，而不应该归咎于dbConnection是否是 auto commit的。我在以前曾经写过一个自己的TransactionManager，就是通过建立一个Thread和dbConnection的map来实现的。刚开始测试这个TransactionManager的时候，也是经常出现“死锁”的问题，最后debug的结果发现并不是auto commit的问题，而是对于thread synchronization的问题处理不得当所导致的。<br /><br />4。“手动地为每一个更新都编写一个恢复原始数据的方法”是错误的。试想想，如果在你手动恢复数据的过程中又出现了Exception，你又如何保证data consistency呢？<br /><br /> 5。自己编写TransactionManager是一件比较复杂的工作，不是我想建议的。这也是为什么banq强烈推荐EJB的原因。EJB提供了TransactionManager的功能，我们可以采用EJB的解决方案，just off-the-shelf,不是吗？或者用JTA和DataSource一道实现Transaction的control。<br /><br />6。如果你必须要采用自己编写的TransactionManager，我可以讲解一下J2EE CONTAINER所提供的JTA/JTS的实现原理，或许对于你编写自己的TransactionManager有一定的帮助。<br /><br />7。我们知道在J2EE Spec中提供了UserTransaction和DataSource的interface的定义，具体是如何实现的留给那些J2EE Container的vendor来实现，对于application developer来说他所能见到的就是这两个interface。对于支撑这两个interface背后的东西，application developer是永远都不可能知道的。看看下面的architecture吧<br /><br />User-defined Application API<br />----------------------------<br />UserTransaction DataSource<br />----------------------------<br />DBConnectionPool ThreadConnectionMap<br />(XAConnection, XAResource, XADataSource)<br /><br />我们清楚的看到，对于UserTransaction,DataSource的支持离不开我们所看不到的J2EE spec中的一些定义，对于application developer来说，它隐藏了这些东西的复杂性。与application developer打交道的只是UserTransaction和DataSource。</span><br /><br />http://www.jdon.com/jivejdon/thread/12894.html
          <br/>
          <span style="color:red;">
            <a href="http://fengzl.javaeye.com/blog/163551#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 20 Feb 2008 10:14:19 +0800</pubDate>
        <link>http://fengzl.javaeye.com/blog/163551</link>
        <guid>http://fengzl.javaeye.com/blog/163551</guid>
      </item>
      <item>
        <title>事务扫盲贴</title>
        <author>fengzl</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://fengzl.javaeye.com">fengzl</a>&nbsp;
          链接：<a href="http://fengzl.javaeye.com/blog/163240" style="color:red;">http://fengzl.javaeye.com/blog/163240</a>&nbsp;
          发表时间: 2008年02月19日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <h2>1.资料</h2>
<ul><li><span class="nobr"><a href="http://www.infoq.com/minibooks/JTDS" title="Visit page outside Confluence" rel="nofollow">《Java Transaction Design Strategies》<sup><img class="rendericon" src="../../images/icons/linkext7.gif" border="0" height="7" align="absmiddle" alt="" width="7" /></sup></a></span>&thinsp;&nbsp;InfoQ minibook，最好的电子文档，Java事务处于懵懂状态的必读。 </li><li>《Expert One on one J2EE Development Without EJB 中文版》与<span class="nobr"><a href="http://www.redsaga.com/spring_ref/2.0/html/transaction.html" title="Visit page outside Confluence" rel="nofollow">Spring参考手册中文版<sup><img class="rendericon" src="../../images/icons/linkext7.gif" border="0" height="7" align="absmiddle" alt="" width="7" /></sup></a></span>&thinsp; 中的相关章节可以看到Spring guys的意见。</li><li><a href="http://www.leftworld.net/online/j2ee/14.htm">http://www.leftworld.net/online/j2ee/14.htm</a></li><li><a href="http://www.ibm.com/developerworks/cn/opensource/os-ag-ejbtrans3/">http://www.ibm.com/developerworks/cn/opensource/os-ag-ejbtrans3/</a></li><li><a href="http://www.ibm.com/developerworks/cn/java/l-transation/part1/">http://www.ibm.com/developerworks/cn/java/l-transation/part1/</a></li><li><a href="http://www.ibm.com/developerworks/cn/java/l-transation/part2/index.html">http://www.ibm.com/developerworks/cn/java/l-transation/part2/index.html</a></li></ul>
<h2><a name="Transaction-2.本地事务与分布式事务" title="Transaction-2.本地事务与分布式事务"></a>2.本地事务与分布式事务</h2>
<ul><li><strong>本地事务</strong><br />
    完全依赖于DB、JMS自身，，如直接调用jdbc中的conn.commit();这里没应用服务器什么事，所以也不支持多数据源的全局事务。 </li><li><strong>分布式事务</strong><br />
    在JavaEE世界的事务在JTA、JTS规范和XA Sources之上实现。<br />
    JTA是用户编程接口，JTS是服务器底层服务，两者一般由应用服务器自带实现，而<span class="nobr"><a href="http://jotm.objectweb.org/" title="Visit page outside Confluence" rel="nofollow">JOTM<sup><img class="rendericon" src="../../images/icons/linkext7.gif" border="0" height="7" align="absmiddle" alt="" width="7" /></sup></a></span>&thinsp;和JBoss Transaction是专门搞局抢生意的。<br />
    XA Sources其实先于JavaEE而存在，JDBC driver必须有javax.sql.XADataSource接口的实现类，否则所谓二阶段提交就是个伪能力。<br />
    JavaEE除了支持JDBC和JMS外，还引入了JCA模型。JCA可以说是目前唯一可移植的插入JavaEE事务的资源模型，因此像JDO这类框架/Server就是靠乖乖出自己的JCA连接器来参与JavaEE事务的。 </li></ul>
<h2><a name="Transaction-3.编程式模型" title="Transaction-3.编程式模型"></a>3.编程式模型</h2>
<p>&nbsp;&nbsp;&nbsp; 手工调用jdbc的connection事务方法和使用JTA接口都属于编程式开发，在EJB中叫BMT(Bean管理事务)。<br />
&nbsp;&nbsp;&nbsp; JTA最重要的接口就是UserTransaction和它的六个方法-begin，commit，rollback，getStatus，setRollbackonly，setTransactionTimeout。<br />
&nbsp;&nbsp;&nbsp; 程序需要UserTransaction时可以从JNDI领取，不过JNDI名随应用服务器不同而不同。EJB3里可以直接用个@Resource注入。</p>
<h2><a name="Transaction-4.宣告式模型" title="Transaction-4.宣告式模型"></a>4.宣告式模型</h2>
<p>&nbsp;&nbsp;&nbsp; 前面都是铺垫，这个才是主打的事务模型，如EJB的CMT(容器管理事务)和Sprin。</p>
<p>&nbsp;&nbsp;&nbsp; 其中EJB2.0，Spring1.0在部署描述符和applicationContext.xml中定义，而EJB3.0和Spring2.0则采用annotation。</p>
<h3><a name="Transaction-4.1事务类型" title="Transaction-4.1事务类型"></a>4.1 事务类型</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 这里JavaEE与Spring的定义基本相同：</p>
<ul><li>Required：如果Context中有事务就加入，没有就自己创建一个。(最常用设置) </li><li>Mandatory：永远加入一个事务。如果当前Context没有事务，抛出异常。(那些不打算自己负责rollback事务的方法，必须加入到别人的事务，由别人来控制rollback) </li><li>RequiresNew：永远新建一个事务。(那些不管别人如何，自己必须提交事务的方法，比如审计信息是一定要写的) </li><li>Supports：如果有事务就加入，如果没有就算了。永远不会创建新事务。(一般用于只读方法，不会主动创建事务，但如果当前有事务就加入，以读到事务中未提交的数据) </li><li>NotSupported：永远不使用事务，如果当前有事务，挂起事务。(那些有可能抛异常但异常并不影响全局的方法) </li><li>Never：不能在有当前事务的情况下调用本方法。（生人勿近?) </li></ul>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 可见，Required是默认的设置，Supports是只读方法的最佳选择。</p>
<h3><a name="Transaction-4.2事务隔离级别" title="Transaction-4.2事务隔离级别"></a>4.2 事务隔离级别</h3>
<ul><li>ReadUncommited：本事务可以看到另一事务未提交的数据。脏读。 </li><li>ReadCommited：本事务只可以看到另一事务已提交的数据。不可重复读。 </li><li>RepeatableRead：可重复读。在一个事务内，第一次读到的数据，在本事务没有提交前，无论另一个事务如何提交数据，本事务读到的数据都是不变的。 </li><li>Serializable：串行化，同时只有一个事务能读相同的数据。 </li></ul>
<p>&nbsp;&nbsp;&nbsp; 级别越低越安全效率也越低。隔离级别需要相关资源支持，如重复读在Oracle里会降级为ReadCommited。Spring里默认的Default级别完全看数据源的脸色行事。</p>
<h3><a name="Transaction-4.3关于Rollback" title="Transaction-4.3关于Rollback"></a>4.3 关于Rollback</h3>
<p>&nbsp;&nbsp;&nbsp; EJB里，想rollback只能sessionContext.setRollbackOnly()，或者抛出EJBException。(EJB3还可以annotation设置某些自定义Exception可以触发rollback)</p>
<p>&nbsp;&nbsp;&nbsp; 在Spring里，同样只会rollback unchecked exception(RuntimeExcption及子类)，而checked exception(Exception及子类)是不会rollback的，除非你特别声明。</p>
<div class="macro">
<div class="code">
<div class="codeContent">
<pre class="code-java">&nbsp;&nbsp;&nbsp;@Transactional(readOnly = <span class="code-keyword">false</span>, propagation = Propagation.REQUIRES_NEW,rollbackFor = {MyException1.class,MyException2.class})</pre>
</div>
</div>
</div>
<p>&nbsp;&nbsp;&nbsp;&nbsp;因此所有在service层方法中用throws定义的Exception，都必须在事务定义中进行rollback设定。(请勿善忘)</p>
<p>&nbsp;&nbsp;&nbsp; 所有在service层方法中c被atch处理了的异常，又希望容器辅助rollback的话，必须重抛一个预定义的RuntimeException的子类。(请勿回望)</p>
<h3><a name="Transaction-4.4关于Spring" title="Transaction-4.4关于Spring"></a>4.4 关于Spring</h3>
<p>&nbsp;&nbsp;&nbsp; Spring不希望编程式事务管理。<br />
&nbsp;&nbsp;&nbsp; Spring也不希望使用EJB CMT--CMT依赖于EJB而无法用于POJO，依赖于JTA全局事务对单数据源场景造成了浪费，而且rollback机制比较麻烦（必须为EJBException或手工setRollbackOnly())。<br />
&nbsp;&nbsp;&nbsp; 因此Spring通过AOP实现了对POJO的整套宣告式事务体系；对jdbc,hibernate,jpa,jms等local数据源和JTA实现了统一的事务管理机制，而且支持本地资源与JTA在配置文件级的切换，而且改进了rollback机制。</p>
<p>&nbsp;&nbsp; 1）一个本地事务管理器：</p>
<div class="macro">
<div class="code">
<div class="codeContent">
<pre class="code-java">&quot;transactionManager&quot;
&nbsp;&nbsp;class=<span class="code-quote">&quot;org.springframework.orm.jpa.JpaTransactionManager&quot;</span>&gt;
&nbsp;&nbsp;&quot;entityManagerFactory&quot; ref=<span class="code-quote">&quot;entityManagerFactory&quot;</span> /&gt;
&nbsp;</pre>
</div>
</div>
</div>
<p>&nbsp;</p>
<p>&nbsp;&nbsp; 2）Spring就会把请求都转发到应用服务器的JTA对象上（注意此时数据源也需要改为用JNDI从应用服务器获取)。</p>
<div class="macro">
<div class="code">
<div class="codeContent">
<pre class="code-java">&quot;myTxManager&quot; class=<span class="code-quote">&quot;org.springframework.transaction.jta.JtaTransactionManager&quot;</span>/&gt;</pre>
</div>
</div>
</div>
<p>&nbsp;</p>
<p>&nbsp;&nbsp; 3）应用服务器专有的类型的JTA事务管理器：</p>
<div class="macro">
<div class="code">
<div class="codeContent">
<pre class="code-java">&quot;myTxManager&quot; class=<span class="code-quote">&quot;org.springframework.transaction.jta.WebLogicJtaTransactionManager&quot;</span>/&gt;</pre><pre class="code-java">&nbsp;</pre><pre class="code-java">在事务管理程序周围的数字框框相应于JTA的三个接口部分：
1.UserTransaction?javax.transaction.UserTransaction
接口提供能够编程地控制事务处理范围的应用程序。
javax.transaction.UserTransaction方法开启一个全局事务并且使用调用线程与事务处理关联。
2.Transaction Manager?javax.transaction.TransactionManager接口允许应用程序服务器来控制代表正在管理的应用程序的事务范围。
3.XAResource?javax.transaction.xa.XAResource接口是一个基于X/Open CAE Specification的行业标准XA接口的Java映射。&nbsp;</pre><pre class="code-java">&nbsp;</pre><div><span style="font-size: small; color: #ff0000"><strong>Java事务处理总结</strong></span></div>
<div>&nbsp;</div>
<div>&nbsp;</div>
<div><strong><span style="color: #ff0000">一、什么是Java事务</span></strong></div>
<div>&nbsp;</div>
<div>通常的观念认为，事务仅与数据库相关。</div>
<div>&nbsp;</div>
<div>事务必须服从ISO/IEC所制定的ACID原则。ACID是原子性（atomicity）、一致性（consistency）、隔离性
（isolation）和持久性（durability）的缩写。事务的原子性表示事务执行过程中的任何失败都将导致事务所做的任何修改失效。一致性表示
当事务执行失败时，所有被该事务影响的数据都应该恢复到事务执行前的状态。隔离性表示在事务执行过程中对数据的修改，在事务提交之前对其他事务不可见。持
久性表示已提交的数据在事务执行失败时，数据的状态都应该正确。</div>
<div>&nbsp;</div>
<div>通俗的理解，事务是一组原子操作单元，从数据库角度说，就是一组SQL指令，要么全部执行成功，若因为某个原因其中一条指令执行有错误，则撤销先前执行过的所有指令。更简答的说就是：要么全部执行成功，要么撤销不执行。</div>
<div>&nbsp;</div>
<div>既然事务的概念从数据库而来，那Java事务是什么？之间有什么联系？</div>
<div>&nbsp;</div>
<div>实际上，一个Java应用系统，如果要操作数据库，则通过JDBC来实现的。增加、修改、删除都是通过相应方法间接来实现的，事务的控制也相应转移到Java程序代码中。因此，数据库操作的事务习惯上就称为Java事务。</div>
<div>&nbsp;</div>
<div><span style="color: #ff0000"><strong>二、为什么需要事务</strong></span></div>
<div>&nbsp;</div>
<div>事务是为解决数据安全操作提出的，事务控制实际上就是控制数据的安全访问。具一个简单例子：比如银行转帐业务，账户A要将自己账户上的1000
元转到B账户下面，A账户余额首先要减去1000元，然后B账户要增加1000元。假如在中间网络出现了问题，A账户减去1000元已经结束，B因为网络
中断而操作失败，那么整个业务失败，必须做出控制，要求A账户转帐业务撤销。这才能保证业务的正确性，完成这个操走就需要事务，将A账户资金减少和B账户
资金增加方到一个事务里面，要么全部执行成功，要么操作全部撤销，这样就保持了数据的安全性。</div>
<div>&nbsp;</div>
<div><span style="color: #ff0000"><strong>三、Java事务的类型</strong></span></div>
<div>&nbsp;</div>
<div>Java事务的类型有三种：JDBC事务、JTA(Java Transaction API)事务、容器事务。</div>
<div>&nbsp;</div>
<div><span style="color: #ff0000">1、JDBC事务</span></div>
<div>&nbsp;</div>
<div>JDBC 事务是用 Connection 对象控制的。JDBC Connection 接口( java.sql.Connection )提供了两种事务模式：自动提交和手工提交。 java.sql.Connection 提供了以下控制事务的方法： </div>
<div>&nbsp;</div>
<div>public void setAutoCommit(boolean) </div>
<div>public boolean getAutoCommit() </div>
<div>public void commit() </div>
<div>public void rollback() </div>
<div>&nbsp;</div>
<div>使用 JDBC 事务界定时，您可以将多个 SQL 语句结合到一个事务中。JDBC 事务的一个缺点是事务的范围局限于一个数据库连接。一个 JDBC 事务不能跨越多个数据库。</div>
<div>&nbsp;</div>
<div><span style="color: #ff0000">2、JTA(Java Transaction API)事务</span></div>
<div>&nbsp;</div>
<div>JTA是一种高层的，与实现无关的，与协议无关的API，应用程序和应用服务器可以使用JTA来访问事务。</div>
<div>&nbsp;</div>
<div>JTA允许应用程序执行分布式事务处理--在两个或多个网络计算机资源上访问并且更新数据，这些数据可以分布在多个数据库上。JDBC驱动程序的JTA支持极大地增强了数据访问能力。</div>
<div>&nbsp;</div>
<div>如果计划用 JTA 界定事务，那么就需要有一个实现 javax.sql.XADataSource 、
javax.sql.XAConnection 和 javax.sql.XAResource 接口的 JDBC
驱动程序。一个实现了这些接口的驱动程序将可以参与 JTA 事务。一个 XADataSource 对象就是一个 XAConnection
对象的工厂。 XAConnection s 是参与 JTA 事务的 JDBC 连接。 </div>
<div>&nbsp;</div>
<div>您将需要用应用服务器的管理工具设置 XADataSource 。从应用服务器和 JDBC 驱动程序的文档中可以了解到相关的指导。 </div>
<div>&nbsp;</div>
<div>J2EE 应用程序用 JNDI 查询数据源。一旦应用程序找到了数据源对象，它就调用 javax.sql.DataSource.getConnection() 以获得到数据库的连接。 </div>
<div>&nbsp;</div>
<div>XA 连接与非 XA 连接不同。一定要记住 XA 连接参与了 JTA 事务。这意味着 XA 连接不支持 JDBC
的自动提交功能。同时，应用程序一定不要对 XA 连接调用 java.sql.Connection.commit() 或者
java.sql.Connection.rollback() 。相反，应用程序应该使用 UserTransaction.begin()、
UserTransaction.commit() 和 serTransaction.rollback() 。</div>
<div>&nbsp;</div>
<div>&nbsp;</div>
<div><span style="color: #ff0000">3、容器事务</span></div>
<div>&nbsp;</div>
<div>容器事务主要是J2EE应用服务器提供的，容器事务大多是基于JTA完成，这是一个基于JNDI的，相当复杂的API实现。相对编码实现JTA
事务管理，我们可以通过EJB容器提供的容器事务管理机制（CMT）完成同一个功能，这项功能由J2EE应用服务器提供。这使得我们可以简单的指定将哪个
方法加入事务，一旦指定，容器将负责事务管理任务。这是我们土建的解决方式，因为通过这种方式我们可以将事务代码排除在逻辑编码之外，同时将所有困难交给
J2EE容器去解决。使用EJB CMT的另外一个好处就是程序员无需关心JTA API的编码，不过，理论上我们必须使用EJB。</div>
<div>&nbsp;</div>
<div><strong><span style="color: #ff0000">四、三种事务差异</span></strong></div>
<div>&nbsp;</div>
<div>1、JDBC事务控制的局限性在一个数据库连接内，但是其使用简单。</div>
<div>&nbsp;</div>
<div>2、JTA事务的功能强大，事务可以跨越多个数据库或多个DAO，使用也比较复杂。</div>
<div>&nbsp;</div>
<div>3、容器事务，主要指的是J2EE应用服务器提供的事务管理，局限于EJB应用使用。</div>
<div>&nbsp;</div>
<div>&nbsp;</div>
<div><span style="color: #ff0000"><strong>五、总结</strong></span></div>
<div>&nbsp;</div>
<div>事务控制是构建J2EE应用不可缺少的一部分，合理选择应用何种事务对整个应用系统来说至关重要。一般说来，在单个JDBC
连接连接的情况下可以选择JDBC事务，在跨多个连接或者数据库情况下，需要选择使用JTA事务，如果用到了EJB，则可以考虑使用EJB容器事务。</div><pre class="code-java">&nbsp;</pre>
</div>
</div>
</div>
          <br/>
          <span style="color:red;">
            <a href="http://fengzl.javaeye.com/blog/163240#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 19 Feb 2008 09:40:23 +0800</pubDate>
        <link>http://fengzl.javaeye.com/blog/163240</link>
        <guid>http://fengzl.javaeye.com/blog/163240</guid>
      </item>
      <item>
        <title>Spring支持JTA事务之JOTM</title>
        <author>fengzl</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://fengzl.javaeye.com">fengzl</a>&nbsp;
          链接：<a href="http://fengzl.javaeye.com/blog/163029" style="color:red;">http://fengzl.javaeye.com/blog/163029</a>&nbsp;
          发表时间: 2008年02月18日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          随着网站(www.jzease.com)的扩大，在三个月的时间里Myslq的表格从四十几张，扩到现在的一百多张，以后肯定还会继续扩大为了更好的管理和运行，着手开始分数据库，这就涉及到跨数据库事务，我们使用的是Tomcat,它本身不支持XA,供我选择的就只有spring+JTA,JTA的实现就只有两种，一种是JOTM，另一种是AtomikosTransactionsEssentials.下面是JOTM的实现:<br />DataSource:<br /><pre name="code" class="xml">&lt;bean id="jtaTxManager" class="org.springframework.transaction.jta.JtaTransactionManager">
&lt;property name="userTransaction" ref="jotmJta"/>
&lt;/bean>
&lt;!--第一个数据源,采用XAPool链接池-->
&lt;bean id="familyDataSource" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource" destroy-method="shutdown">
&lt;property name="dataSource">
&lt;bean class="org.enhydra.jdbc.standard.StandardXADataSource" destroy-method="shutdown">
&lt;property name="transactionManager" ref="jotmJta" />
&lt;property name="driverName" value="com.mysql.jdbc.Driver"/>
&lt;property name="url" value="jdbc:mysql://localhost:3306/family?useUnicode=true&characterEncoding=GBK"/>
&lt;/bean>
&lt;/property>
&lt;property name="user" value="root"/>
&lt;property name="password" value="1234"/>
&lt;/bean>
&lt;!--第二个数据源,采用XAPool链接池-->
&lt;bean id="localDataSource" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource" destroy-method="shutdown">
&lt;property name="dataSource">
&lt;bean class="org.enhydra.jdbc.standard.StandardXADataSource" destroy-method="shutdown">
&lt;property name="transactionManager" ref="jotmJta" />
&lt;property name="driverName" value="com.mysql.jdbc.Driver"/>
&lt;property name="url" value="jdbc:mysql://localhost:3306/local?useUnicode=true&characterEncoding=GBK"/>
&lt;/bean>
&lt;/property>
&lt;property name="user" value="root"/>
&lt;property name="password" value="1234"/>
&lt;/bean>
&lt;!--tx配置aop,spring2.0的用法-->
&lt;tx:annotation-driven transaction-manager="jtaTxManager"/>
DAO:
&lt;beans>
&lt;bean id="familyJdbcDAO" class="com.xband.admin.spring.LocalJdbcDAO">
&lt;property name="dataSource" ref="familyDataSource" />
&lt;/bean>

&lt;bean id="localJdbcDAO" class="com.xband.admin.spring.LocalJdbcDAO">
&lt;property name="dataSource" ref="localDataSource" />
&lt;/bean>
&lt;/beans>
</pre><br /><br />Service:<br />&lt;!--在程序里使用元数据@Transactional声明事务--><br /><pre name="code" class="xml">&lt;bean id="springService" class=" com.xband.admin.spring.SpringService">
&lt;property name="familyJdbcDAO" ref="familyHibernatedao"/>
&lt;property name="localJdbcDAO" ref="localJdbcDAO"/>
&lt;/bean></pre><br />好!如果你只用JDBC写DAO,上面的配置完全没问题，两个数据源能同时rollback(),接着我们配置Hibernater,配置如下:<br /><pre name="code" class="xml">&lt;beans>
&lt;bean id="familySessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
&lt;property name="dataSource" ref="familyDataSource"/>
&lt;property name="configLocation" value="classpath:hibernate.cfg.family.xml"/>
&lt;property name="jtaTransactionManager"> &lt;ref bean="jotmJta" /> &lt;/property>
&lt;/bean>
&lt;bean id="localSessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
&lt;property name="dataSource" ref="localDataSource"/>
&lt;property name="configLocation" value="classpath:hibernate.cfg.local.xml"/>
&lt;property name="jtaTransactionManager"> &lt;ref bean="jotmJta" /> &lt;/property>
&lt;/bean>
&lt;/beans> </pre><br /><br />http://www.javaeye.com/topic/145404
          <br/>
          <span style="color:red;">
            <a href="http://fengzl.javaeye.com/blog/163029#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 18 Feb 2008 17:46:57 +0800</pubDate>
        <link>http://fengzl.javaeye.com/blog/163029</link>
        <guid>http://fengzl.javaeye.com/blog/163029</guid>
      </item>
      <item>
        <title>JTA中跨数据库事备的实现原理----简单理解</title>
        <author>fengzl</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://fengzl.javaeye.com">fengzl</a>&nbsp;
          链接：<a href="http://fengzl.javaeye.com/blog/162986" style="color:red;">http://fengzl.javaeye.com/blog/162986</a>&nbsp;
          发表时间: 2008年02月18日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          我根据自己的经验谈一下个人看法。这是一个非常复杂的话题。三言两语可能说不清楚。<br /><br />1. Transaction 分两种，Local Transaction 和 Global Transaction。<br />涉及到一个Connection的Commit，称为Local Transaction。<br />涉及到多个Connection的Commit，称为Global Transaction。<br />楼主提到的是，Global Transaction.<br /><br />2. Global Transaction 需要XA接口（包括在JTA里面）的支持。<br /><br />import javax.sql.XAConnection;<br />import javax.transaction.xa.Xid;<br />import javax.transaction.xa.XAResource;<br />import javax.transaction.xa.XAException;<br />import javax.transaction.Transaction;<br />import javax.transaction.TransactionManager;<br /><br />其中的<br />javax.sql.XAConnection;<br />javax.transaction.xa.Xid;<br />javax.transaction.xa.XAResource;<br /><br />这些XA接口的实现，需要数据库的JDBC提供。<br />数据库本身要支持XA。数据库的JDBC也要提供XA的实现。<br /><br />Oracle, Sybase, DB2, SQL Server等大型数据库才支持XA, 支持Global Transaction。<br />My SQL 连Local Transaction都支持不好，更别说Global Transation了。<br /><br />3. XA需要两阶段提交 -- prepare 和 commit.<br />假设有两个Connection, con1, con2, 大体的过程如下，<br /><br /><pre name="code" class="java">con1 = XAResouce1.getConnection...
con2 = XAResouce2.getConnection...

con1 do some thing.
con2 do some thing.
after they finish.

pre1 = XAResouce1.prepare();
pre2 = XAResouce2.prepare();

if( both pre1 and pre2 are OK）{
XAResouce1 and 2 commit
}else {
XAResouce1 and 2 rollback
}</pre><br /><br />前面有人讲了，在XAResouce1 and 2 commit的时候，<br />可能XAResouce1 commit() 成功了，XAResouce2 commit()失败了。<br />这时候，会抛出一个 “启发式异常”。程序可以处理这个异常。比如，XAResouce.recover()之类。<br />但一般情况下，还真没别的办法，需要数据管理员根据数据操作日志 undo所有的操作，或者恢复数据备份。<br />有的数据库在进行数据操作的时候，会生成一个“反操作”日志。比如，insert 对 delete, 等。<br /><br />4. TransactionManager的实现能够处理多个XAResouce(一个XAResouce list)的情况。<br />比如Tyrex。或JBoss等EJB Server的Transaction实现代码
          <br/>
          <span style="color:red;">
            <a href="http://fengzl.javaeye.com/blog/162986#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/92' target='_blank'><span style="color:red;font-weight:bold;">快来参加7月17日在成都举行的SOA中国技术论坛</span></a></li><li><a href='/adverts/106' target='_blank'><span style="color:blue;font-weight:bold;">JavaEye问答大赛开始了！ 从6月23日 至 7月6日，奖品丰厚 ！</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/97' target='_blank'><span style="color:blue;font-weight:bold;">Oracle专区上线，有Oracle最新文章，重要下载及知识库等精彩内容，欢迎访问。</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 18 Feb 2008 15:24:09 +0800</pubDate>
        <link>http://fengzl.javaeye.com/blog/162986</link>
        <guid>http://fengzl.javaeye.com/blog/162986</guid>
      </item>
      <item>
        <title>blob、clob字段小结（五）</title>
        <author>fengzl</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://fengzl.javaeye.com">fengzl</a>&nbsp;
          链接：<a href="http://fengzl.javaeye.com/blog/162974" style="color:red;">http://fengzl.javaeye.com/blog/162974</a>&nbsp;
          发表时间: 2008年02月18日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          最后一个问题就是更新了，对于blob没什么好讲的，但是clob倒是有点需要注意的，问题是这样的，如果clob原来插入的数据内容为： 123456789，现在你update为：ABC，等你查看数据库的插入结果时你会发现更新后的数据其实是：ABC456789，相信你已经知道原因了，那么看看以下的具体处理方法吧：<br /><br /><pre name="code" class="java">            int k = 0;
            for (int i=9; i >=0; i--) {
                // 选择并锁定该记录
                LargeObject large = (LargeObject)session.load(LargeObject.class, k+"", LockMode.UPGRADE);
                large.setName("第" + k + "个改动");

                // 插入图片数据
                String fileName = "E:/AAA/" + i + ".jpg";
                SerializableBlob sb = (SerializableBlob)large.getImage();
                java.sql.Blob wrapBlob = sb.getWrappedBlob();
                // 通过非weblogic容器中数据源获得连接的情况
                if(wrapBlob instanceof oracle.sql.BLOB){
             