﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-仿冒+改良=创新-随笔分类-架构设计</title><link>http://www.blogjava.net/obpm/category/46135.html</link><description /><language>zh-cn</language><lastBuildDate>Thu, 23 Jun 2011 15:01:05 GMT</lastBuildDate><pubDate>Thu, 23 Jun 2011 15:01:05 GMT</pubDate><ttl>60</ttl><item><title>接口和抽象类的区别（转载）</title><link>http://www.blogjava.net/obpm/archive/2010/11/07/337451.html</link><dc:creator>obpm</dc:creator><author>obpm</author><pubDate>Sun, 07 Nov 2010 05:57:00 GMT</pubDate><guid>http://www.blogjava.net/obpm/archive/2010/11/07/337451.html</guid><wfw:comment>http://www.blogjava.net/obpm/comments/337451.html</wfw:comment><comments>http://www.blogjava.net/obpm/archive/2010/11/07/337451.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/obpm/comments/commentRss/337451.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/obpm/services/trackbacks/337451.html</trackback:ping><description><![CDATA[从设计理念层面看abstract class和interface <br />
<br />
上面主要从语法定义和编程的角度论述了abstract class和interface的区别，这些层面的区别是比较低层次的、非本质的。本小节将从另一个层面：abstract class和interface所反映出的设计理念，来分析一下二者的区别。作者认为，从这个层面进行分析才能理解二者概念的本质所在。 <br />
<br />
前面已经提到过，abstarct class在Java语言中体现了一种继承关系，要想使得继承关系合理，父类和派生类之间必须存在"is a"关系，即父类和派生类在概念本质上应该是相同的（参考文献〔3〕中有关于"is a"关系的大篇幅深入的论述，有兴趣的读者可以参考）。对于interface 来说则不然，并不要求interface的实现者和interface定义在概念本质上是一致的，仅仅是实现了interface定义的契约而已。为了使论述便于理解，下面将通过一个简单的实例进行说明。 <br />
<br />
考虑这样一个例子，假设在我们的问题领域中有一个关于Door的抽象概念，该Door具有执行两个动作open和close，此时我们可以通过abstract class或者interface来定义一个表示该抽象概念的类型，定义方式分别如下所示： <br />
<br />
使用abstract class方式定义Door： <br />
<br />
abstract class Door { <br />
abstract void open(); <br />
abstract void close()； <br />
} <br />
<br />
<br />
使用interface方式定义Door： <br />
<br />
<br />
interface Door { <br />
void open(); <br />
void close(); <br />
} <br />
<br />
<br />
其他具体的Door类型可以extends使用abstract class方式定义的Door或者implements使用interface方式定义的Door。看起来好像使用abstract class和interface没有大的区别。 <br />
<br />
如果现在要求Door还要具有报警的功能。我们该如何设计针对该例子的类结构呢（在本例中，主要是为了展示abstract class和interface反映在设计理念上的区别，其他方面无关的问题都做了简化或者忽略）？下面将罗列出可能的解决方案，并从设计理念层面对这些不同的方案进行分析。 <br />
<br />
解决方案一： <br />
<br />
简单的在Door的定义中增加一个alarm方法，如下： <br />
<br />
abstract class Door { <br />
abstract void open(); <br />
abstract void close()； <br />
abstract void alarm(); <br />
} <br />
<br />
<br />
或者 <br />
<br />
interface Door { <br />
void open(); <br />
void close(); <br />
void alarm(); <br />
} <br />
<br />
<br />
那么具有报警功能的AlarmDoor的定义方式如下： <br />
<br />
class AlarmDoor extends Door { <br />
void open() { &#8230; } <br />
void close() { &#8230; } <br />
void alarm() { &#8230; } <br />
} <br />
<br />
<br />
或者 <br />
<br />
class AlarmDoor implements Door ｛ <br />
void open() { &#8230; } <br />
void close() { &#8230; } <br />
void alarm() { &#8230; } <br />
｝ <br />
<br />
这种方法违反了面向对象设计中的一个核心原则ISP（Interface Segregation Priciple），在Door的定义中把Door概念本身固有的行为方法和另外一个概念"报警器"的行为方法混在了一起。这样引起的一个问题是那些仅仅依赖于Door这个概念的模块会因为"报警器"这个概念的改变（比如：修改alarm方法的参数）而改变，反之依然。 <br />
<br />
解决方案二： <br />
<br />
既然open、close和alarm属于两个不同的概念，根据ISP原则应该把它们分别定义在代表这两个概念的抽象类中。定义方式有：这两个概念都使用abstract class方式定义；两个概念都使用interface方式定义；一个概念使用abstract class方式定义，另一个概念使用interface方式定义。 <br />
<br />
显然，由于Java语言不支持多重继承，所以两个概念都使用abstract class方式定义是不可行的。后面两种方式都是可行的，但是对于它们的选择却反映出对于问题领域中的概念本质的理解、对于设计意图的反映是否正确、合理。我们一一来分析、说明。 <br />
<br />
如果两个概念都使用interface方式来定义，那么就反映出两个问题：1、我们可能没有理解清楚问题领域，AlarmDoor在概念本质上到底是Door还是报警器？2、如果我们对于问题领域的理解没有问题，比如：我们通过对于问题领域的分析发现AlarmDoor在概念本质上和Door是一致的，那么我们在实现时就没有能够正确的揭示我们的设计意图，因为在这两个概念的定义上（均使用interface方式定义）反映不出上述含义。 <br />
<br />
如果我们对于问题领域的理解是：AlarmDoor在概念本质上是Door，同时它有具有报警的功能。我们该如何来设计、实现来明确的反映出我们的意思呢？前面已经说过，abstract class在Java语言中表示一种继承关系，而继承关系在本质上是"is a"关系。所以对于Door这个概念，我们应该使用abstarct class方式来定义。另外，AlarmDoor又具有报警功能，说明它又能够完成报警概念中定义的行为，所以报警概念可以通过interface方式定义。如下所示： <br />
<br />
abstract class Door { <br />
abstract void open(); <br />
abstract void close()； <br />
} <br />
interface Alarm { <br />
void alarm(); <br />
} <br />
class AlarmDoor extends Door implements Alarm { <br />
void open() { &#8230; } <br />
void close() { &#8230; } <br />
void alarm() { &#8230; } <br />
} <br />
<br />
<br />
这种实现方式基本上能够明确的反映出我们对于问题领域的理解，正确的揭示我们的设计意图。其实abstract class表示的是"is a"关系，interface表示的是"like a"关系，大家在选择时可以作为一个依据，当然这是建立在对问题领域的理解上的，比如：如果我们认为AlarmDoor在概念本质上是报警器，同时又具有Door的功能，那么上述的定义方式就要反过来了。 <br />
<br />
转载人员-Nicholas<br />
</ca><!-- 导入 在此 参考资料--><!-- end 参考资料-->
<img src ="http://www.blogjava.net/obpm/aggbug/337451.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/obpm/" target="_blank">obpm</a> 2010-11-07 13:57 <a href="http://www.blogjava.net/obpm/archive/2010/11/07/337451.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>基于RBAC的权限设计模型</title><link>http://www.blogjava.net/obpm/archive/2010/09/14/331967.html</link><dc:creator>obpm</dc:creator><author>obpm</author><pubDate>Tue, 14 Sep 2010 05:30:00 GMT</pubDate><guid>http://www.blogjava.net/obpm/archive/2010/09/14/331967.html</guid><wfw:comment>http://www.blogjava.net/obpm/comments/331967.html</wfw:comment><comments>http://www.blogjava.net/obpm/archive/2010/09/14/331967.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/obpm/comments/commentRss/331967.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/obpm/services/trackbacks/331967.html</trackback:ping><description><![CDATA[<p align="center"><strong>权限分析文档</strong> <p>基于RBAC的权限设计模型： <p><strong>1 </strong><strong>RBAC </strong><strong>介绍</strong> <p>RBAC 模型作为目前最为广泛接受的权限模型。 <p>NIST （The National Institute of Standards and Technology，美国国家标准与技术研究院）标准RBAC模型由4个部件模型组成，这4个部件模型分别是基本模型RBAC0（Core RBAC）、角色分级模型RBAC1（Hierarchal RBAC）、角色限制模型RBAC2（Constraint RBAC）和统一模型RBAC3（Combines RBAC）<sup>[1]</sup>。RBAC0模型如图1所示。 <p><img border="0" alt="clip_image001.jpg" src="http://www.blogjava.net/images/blogjava_net/anwenhao/clip_image001.jpg" width="482" height="144"><br>图表 1 RBAC 0 模型 <p>l <strong>RBAC0 </strong><strong>定义了能构成一个RBAC控制系统的最小的元素集合</strong> <p>在RBAC之中,包含用户users(USERS)、角色roles(ROLES)、目标objects(OBS)、操作operations(OPS)、许可权permissions(PRMS)五个基本数据元素，权限被赋予角色,而不是用户，当一个角色被指定给一个用户时，此用户就拥有了该角色所包含的权限。会话sessions是用户与激活的角色集合之间的映射。RBAC0与传统访问控制的差别在于增加一层间接性带来了灵活性，RBAC1、RBAC2、RBAC3都是先后在RBAC0上的扩展。 <p>l <strong>RBAC1 </strong><strong>引入角色间的继承关系</strong> <p>角色间的继承关系可分为一般继承关系和受限继承关系。一般继承关系仅要求角色继承关系是一个绝对偏序关系，允许角色间的多继承。而受限继承关系则进一步要求角色继承关系是一个树结构。 <p>l <strong>RBAC2 </strong><strong>模型中添加了责任分离关系</strong> <p>RBAC2 的约束规定了权限被赋予角色时,或角色被赋予用户时,以及当用户在某一时刻激活一个角色时所应遵循的强制性规则。责任分离包括静态责任分离和动态责任分离。约束与用户-角色-权限关系一起决定了RBAC2模型中用户的访问许可。 <p>l <strong>RBAC3 </strong><strong>包含了RBAC1和RBAC2</strong> <p>既提供了角色间的继承关系，又提供了责任分离关系。 <p>建立角色定义表。定出当前系统中角色。 <p>因为有继承的问题，所以角色体现出的是一个树形结构。 <p><img border="0" alt="test.bmp" src="http://www.blogjava.net/images/blogjava_net/anwenhao/%E6%9C%AA%E5%91%BD%E5%90%8D1.bmp" width="603" height="407"> <p><strong>2 </strong><strong>权限设计：</strong> <p>配置资源以及资源的操作 ： 这里资源可以定义为一个通用的资源模型。提供通用的资源统一接口。 <p>数据库 ER 图： <p><img border="0" alt="clip_image002.gif" src="http://www.blogjava.net/images/blogjava_net/anwenhao/clip_image002.gif" width="553" height="322"> <p>关系图： <p><img border="0" alt="clip_image003.gif" src="http://www.blogjava.net/images/blogjava_net/anwenhao/clip_image003.gif" width="553" height="419"> <p><img border="0" alt="未命名.bmp" src="http://www.blogjava.net/images/blogjava_net/anwenhao/%E6%9C%AA%E5%91%BD%E5%90%8D.bmp" width="603" height="739"> <p><strong>3 </strong><strong>分析：</strong> <p>根据以上的类关系图和ER图可以看出。整个权限可以抽象为五个对象组成。 <p><strong>OrgBean : </strong><strong>用于描述org模型。</strong> <p><strong>Role </strong><strong>： 用于描述角色。</strong> <p><strong>Permission </strong><strong>： 用于描述权限。</strong> <p><strong>Resource </strong><strong>： 用于描述资源。</strong> <p><strong>Operation </strong><strong>： 用于描述操作。</strong> <p><strong></strong> <p><strong>其中Permission中有Resource , Operation 的聚合，资源和操作组成权限。</strong> <p><strong>Role </strong><strong>和 Permission 都有自包含。因为设计到权限的继承。</strong> <p><strong>资源Resource 也可能出现一颗树形结构，那资源也要有自包含。</strong> <p><strong></strong> <p><strong>思想 </strong><strong>:</strong> <p>权限系统的核心由以下三部分构成： 1. 创造权限， 2. 分配权限， 3. 使用权限，然后，系统各部分的主要参与者对照如下： 1. 创造权限 -Creator 创造， 2. 分配权限 - Administrator 分配， 3. 使用权限 - User ： <p>1. Creator 创造 Privilege ， Creator 在设计和实现系统时会划分，一个子系统或称为模块，应该有哪些权限。这里完成的是 Privilege 与Resource 的对象声明，并没有真正将 Privilege 与具体 Resource 实例联系在一起，形成 Operator 。 <p>2. Administrator 指定 Privilege 与 Resource Instance 的关联 。在这一步， 权限真正与资源实例联系到了一起， 产生了 Operator （Privilege Instance ）。 Administrator 利用 Operator 这个基本元素，来创造他理想中的权限模型。如，创建角色，创建用户组，给用户组分配用户，将用户组与角色关联等等 ... 这些操作都是由 Administrator 来完成的。 <p>3. User 使用 Administrator 分配给的权限去使用各个子系统。 Administrator 是用户，在他的心目中有一个比较适合他管理和维护的权限模型。于是，程序员只要回答一个问题，就是什么权限可以访问什么资源，也就是前面说的 Operator 。程序员提供 Operator 就意味着给系统穿上了盔甲。 Administrator 就可以按照他的意愿来建立他所希望的权限框架 可以自行增加，删除，管理 Resource 和 Privilege 之间关系。可以自行设定用户 User 和角色 Role 的对应关系。 ( 如果将 Creator 看作是 Basic 的发明者， Administrator 就是 Basic 的使用者，他可以做一些脚本式的编程 ) Operator 是这个系统中最关键的部分，它是一个纽带，一个系在 Programmer ， Administrator ， User 之间的纽带。 <p><strong></strong> <p><strong>4 </strong><strong>权限API</strong> <p><strong> getPermissionByOrgGuid(String orgGuid )</strong> <p>通过传入一个org的Guid ， 拿到当前这个org对象都具有那些访问权限。 <p><strong> getSourcePermissionByOrgGuid(String orgGuid , String resouceGuid)</strong> <p>通过传入一个org的Guid 和 一个资源的Guid ， 返回改Org对当前这个资源的访问权限。 <p><strong>getPermissionByResourceGuid(String resource)</strong> <p>通过传入一个资源的Guid ， 得到当前资源下都有那些权限定义。 <p><strong>havingHeritPermission(String orgGuid , String resouceGuid) : Boolean</strong> <p>传入一个orgGuid， 资源GUID ，查看改OrgGuid下对资源是否有向下继承的权限。这里继承是资源的继承。即对父栏目有权限，可以继承下去对父栏目下的子栏目同样有权限。 <p><strong>havingPermission(String orgGuid , String resourceGuid) : Boolean</strong> <p>判断某Org对某一资源是否用权限。 <p>以上是粗粒度的权限API 。 以下为细粒度的权限： <p><strong>getOperationByPermission(String permissionGuid)</strong> <p>通过permission 的Guid 得到该permission 的所有有效操作。 <p><strong>getOperationByGuid(String permissionGuid , String resourceGuid)</strong> <p>通过permision的Guid ， 资源的Guid 得到该资源下所有的有效操作。 <p><strong>screeningOpreationByGuid (String permissionGuid , String resourceGuid , String orgGuid)</strong> <p>通过permission ， resource ， org的Guid 得到改Org对这一资源的有效操作。 <p><strong>hasOperation(String operationGuid) : boolean</strong> <p>通过传入的operationGuid 返回是否具有操作权限。 <p><strong>5 </strong><strong>权限的实现：</strong> <p>1 ．表单式认证，这是常用的，但用户到达一个不被授权访问的资源时， Web 容器就发 <p>出一个 html 页面，要求输入用户名和密码。 <p>2 ．用 Filter 防止用户访问一些未被授权的资源， Filter 会截取所有 Request/Response ， <p>然后放置一个验证通过的标识在用户的 Session 中，然后 Filter 每次依靠这个标识来决定是否放行 Response 。 <p>这个模式分为： <p>Gatekeeper ：采取 Filter 或统一 Servlet 的方式。 <p>Authenticator ： 在 Web 中使用 JAAS 自己来实现。 <p>Filter 拦截只是拦截该用户是否有访问这个页面，或这一资源的权限。真正做到显示后拦截是在应用程序内部去做。 <p>做显示拦截提供API ， 标签这两种方式 <p>&nbsp; <p>转载人员：Happy <p><b>原文地址</b> <a href="http://blog.csdn.net/huanghanzzz2006/archive/2006/12/04/1429666.aspx">http://blog.csdn.net/huanghanzzz2006/archive/2006/12/04/1429666.aspx</a> <img src ="http://www.blogjava.net/obpm/aggbug/331967.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/obpm/" target="_blank">obpm</a> 2010-09-14 13:30 <a href="http://www.blogjava.net/obpm/archive/2010/09/14/331967.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>LDAP快速入门</title><link>http://www.blogjava.net/obpm/archive/2010/08/28/330195.html</link><dc:creator>obpm</dc:creator><author>obpm</author><pubDate>Sat, 28 Aug 2010 06:38:00 GMT</pubDate><guid>http://www.blogjava.net/obpm/archive/2010/08/28/330195.html</guid><wfw:comment>http://www.blogjava.net/obpm/comments/330195.html</wfw:comment><comments>http://www.blogjava.net/obpm/archive/2010/08/28/330195.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/obpm/comments/commentRss/330195.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/obpm/services/trackbacks/330195.html</trackback:ping><description><![CDATA[<p align="center"><span style="font-size: 36pt"><strong>LDAP</strong><strong>快速入门</strong></span></p>
<h1>1. LDAP简介</h1>
<p>　　LDAP（轻量级目录访问协议，Lightweight Directory Access Protocol)是实现提供被称为目录服务的信息服务。目录服务是一种特殊的数据库系统，其专门针对读取，浏览和搜索操作进行了特定的优化。目录一般用来包含描述性的，基于属性的信息并支持精细复杂的过滤能力。目录一般不支持通用数据库针对大量更新操作操作需要的复杂的事务管理或回卷策略。而目录服务的更新则一般都非常简单。这种目录可以存储包括个人信息、web链结、jpeg图像等各种信息。为了访问存储在目录中的信息，就需要使用运行在TCP/IP 之上的访问协议—LDAP。</p>
<p>&nbsp;</p>
<p>　　LDAP目录中的信息是是按照树型结构组织，具体信息存储在条目(entry)的数据结构中。条目相当于关系数据库中表的记录；条目是具有区别名DN （Distinguished Name）的属性（Attribute），DN是用来引用条目的，DN相当于关系数据库表中的关键字（Primary Key）。属性由类型（Type）和一个或多个值（Values）组成，相当于关系数据库中的字段（Field）由字段名和数据类型组成，只是为了方便检索的需要，LDAP中的Type可以有多个Value，而不是关系数据库中为降低数据的冗余性要求实现的各个域必须是不相关的。LDAP中条目的组织一般按照地理位置和组织关系进行组织，非常的直观。LDAP把数据存放在文件中，为提高效率可以使用基于索引的文件数据库，而不是关系数据库。类型的一个例子就是mail，其值将是一个电子邮件地址。</p>
<p>&nbsp;</p>
<p>LDAP的信息是以树型结构存储的，在树根一般定义国家(c=CN)或域名(dc=com)，在其下则往往定义一个或多个组织 (organization)(o=Acme)或组织单元(organizational units) (ou=People)。一个组织单元可能包含诸如所有雇员、大楼内的所有打印机等信息。此外，LDAP支持对条目能够和必须支持哪些属性进行控制，这是有一个特殊的称为对象类别(objectClass)的属性来实现的。该属性的值决定了该条目必须遵循的一些规则，其规定了该条目能够及至少应该包含哪些属性。例如：inetorgPerson对象类需要支持sn(surname)和cn(common name)属性，但也可以包含可选的如邮件，电话号码等属性。</p>
<p>&nbsp;</p>
<h1>2. LDAP简称对应</h1>
<ol>
    <li>o&#8211; organization（组织-公司）
    <li>ou &#8211; organization unit（组织单元-部门）
    <li>c - countryName（国家）
    <li>dc - domainComponent（域名）
    <li>sn &#8211; suer name（真实名称）
    <li>cn - common name（常用名称） </li>
</ol>
<p>&nbsp;</p>
<h1>3. 目录设计</h1>
<p>设计目录结构是LDAP最重要的方面之一。下面我们将通过一个简单的例子来说明如何设计合理的目录结构。该例子将通过Netscape地址薄来访文。假设有一个位于美国US(c=US)而且跨越多个州的名为Acme(o=Acme)的公司。Acme希望为所有的雇员实现一个小型的地址薄服务器。 </p>
<p>&nbsp;</p>
<p>　　我们从一个简单的组织DN开始：　</p>
<p>　　　　dn: o=Acme, c=US</p>
<p>&nbsp;</p>
<p>　　Acme所有的组织分类和属性将存储在该DN之下，这个DN在该存储在该服务器的目录是唯一的。Acme希望将其雇员的信息分为两类：管理者(ou= Managers)和普通雇员(ou=Employees),这种分类产生的相对区别名(RDN,relative distinguished names。表示相对于顶点DN)就shi ： </p>
<p>&nbsp;</p>
<p>　　　　dn: ou=Managers, o=Acme, c=US</p>
<p>　　　　dn: ou=Employees, o=Acme, c=US</p>
<p>&nbsp;</p>
<p>　　在下面我们将会看到分层结构的组成：顶点是US的Acme，下面是管理者组织单元和雇员组织单元。因此包括Managers和Employees的DN组成为： </p>
<p>　　　　dn: cn=Jason H. Smith, ou=Managers, o=Acme, c=US</p>
<p>　　　　dn: cn=Ray D. Jones, ou=Employees, o=Acme, c=US</p>
<p>　　　　dn: cn=Eric S. Woods, ou=Employees, o=Acme, c=US</p>
<p>&nbsp;</p>
<p>　　为了引用Jason H. Smith的通用名(common name )条目，LDAP将采用cn=Jason H. Smith的RDN。然后将前面的父条目结合在一起就形成如下的树型结构： </p>
<p>&nbsp;</p>
<p>　　　　cn=Jason H. Smith</p>
<p>　　　　　　　　+ ou=Managers</p>
<p>　　　　　　　　　　　　+ o=Acme</p>
<p>　　　　　　　　　　　　　　　　+ c=US</p>
<p>　&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt; dn: cn=Jason H. Smith,ou=Managers,o=Acme,c=US</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>　　现在已经定义好了目录结构，下一步就需要导入目录信息数据。目录信息数据将被存放在LDIF文件中，其是导入目录信息数据的默认存放文件。用户可以方便的编写Perl脚本来从例如/etc/passwd、NIS等系统文件中自动创建LDIF文件。</p>
<p>&nbsp;</p>
<p>　　下面的实例保存目录信息数据为testdate.ldif文件，该文件的格式说明将可以在man ldif中得到。</p>
<p>　　在添加任何组织单元以前，必须首先定义Acme DN：　 </p>
<p>　　　　dn: o=Acme, c=US</p>
<p>　　　　objectClass: organization</p>
<p>&nbsp;</p>
<p>　　这里o属性是必须的 </p>
<p>　　　　o: Acme</p>
<p>&nbsp;</p>
<p>　　下面是管理组单元的DN，在添加任何管理者信息以前，必须先定义该条目。 </p>
<p>　　　　dn: ou=Managers, o=Acme, c=US</p>
<p>　　　　objectClass: organizationalUnit</p>
<p>这里ou属性是必须的。 </p>
<p>&nbsp;</p>
<p>ou: Managers</p>
<p>　　第一个管理者DN： </p>
<p>　　　　dn: cn=Jason H. Smith, ou=Managers, o=Acme, c=US</p>
<p>　　　　objectClass: inetOrgPerson</p>
<p>　　cn和sn都是必须的属性： </p>
<p>　　　　cn: Jason H. Smith</p>
<p>　　　　sn: Smith</p>
<p>　　但是还可以定义一些可选的属性：</p>
<p>　　　　telephoneNumber: 111-222-9999</p>
<p>　　　　mail: headhauncho@acme.com</p>
<p>　　　　localityName: <st1:place><st1:city>Houston</st1:city></st1:place></p>
<p>&nbsp;</p>
<p>　　可以定义另外一个组织单元： </p>
<p>　　　　dn: ou=Employees, o=Acme, c=US</p>
<p>　　　　objectClass: organizationalUnit</p>
<p>　　　　ou: Employees</p>
<p>&nbsp;</p>
<p>　　并添加雇员信息如下： </p>
<p>　　　　dn: cn=Ray D. Jones, ou=Employees, o=Acme, c=US</p>
<p>　　　　objectClass: inetOrgPerson</p>
<p>　　　　cn: Ray D. Jones</p>
<p>　　　　sn: Jones</p>
<p>　　　　telephoneNumber: 444-555-6767</p>
<p>　　　　mail: jonesrd@acme.com</p>
<p>　　　　localityName: <st1:place><st1:city>Houston</st1:city></st1:place></p>
<p>　　　　dn: cn=Eric S. Woods, ou=Employees, o=Acme, c=US</p>
<p>　　　　objectClass: inetOrgPerson</p>
<p>　　　　cn: Eric S. Woods</p>
<p>　　　　sn: Woods</p>
<p>　　　　telephoneNumber: 444-555-6768</p>
<p>　　　　mail: woodses@acme.com</p>
<p>　　　　localityName: <st1:place><st1:city>Houston</st1:city></st1:place></p>
<p><st1:place><st1:city></st1:city></st1:place>&nbsp;</p>
<p>&nbsp;</p>
<h1>4. 配置OpenLDAP</h1>
<p>本文实践了在 Windows 下安装配 openldap，并添加一个条目，LdapBrowser 浏览，及 Java 程序连接 openldap 的全过程。</p>
<p>&nbsp;</p>
<p>1. 下载安装 <strong>openldap</strong> for windows，当前版本<st1:chsdate isrocdate="False" islunardate="False" day="30" month="12" year="1899">2.2.29</st1:chsdate>下载地址：http://download.bergmans.us/openldap/openldap-<st1:chsdate isrocdate="False" islunardate="False" day="30" month="12" year="1899">2.2.29</st1:chsdate>/openldap-2.2.29-db-4.3.29-openssl-0.<st1:chmetcnv tcsc="0" numbertype="1" negative="False" hasspace="False" sourcevalue="9.8" unitname="a">9.8a</st1:chmetcnv>-win32_Setup.exe</p>
<p>&nbsp;&nbsp;&nbsp; 相关链接：http://lucas.bergmans.us/hacks/openldap/</p>
<p>&nbsp;&nbsp; 安装很简单，一路 next 即可，假设我们安装在 c:\openldap</p>
<p>&nbsp;</p>
<p>2. 配置 openldap，编辑 sldap.conf 文件</p>
<p>&nbsp;&nbsp; 1) 打开 c:\openldap\sldap.conf，找到</p>
<p>&nbsp;&nbsp;&nbsp; include&nbsp; C:/openldap/etc/schema/core.schema，在它后面添加</p>
<p>&nbsp;&nbsp;&nbsp; include&nbsp; C:/openldap/etc/schema/cosine.schema</p>
<p>&nbsp;&nbsp;&nbsp; include&nbsp; C:/openldap/etc/schema/inetorgperson.schema</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp; 接下来的例子只需要用到以上三个 schema，当然，如果你觉得需要的话，你可以把其他的 schema 全部添加进来</p>
<p>&nbsp;&nbsp;&nbsp; include&nbsp; C:/openldap/etc/schema/corba.schema</p>
<p>&nbsp;&nbsp;&nbsp; include&nbsp; C:/openldap/etc/schema/dyngroup.schema</p>
<p>&nbsp;&nbsp;&nbsp; include&nbsp; C:/openldap/etc/schema/java.schema</p>
<p>&nbsp;&nbsp;&nbsp; include&nbsp; C:/openldap/etc/schema/misc.schema</p>
<p>&nbsp;&nbsp;&nbsp; include&nbsp; C:/openldap/etc/schema/nis.schema</p>
<p>&nbsp;&nbsp;&nbsp; include&nbsp; C:/openldap/etc/schema/openldap.schema</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp; 2) 还是在 sldap.conf 文件中，找到</p>
<p>&nbsp;&nbsp;&nbsp; suffix&nbsp; "dc=my-domain,dc=com"</p>
<p>&nbsp;&nbsp;&nbsp; rootdn&nbsp; "cn=Manager,dc=my-domain,dc=com"</p>
<p>&nbsp;&nbsp;&nbsp; 把这两行改为</p>
<p>&nbsp;&nbsp;&nbsp; suffix "o=teemlink,c=cn"&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; rootdn "cn=Manager,o=teemlink,dc=cn" </p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp; suffix 就是看自己如何定义了，后面步骤的 ldif 文件就必须与它定义了。还要注意到这个配置文件中有一个 rootpw&nbsp; secret，这个 secret 是 cn=Manager 的密码，以后会用到，不过这里是明文密码，你可以用命令： slappasswd -h {MD5} -s secret 算出加密的密码 {MD5}Xr4ilOzQ4PCOq3aQ0qbuaQ== 取代配置中的 secret。</p>
<p>&nbsp;</p>
<p>3. 启动 openldap</p>
<p>&nbsp;&nbsp;&nbsp; CMD 进入到 c:\openldap 下，运行命令 sldapd -d 1</p>
<p>&nbsp;&nbsp;&nbsp; 用可以看到控制台下打印一片信息，openldap 默认是用的 Berkeley DB 数据库存储目录数据的。</p>
<p>&nbsp;</p>
<p>4. 建立条目,编辑导入 ldif 文件</p>
<p>&nbsp;&nbsp; 1) 新建一个 ldif(LDAP Data Interchanged Format) 文件(纯文本格式)，例如 test.ldif，文件内容如下：</p>
<p>&nbsp;&nbsp;&nbsp; </p>
<p>dn: o=teemlink</p>
<p>objectclass: top</p>
<p>objectclass: organization</p>
<p>o: develop</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp; 2) 执行命令：ldapadd -l test.ldif</p>
<p>&nbsp;</p>
<p>5. 使用LDAP Browser进行访问</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5.1安装LDAP Browser2.6软件，进行如下操作：</p>
<p>&nbsp;&nbsp;&nbsp; <img alt="" src="http://pic002.cnblogs.com/img/obpm/201008/2010082814190153.jpg" /></p>
<p>&nbsp;　<img alt="" src="http://pic002.cnblogs.com/img/obpm/201008/2010082814200667.jpg" /></p>
<p>&nbsp;</p>
<p>5.2显示效果</p>
<p>　 <img alt="" src="http://pic002.cnblogs.com/img/obpm/201008/2010082814363213.jpg" /></p>
<p>&nbsp;</p>
<h1>5. Java操作LDAP</h1>
<p>&nbsp;</p>
<h2>5.1 用JNDI进访问</h2>
<div class="cnblogs_code" style="width: 718px; height: 885px">
<pre>
<div><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000ff">package</span><span style="color: #000000"> cn.myapps.test;<br />
<br />
</span><span style="color: #0000ff">import</span><span style="color: #000000"> java.util.Hashtable;<br />
</span><span style="color: #0000ff">import</span><span style="color: #000000"> javax.naming.Context;<br />
</span><span style="color: #0000ff">import</span><span style="color: #000000"> javax.naming.NamingException;<br />
</span><span style="color: #0000ff">import</span><span style="color: #000000"> javax.naming.directory.Attributes;<br />
</span><span style="color: #0000ff">import</span><span style="color: #000000"> javax.naming.directory.DirContext;<br />
</span><span style="color: #0000ff">import</span><span style="color: #000000"> javax.naming.directory.InitialDirContext;<br />
<br />
</span><span style="color: #0000ff">public</span><span style="color: #000000"> </span><span style="color: #0000ff">class</span><span style="color: #000000"> LdapTest {<br />
</span><span style="color: #0000ff">public</span><span style="color: #000000"> </span><span style="color: #0000ff">void</span><span style="color: #000000"> JNDILookup() {<br />
String root </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #000000">"</span><span style="color: #000000">o=teemlink,c=cn</span><span style="color: #000000">"</span><span style="color: #000000">;<br />
Hashtable env </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #0000ff">new</span><span style="color: #000000"> Hashtable();<br />
env.put(Context.INITIAL_CONTEXT_FACTORY, </span><span style="color: #000000">"</span><span style="color: #000000">com.sun.jndi.ldap.LdapCtxFactory</span><span style="color: #000000">"</span><span style="color: #000000">);<br />
env.put(Context.PROVIDER_URL, </span><span style="color: #000000">"</span><span style="color: #000000">ldap://192.168.0.30/</span><span style="color: #000000">"</span><span style="color: #000000"> </span><span style="color: #000000">+</span><span style="color: #000000"> root);<br />
env.put(Context.SECURITY_AUTHENTICATION, </span><span style="color: #000000">"</span><span style="color: #000000">simple</span><span style="color: #000000">"</span><span style="color: #000000">);<br />
env.put(Context.SECURITY_PRINCIPAL, </span><span style="color: #000000">"</span><span style="color: #000000">cn=Nicholas,ou=develop,o=teemlink,c=cn</span><span style="color: #000000">"</span><span style="color: #000000">);<br />
env.put(Context.SECURITY_CREDENTIALS, </span><span style="color: #000000">"</span><span style="color: #000000">123456</span><span style="color: #000000">"</span><span style="color: #000000">);<br />
DirContext ctx </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #0000ff">null</span><span style="color: #000000">;<br />
<br />
</span><span style="color: #0000ff">try</span><span style="color: #000000"> {<br />
ctx </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #0000ff">new</span><span style="color: #000000"> InitialDirContext(env);<br />
Attributes attrs </span><span style="color: #000000">=</span><span style="color: #000000"> ctx.getAttributes(</span><span style="color: #000000">"</span><span style="color: #000000">cn=Nicholas,ou=develop</span><span style="color: #000000">"</span><span style="color: #000000">);<br />
System.out.println(</span><span style="color: #000000">"</span><span style="color: #000000">Last Name: </span><span style="color: #000000">"</span><span style="color: #000000"> </span><span style="color: #000000">+</span><span style="color: #000000"> attrs.get(</span><span style="color: #000000">"</span><span style="color: #000000">sn</span><span style="color: #000000">"</span><span style="color: #000000">).get());<br />
System.out.println(</span><span style="color: #000000">"</span><span style="color: #000000">认证成功</span><span style="color: #000000">"</span><span style="color: #000000">);<br />
} </span><span style="color: #0000ff">catch</span><span style="color: #000000"> (javax.naming.AuthenticationException e) {<br />
e.printStackTrace();<br />
System.out.println(</span><span style="color: #000000">"</span><span style="color: #000000">认证失败</span><span style="color: #000000">"</span><span style="color: #000000">);<br />
} </span><span style="color: #0000ff">catch</span><span style="color: #000000"> (Exception e) {<br />
System.out.println(</span><span style="color: #000000">"</span><span style="color: #000000">认证出错：</span><span style="color: #000000">"</span><span style="color: #000000">);<br />
e.printStackTrace();<br />
}<br />
</span><span style="color: #0000ff">if</span><span style="color: #000000"> (ctx </span><span style="color: #000000">!=</span><span style="color: #000000"> </span><span style="color: #0000ff">null</span><span style="color: #000000">) {<br />
</span><span style="color: #0000ff">try</span><span style="color: #000000"> {<br />
ctx.close();<br />
} </span><span style="color: #0000ff">catch</span><span style="color: #000000"> (NamingException e) {<br />
</span><span style="color: #008000">//</span><span style="color: #008000"> ignore</span><span style="color: #008000"><br />
</span><span style="color: #000000">            }<br />
}<br />
}<br />
<br />
</span><span style="color: #0000ff">public</span><span style="color: #000000"> </span><span style="color: #0000ff">static</span><span style="color: #000000"> </span><span style="color: #0000ff">void</span><span style="color: #000000"> main(String[] args) {<br />
LdapTest LDAPTest </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #0000ff">new</span><span style="color: #000000"> LdapTest();<br />
LDAPTest.JNDILookup();<br />
}<br />
}</span></div>
</pre>
</div>
<p>&nbsp;</p>
<h2>5.2 用JLDAP进访问</h2>
<p>访问地址：<a href="http://www.openldap.org/jldap/">http://www.openldap.org/jldap/</a> 并下载相关lib</p>
<p>&nbsp;</p>
<div class="cnblogs_code">
<pre>
<div><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000ff">import</span><span style="color: #000000"> com.novell.ldap.</span><span style="color: #000000">*</span><span style="color: #000000">;<br />
<br />
</span><span style="color: #0000ff">import</span><span style="color: #000000"> java.io.UnsupportedEncodingException;<br />
<br />
</span><span style="color: #0000ff">public</span><span style="color: #000000"> </span><span style="color: #0000ff">class</span><span style="color: #000000"> List<br />
<br />
{<br />
<br />
</span><span style="color: #0000ff">public</span><span style="color: #000000"> </span><span style="color: #0000ff">static</span><span style="color: #000000"> </span><span style="color: #0000ff">void</span><span style="color: #000000"> main(String[] args)<br />
<br />
{<br />
</span><span style="color: #0000ff">int</span><span style="color: #000000"> ldapPort </span><span style="color: #000000">=</span><span style="color: #000000"> LDAPConnection.DEFAULT_PORT;<br />
</span><span style="color: #0000ff">int</span><span style="color: #000000"> searchScope </span><span style="color: #000000">=</span><span style="color: #000000"> LDAPConnection.SCOPE_ONE;<br />
</span><span style="color: #0000ff">int</span><span style="color: #000000"> ldapVersion </span><span style="color: #000000">=</span><span style="color: #000000"> LDAPConnection.LDAP_V3;<br />
</span><span style="color: #0000ff">boolean</span><span style="color: #000000"> attributeOnly </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #0000ff">false</span><span style="color: #000000">;<br />
String attrs[] </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #0000ff">null</span><span style="color: #000000">;<br />
String ldapHost </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #000000">"</span><span style="color: #000000">192.168.0.30</span><span style="color: #000000">"</span><span style="color: #000000">;<br />
String loginDN </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #000000">"</span><span style="color: #000000">cn=Manager,o=teemlink,c=cn</span><span style="color: #000000">"</span><span style="color: #000000">;<br />
String password </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #000000">"</span><span style="color: #000000">secret</span><span style="color: #000000">"</span><span style="color: #000000">;<br />
String searchBase </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #000000">"</span><span style="color: #000000">ou=develop,o=teemlink,c=cn</span><span style="color: #000000">"</span><span style="color: #000000">;<br />
String searchFilter </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #000000">"</span><span style="color: #000000">objectClass=*</span><span style="color: #000000">"</span><span style="color: #000000">;<br />
<br />
LDAPConnection lc </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #0000ff">new</span><span style="color: #000000"> LDAPConnection();<br />
</span><span style="color: #0000ff">try</span><span style="color: #000000"> {<br />
</span><span style="color: #008000">//</span><span style="color: #008000"> connect to the server</span><span style="color: #008000"><br />
</span><span style="color: #000000">            lc.connect(ldapHost, ldapPort);<br />
<br />
</span><span style="color: #008000">//</span><span style="color: #008000"> bind to the server</span><span style="color: #008000"><br />
</span><span style="color: #000000">            lc.bind(ldapVersion, loginDN, password.getBytes(</span><span style="color: #000000">"</span><span style="color: #000000">UTF8</span><span style="color: #000000">"</span><span style="color: #000000">));<br />
<br />
LDAPSearchResults searchResults </span><span style="color: #000000">=</span><span style="color: #000000"><br />
<br />
lc.search(searchBase, </span><span style="color: #008000">//</span><span style="color: #008000"> container to search</span><span style="color: #008000"><br />
</span><span style="color: #000000">                    searchScope, </span><span style="color: #008000">//</span><span style="color: #008000"> search scope</span><span style="color: #008000"><br />
</span><span style="color: #000000">                    searchFilter, </span><span style="color: #008000">//</span><span style="color: #008000"> search filter</span><span style="color: #008000"><br />
</span><span style="color: #000000">                    attrs, </span><span style="color: #008000">//</span><span style="color: #008000"> "1.1" returns entry name only</span><span style="color: #008000"><br />
</span><span style="color: #000000">                    attributeOnly); </span><span style="color: #008000">//</span><span style="color: #008000"> no attributes are returned<br />
<br />
</span><span style="color: #008000">//</span><span style="color: #008000"> print out all the objects</span><span style="color: #008000"><br />
</span><span style="color: #000000">            </span><span style="color: #0000ff">while</span><span style="color: #000000"> (searchResults.hasMore()) {<br />
LDAPEntry nextEntry </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #0000ff">null</span><span style="color: #000000">;<br />
</span><span style="color: #0000ff">try</span><span style="color: #000000"> {<br />
nextEntry </span><span style="color: #000000">=</span><span style="color: #000000"> searchResults.next();<br />
System.out.println(</span><span style="color: #000000">"</span><span style="color: #000000">\n</span><span style="color: #000000">"</span><span style="color: #000000"> </span><span style="color: #000000">+</span><span style="color: #000000"> nextEntry.getDN());<br />
System.out.println(nextEntry.getAttributeSet());<br />
} </span><span style="color: #0000ff">catch</span><span style="color: #000000"> (LDAPException e) {<br />
System.out.println(</span><span style="color: #000000">"</span><span style="color: #000000">Error: </span><span style="color: #000000">"</span><span style="color: #000000"> </span><span style="color: #000000">+</span><span style="color: #000000"> e.toString());<br />
</span><span style="color: #008000">//</span><span style="color: #008000"> Exception is thrown, go for next entry</span><span style="color: #008000"><br />
</span><span style="color: #000000">                    </span><span style="color: #0000ff">continue</span><span style="color: #000000">;<br />
}<br />
}<br />
<br />
</span><span style="color: #008000">//</span><span style="color: #008000"> disconnect with the server</span><span style="color: #008000"><br />
</span><span style="color: #000000">            lc.disconnect();<br />
<br />
} </span><span style="color: #0000ff">catch</span><span style="color: #000000"> (LDAPException e) {<br />
System.out.println(</span><span style="color: #000000">"</span><span style="color: #000000">Error: </span><span style="color: #000000">"</span><span style="color: #000000"> </span><span style="color: #000000">+</span><span style="color: #000000"> e.toString());<br />
} </span><span style="color: #0000ff">catch</span><span style="color: #000000"> (UnsupportedEncodingException e) {<br />
System.out.println(</span><span style="color: #000000">"</span><span style="color: #000000">Error: </span><span style="color: #000000">"</span><span style="color: #000000"> </span><span style="color: #000000">+</span><span style="color: #000000"> e.toString());<br />
}<br />
System.exit(</span><span style="color: #000000">0</span><span style="color: #000000">);<br />
}<br />
}</span></div>
</pre>
</div>
<p>&nbsp;</p>
<h2>5.3 用JDBC-LDAP进访问</h2>
<p>访问地址：<a href="http://www.openldap.org/jdbcldap/">http://www.openldap.org/jdbcldap/</a> 并下载相关lib</p>
<p>&nbsp;</p>
<div class="cnblogs_code" style="width: 929px; height: 561px">
<pre>
<div><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000ff">package</span><span style="color: #000000"> jdbcldap;<br />
<br />
</span><span style="color: #0000ff">import</span><span style="color: #000000"> java.sql.Connection;<br />
</span><span style="color: #0000ff">import</span><span style="color: #000000"> java.sql.DriverManager;<br />
</span><span style="color: #0000ff">import</span><span style="color: #000000"> java.sql.ResultSet;<br />
</span><span style="color: #0000ff">import</span><span style="color: #000000"> java.sql.Statement;<br />
<br />
</span><span style="color: #0000ff">public</span><span style="color: #000000"> </span><span style="color: #0000ff">class</span><span style="color: #000000"> JdbcLdap {<br />
<br />
</span><span style="color: #008000">/**</span><span style="color: #008000"><br />
* </span><span style="color: #808080">@param</span><span style="color: #008000"> args<br />
* </span><span style="color: #808080">@throws</span><span style="color: #008000"> Exception <br />
</span><span style="color: #008000">*/</span><span style="color: #000000"><br />
</span><span style="color: #0000ff">public</span><span style="color: #000000"> </span><span style="color: #0000ff">static</span><span style="color: #000000"> </span><span style="color: #0000ff">void</span><span style="color: #000000"> main(String[] args) </span><span style="color: #0000ff">throws</span><span style="color: #000000"> Exception {<br />
Class.forName(</span><span style="color: #000000">"</span><span style="color: #000000">com.octetstring.jdbcLdap.sql.JdbcLdapDriver</span><span style="color: #000000">"</span><span style="color: #000000">);<br />
String ldapConnectString </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #000000">"</span><span style="color: #000000">jdbc:ldap://192.168.0.30/o=teemlink,c=cn?SEARCH_SCOPE:=subTreeScope</span><span style="color: #000000">"</span><span style="color: #000000">;<br />
Connection con </span><span style="color: #000000">=</span><span style="color: #000000"> DriverManager.getConnection(ldapConnectString, </span><span style="color: #000000">"</span><span style="color: #000000">cn=Manager,o=teemlink,c=cn</span><span style="color: #000000">"</span><span style="color: #000000">, </span><span style="color: #000000">"</span><span style="color: #000000">secret</span><span style="color: #000000">"</span><span style="color: #000000">);<br />
<br />
String sql </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #000000">"</span><span style="color: #000000">SELECT * FROM ou=develop,o=teemlink,c=cn</span><span style="color: #000000">"</span><span style="color: #000000">;<br />
<br />
Statement sat </span><span style="color: #000000">=</span><span style="color: #000000"> con.createStatement();<br />
ResultSet rs </span><span style="color: #000000">=</span><span style="color: #000000"> sta.executeQuery(sql);<br />
</span><span style="color: #0000ff">while</span><span style="color: #000000"> (rs.next()) {<br />
System.out.println(rs.getString(</span><span style="color: #000000">1</span><span style="color: #000000">));<br />
}<br />
<br />
</span><span style="color: #0000ff">if</span><span style="color: #000000"> (con </span><span style="color: #000000">!=</span><span style="color: #000000"> </span><span style="color: #0000ff">null</span><span style="color: #000000">)<br />
con.close();<br />
}<br />
}</span></div>
</pre>
</div>
<p>&nbsp;</p>
<p>原创人员：Nicholas</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<br />
文章来源:<a href="http://www.cnblogs.com/obpm/archive/2010/08/28/1811065.html">http://www.cnblogs.com/obpm/archive/2010/08/28/1811065.html</a> 
<img src ="http://www.blogjava.net/obpm/aggbug/330195.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/obpm/" target="_blank">obpm</a> 2010-08-28 14:38 <a href="http://www.blogjava.net/obpm/archive/2010/08/28/330195.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>重构实战案例一，以子类取代类型编码(原创)</title><link>http://www.blogjava.net/obpm/archive/2010/07/13/330196.html</link><dc:creator>obpm</dc:creator><author>obpm</author><pubDate>Tue, 13 Jul 2010 15:22:00 GMT</pubDate><guid>http://www.blogjava.net/obpm/archive/2010/07/13/330196.html</guid><wfw:comment>http://www.blogjava.net/obpm/comments/330196.html</wfw:comment><comments>http://www.blogjava.net/obpm/archive/2010/07/13/330196.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/obpm/comments/commentRss/330196.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/obpm/services/trackbacks/330196.html</trackback:ping><description><![CDATA[<p style="text-align: center;"><span style="font-size: 24pt;">以子类取代类型编码</span> </p>
<p style="text-align: center;">&nbsp;<span style="font-size: 24pt;">Replace Type Code with Subclasses</span> </p>
<h1 style="text-align: left;"></h1>
<h1 style="text-align: left;">1. 何谓重构</h1>
<p style="text-align: left;">&nbsp;</p>
<h2 style="text-align: left;">1.1名词解释</h2>
<p style="text-align: left;">　　<span style="font-size: 12pt;">对软件内部结构的㆒种调整，目的是在不改变「软件之可察行为」前提下，提高其可理解性，降低其修改成本。</span></p>
<p style="text-align: left;"><span style="font-size: 12pt;">&nbsp;</span>&nbsp;</p>
<h2>1.2动词解释</h2>
<p>　　<span style="font-size: 12pt;">使用一系列重构准则（手法），在不改变「软件之可察行为」前提下，调整其结构。</span> </p>
<p>&nbsp;</p>
<h1>2. 为何重构</h1>
<p>&nbsp;</p>
<h2>2.1「重构」改进软件设计</h2>
<p>　　<span style="font-size: 12pt;">同样完成一件事，设计不良的程序往往需要更多代码，<b>这常常是因为代码在不同的</b><b>地</b><b>方使用完全相同的语句做同样的事</b>。因此改进设计的一个重要方向就是消除重复代码<b><span style="color: #0000ff;">（Duplicate Code）</span></b></span></p>
<p><span style="font-size: 12pt;"><b><span style="color: #0000ff;">&nbsp;</span></b></span></p>
<h2>2.2「重构」使软件更易被理解</h2>
<p>　　<span style="font-size: 12pt;">你的源码还有其它读者：<b>数个月之后可能会有另一位程序员尝试读懂你的代码并做一些修改</b>。我们很容易忘记这第二位读者，但他才是最重要的。计算器是否多花了数个钟头进行编译，又有什么关系呢？如果一个程序员花费一周时间来修改某段代码，那才关系重大&mdash; 如果他理解你的代码，这个修改原本只需一小时 </span></p>
<p><span style="font-size: 12pt;">&nbsp;</span></p>
<h2>2.3「重构」助你找到臭虫 （ bugs）</h2>
<p>　　<span style="font-size: 12pt;">Kent Beck 经常形容自己的㆒句话：『<b>我不是个伟大的程序员；我只是个有着</b><b>㆒</b><b>些优秀习惯的好程序员而已</b>。』重构能够帮助我更有效地写出强固稳健（robust）的代码。<b>&nbsp;</b></span></p>
<p><span style="font-size: 12pt;"><strong>&nbsp;</strong></span></p>
<h2>2.4「重构」助你提高编程速度</h2>
<p>　　<span style="font-size: 12pt;">终于，前面的一切都归结到了这最后一点：重构帮助你更快速地开发程序。听起来有违反直觉。当我谈到重构，人们很容易看出它能够提高质量。<b>改善设计、提升可读性、减少错误</b>，这些都是提高质量。但这难道不会降低开发速度吗？我强烈相信：良好设计是快速软件开发的根本。事实上拥有良好设计才可能达成快速的开发。<b>如果没有良好设计，或许某</b><b>一</b><b>段时间内你的进展迅速，但恶劣的设计很快就让你的速度慢</b><b>下</b><b>来。你会把时间花在调试</b><b>上</b><b>面，无法添加新功能</b>。修改时间愈来愈长，因为你必须花愈来愈多的时间去理解系统、寻找重复代码。随着你给最初程序打上一个又一个的补丁（patch），新特性需要更多代码才能实现。真是个恶性循环。 </span></p>
<h1></h1>
<h1>&nbsp;</h1>
<h1>3.何时重构？</h1>
<p><b>　　<span style="font-size: 12pt;">重构本来就不是一件「特别拨出时间做」的事情，重构应该随时随地进行</span></b><span style="font-size: 12pt;">。你不应该为重构而重构，你之所以重构，是因为你想做别的什么事，而重构可以帮助你把那些事做好 </span></p>
<p><span style="font-size: 12pt;">&nbsp;</span></p>
<h2>3.1三次法则（The Rule of Three）</h2>
<p>　　<span style="font-size: 12pt;">Don Roberts 给了我一条准则：第一次做某件事时只管去做；第二次做类似的事会产生反感，但无论如何还是做了；第三次再做类似的事，你就应该重构。 </span></p>
<p><span style="font-size: 12pt;">　　☆&nbsp;<b>事不过</b><b>三</b><b>，</b><b>三</b><b>则重构。（Three strikes and you refactor.</b><b>）</b></span></p>
<p><span style="font-size: 12pt;"><strong>&nbsp;</strong></span></p>
<h2>3.2重构时机</h2>
<p><span style="font-size: 12pt;">　　添加功能时一并重构</span></p>
<p><span style="font-size: 12pt;">　　修补错误时一并重构</span></p>
<p><span style="font-size: 12pt;">　　复审代码时一并重构 </span></p>
<p>&nbsp;</p>
<p style="text-align: left;"><b><span style="color: #ff0000;"><span style="font-size: 14pt;">-以上章节摘抄自《重构-改善既有代码的设计<b>》</b></span></span></b></p>
<p style="text-align: left;"><b><span style="color: #ff0000;"><span style="font-size: 14pt;">&nbsp;</span></span></b></p>
<h1>4.平台重构案例</h1>
<p>&nbsp;</p>
<h2>4.1重构动机</h2>
<p>　　<span style="font-size: 12pt;">1. 实例模块为View，重构元素为ViewAction、ViewProcessBean、View，以下为关系图。</span></p>
<p>　　<img src="http://pic002.cnblogs.com/img/obpm/201007/2010071809261958.gif" /></p>
<p>　　<span style="font-size: 12pt;">2. <b>由于View</b><b>存在多种editMode</b><b>（编辑模式），而每个调用的地方都需要进行type code</b><b>（类型码）判断</b>，然后再进行相应的业务逻辑处理，最终在每个调用的地方都形成了大量的if-else代码，大大减弱了代码的可读性，和逻辑清晰度。</span> </p>
<p>&nbsp;</p>
<p>　　<span style="font-size: 12pt;">3. 调用的地方：</span></p>
<p>　　<img src="http://pic002.cnblogs.com/img/obpm/201007/2010071809284361.jpg" /></p>
<h2>4.2重构作法</h2>
<p>　　<img src="http://pic002.cnblogs.com/img/obpm/201007/2010071809335620.jpg" /></p>
<p>&nbsp;</p>
<p><span style="font-size: 12pt;">如上图所示，将每种type code重构成subclass，加强了每种类型处理业务逻辑的能力。</span></p>
<p>&nbsp;</p>
<p><span style="font-size: 12pt;">View-版本1566代码片段：</span></p>
<p>&nbsp;</p>
<div class="cnblogs_code">
<pre><div><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000ff;">public</span><span style="color: #000000;"> EditMode getEditModeType() {<br />    </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (EDIT_MODE_CODE_DQL.equals(getEditMode())) {<br />        </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> DQLEditMode(</span><span style="color: #0000ff;">this</span><span style="color: #000000;">);<br />    } </span><span style="color: #0000ff;">else</span><span style="color: #000000;"> </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (EDIT_MODE_CODE_SQL.equals(getEditMode())) {<br />        </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> SQLEditMode(</span><span style="color: #0000ff;">this</span><span style="color: #000000;">);<br />    } </span><span style="color: #0000ff;">else</span><span style="color: #000000;"> </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (EDIT_MODE_DESIGN.equals(getEditMode())) {<br />        </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> DesignEditMode(</span><span style="color: #0000ff;">this</span><span style="color: #000000;">);<br />    }<br /><br />    </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> NullEditMode(</span><span style="color: #0000ff;">this</span><span style="color: #000000;">);<br />}<br /></span></div></pre>
</div>
<p><span style="font-size: 12pt;">说明：调用者无需了解具体的类型，由View自身作判断，返回EditMode接口，从而实现多态调用。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span style="font-size: 12pt;">ViewProcessBean代码片段：</span></p>
<p><span style="font-size: 12pt;">重构前-版本1503：</span></p>
<p>&nbsp;</p>
<div style="width: 813px; height: 243px;" class="cnblogs_code">
<pre><div><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000ff;">public</span><span style="color: #000000;"> String expDocToExcel(String viewid, WebUser user, ParamsTable params) </span><span style="color: #0000ff;">throws</span><span style="color: #000000;"> Exception {<br />    </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (view.getEditMode().equals(View.EDIT_MODE_DESIGN)) {<br />        datas </span><span style="color: #000000;">=</span><span style="color: #000000;"> dp.queryBySQLPage(sql, params, tempPage, LINES, user.getDomainid());<br />    } </span><span style="color: #0000ff;">else</span><span style="color: #000000;"> </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (view.getEditMode().equals(View.EDIT_MODE_CODE_DQL)) {<br />        datas </span><span style="color: #000000;">=</span><span style="color: #000000;"> dp.queryByDQLPage(dql, params, tempPage, LINES, user.getDomainid());<br />    } </span><span style="color: #0000ff;">else</span><span style="color: #000000;"> </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (view.getEditMode().endsWith(View.EDIT_MODE_CODE_SQL)) {<br />        datas </span><span style="color: #000000;">=</span><span style="color: #000000;"> dp.queryBySQLPage(sql, params, tempPage, LINES, user.getDomainid());<br />    }<br />}<br /></span></div></pre>
</div>
<p>&nbsp;</p>
<p><span style="font-size: 12pt;">重构后-版本1566：</span></p>
<p>&nbsp;</p>
<div style="width: 814px; height: 138px;" class="cnblogs_code">
<pre><div><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000ff;">public</span><span style="color: #000000;"> String expDocToExcel(String viewid, WebUser user, ParamsTable params) </span><span style="color: #0000ff;">throws</span><span style="color: #000000;"> Exception {<br />    datas </span><span style="color: #000000;">=</span><span style="color: #000000;"> view.getEditModeType().getDataPackage(params, tempPage, LINES, user, currdoc);<br />    </span><span style="color: #008000;">//</span><span style="color: #008000;">其他业务逻辑</span><span style="color: #008000;"><br /></span><span style="color: #000000;">}<br /></span></div></pre>
</div>
<p>&nbsp;</p>
<p>&nbsp;</p>
<h1>5.结语</h1>
<p>　　<span style="font-size: 12pt;">由上述案例可看到，引入subclass代替type code可以大大减少if-else判断，而且可以把责任内聚到每种type中，使代码结构更清晰易懂。</span></p>
<h1>&nbsp;</h1>
<h1><a name="_Toc267058364"><span lang="EN-US"><span style="font-size: 24pt; font-family: Times New Roman;">6.</span></span></a><span style="mso-bookmark: _Toc267058364;"><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman';"><span style="font-size: 24pt;">其他</span></span></span></h1>
<p><span style="font-size: 10.5pt; font-family: 'Times New Roman'; mso-bidi-font-size: 12.0pt; mso-fareast-font-family: 宋体; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA;" lang="EN-US"><span style="mso-tab-count: 1;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-size: 12pt;"> </span></span></span><span style="font-size: 12pt;"><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-size: 12.0pt; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-bidi-font-family: 'Times New Roman';">在本案例中引入了空类型概念，即</span><span style="color: black; font-family: 'Courier New'; mso-fareast-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA;" lang="EN-US">NullEditMode</span><span style="font-family: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-size: 12.0pt; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-bidi-font-family: 'Times New Roman';">，代码如下：</span></span></p>
<p><span style="font-size: 10.5pt; font-family: 宋体; mso-bidi-font-size: 12.0pt; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman';">　　</span></p>
<div style="width: 934px; height: 542px;" class="cnblogs_code">
<pre><div><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008000;">/**</span><span style="color: #008000;"><br /> * <br /> * </span><span style="color: #808080;">@author</span><span style="color: #008000;"> nicholas zhen<br /> * <br /> </span><span style="color: #008000;">*/</span><span style="color: #000000;"><br /></span><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">class</span><span style="color: #000000;"> NullEditMode </span><span style="color: #0000ff;">extends</span><span style="color: #000000;"> AbstractEditMode </span><span style="color: #0000ff;">implements</span><span style="color: #000000;"> EditMode {<br /><br />    </span><span style="color: #0000ff;">public</span><span style="color: #000000;"> NullEditMode(View view) {<br />        </span><span style="color: #0000ff;">super</span><span style="color: #000000;">(view);<br />    }<br /><br />    </span><span style="color: #0000ff;">public</span><span style="color: #000000;"> String getQueryString(ParamsTable params, WebUser user, Document sDoc) {<br />        </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> </span><span style="color: #000000;">""</span><span style="color: #000000;">;<br />    }<br /><br />    </span><span style="color: #0000ff;">public</span><span style="color: #000000;"> DataPackage getDataPackage(ParamsTable params, WebUser user, Document doc) </span><span style="color: #0000ff;">throws</span><span style="color: #000000;"> Exception {<br />        </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> DataPackage();<br />    }<br /><br />    </span><span style="color: #0000ff;">public</span><span style="color: #000000;"> DataPackage getDataPackage(ParamsTable params, </span><span style="color: #0000ff;">int</span><span style="color: #000000;"> page, </span><span style="color: #0000ff;">int</span><span style="color: #000000;"> lines, WebUser user, Document doc) </span><span style="color: #0000ff;">throws</span><span style="color: #000000;"> Exception {<br />        </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> </span><span style="color: #0000ff;">new</span><span style="color: #000000;"> DataPackage();<br />    }<br /><br />    </span><span style="color: #0000ff;">public</span><span style="color: #000000;"> </span><span style="color: #0000ff;">long</span><span style="color: #000000;"> count(ParamsTable params, WebUser user, Document doc) </span><span style="color: #0000ff;">throws</span><span style="color: #000000;"> Exception {<br />        </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> </span><span style="color: #000000;">0</span><span style="color: #000000;">;<br />    }<br />}<br /></span></div></pre>
</div>
<p><span style="font-size: 12pt;">说明：空类型保证了调用每个方法都有默认值返回，而不需要进行非空判断，当View没有类型时，即返回默认的空类型</span></p>
<p>&nbsp;</p>
<p>原创人员：Nicholas</p><br>文章来源:<a href='http://www.cnblogs.com/obpm/archive/2010/07/13/1776856.html'>http://www.cnblogs.com/obpm/archive/2010/07/13/1776856.html</a>  <img src ="http://www.blogjava.net/obpm/aggbug/330196.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/obpm/" target="_blank">obpm</a> 2010-07-13 23:22 <a href="http://www.blogjava.net/obpm/archive/2010/07/13/330196.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Hibernate的乐观锁与悲观锁(转载)</title><link>http://www.blogjava.net/obpm/archive/2010/07/11/330200.html</link><dc:creator>obpm</dc:creator><author>obpm</author><pubDate>Sun, 11 Jul 2010 01:58:00 GMT</pubDate><guid>http://www.blogjava.net/obpm/archive/2010/07/11/330200.html</guid><wfw:comment>http://www.blogjava.net/obpm/comments/330200.html</wfw:comment><comments>http://www.blogjava.net/obpm/archive/2010/07/11/330200.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/obpm/comments/commentRss/330200.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/obpm/services/trackbacks/330200.html</trackback:ping><description><![CDATA[<p>锁（ locking ） <br />业务逻辑的实现过程中，往往需要保证数据访问的排他性。如在金融系统的日终结算处理中，我们希望针对某个 cut-off 时间点的数据进行处理，而不希望在结算进行过程中（可能是几秒种，也可能是几个小时），数据再发生变化。此时，我们就需要通过一些机制来保证这些数据在某个操作过程中不会被外界修改，这样的机制，在这里，也就是所谓的 &ldquo;锁&rdquo; ，即给我们选定的目标数据上锁，使其无法被其他程序修改。Hibernate 支持两种锁机制：即通常所说的 &ldquo;悲观锁（ Pessimistic Locking ）&rdquo;和 &ldquo;乐观锁（ Optimistic Locking ）&rdquo; 。<br /><br />悲观锁（ Pessimistic Locking ） <br />悲观锁，正如其名，它指的是对数据被外界（包括本系统当前的其他事务，以及来自外部系统的事务处理）修改持保守态度，因此，在整个数据处理过程中，将数据处于锁定状态。悲观锁的实现，往往依靠数据库提供的锁机制（也只有数据库层提供的锁机制才能真正保证数据访问的排他性，否则，即使在本系统中实现了加锁机制，也无法保证外部系统不会修改数据）。 <br />一个典型的倚赖数据库的悲观锁调用： <br />select * from account where name=&rdquo;Erica&rdquo; for update<br />这条 sql 语句锁定了 account 表中所有符合检索条件（name=&rdquo;Erica&rdquo;）的记录。本次事务提交之前（事务提交时会释放事务过程中的锁），外界无法修改这些记录。Hibernate 的悲观锁，也是基于数据库的锁机制实现。<br />下面的代码实现了对查询记录的加锁： <br /><span style="color: #33cc00;"><span style="font-family: Arial, Helvetica, sans-serif;"><span style="background-color: #ffffff;"><span style="color: #3333ff;"><span style="font-size: 10pt; font-family: Verdana, Arial, Helvetica, sans-serif;">String hqlStr ="from TUser as user where user.name='Erica'";<br />Query query = session.createQuery(hqlStr);<br />query.setLockMode("user",LockMode.UPGRADE); // 加锁 <br />List userList = query.list();// 执行查询，获取数据</span> </span></span><br /></span></span>query.setLockMode 对查询语句中，特定别名所对应的记录进行加锁（我们为TUser 类指定了一个别名 &ldquo;user&rdquo; ），这里也就是对返回的所有 user 记录进行加锁。<br />观察运行期 Hibernate 生成的 SQL 语句：<br /><span style="font-size: 10pt; color: #3333ff; font-family: Verdana, Arial, Helvetica, sans-serif;">select tuser0_.id as id, tuser0_.name as name, tuser0_.group_id<br />as group_id, tuser0_.user_type as user_type, tuser0_.sex as sex<br />from t_user tuser0_ where (tuser0_.name='Erica' ) for update</span><br />这里 Hibernate 通过使用数据库的 for update 子句实现了悲观锁机制。 <br />Hibernate 的加锁模式有： <br /><strong>LockMode.NONE ：</strong> 无锁机制。 <br /><strong>LockMode.WRITE ：</strong> Hibernate 在 Insert 和 Update 记录的时候会自动获取。 <br /><strong>LockMode.READ ：</strong> Hibernate 在读取记录的时候会自动获取。<br />以上这三种锁机制一般由 Hibernate 内部使用，如 Hibernate 为了保证 Update过程中对象不会被外界修改，会在 save 方法实现中自动为目标对象加上 WRITE 锁。<br /><strong>LockMode.UPGRADE ：</strong>利用数据库的 for update 子句加锁。 <br /><strong>LockMode. UPGRADE_NOWAIT ：</strong> Oracle 的特定实现，利用 Oracle 的 for update nowait 子句实现加锁。 <br />上面这两种锁机制是我们在应用层较为常用的，加锁一般通过以下方法实现： <br /><strong>Criteria.setLockMode<br />Query.setLockMode<br />Session.lock</strong><br />注意，只有在查询开始之前（也就是 Hiberate 生成 SQL 之前）设定加锁，才会真正通过数据库的锁机制进行加锁处理，否则，数据已经通过不包含 for update 子句的 Select SQL 加载进来，所谓数据库加锁也就无从谈起。<br /><br />乐观锁（ Optimistic Locking ） <br />相对悲观锁而言，乐观锁机制采取了更加宽松的加锁机制。悲观锁大多数情况下依靠数据库的锁机制实现，以保证操作最大程度的独占性。但随之而来的就是数据库性能的大量开销，特别是对长事务而言，这样的开销往往无法承受。 <br />如一个金融系统，当某个操作员读取用户的数据，并在读出的用户数据的基础上进行修改时如更改用户帐户余额），如果采用悲观锁机制，也就意味着整个操作过程中（从操作员读出数、开始修改直至提交修改结果的全过程，甚至还包括操作员中途去煮咖啡的时间），数据库记录始终处于加锁状态，可以想见，如果面对几百上千个并发，这样的情况将导致怎样的后果。乐观锁机制在一定程度上解决了这个问题。乐观锁，大多是基于数据版本（ Version ）记录机制实现。何谓数据版本？即为数据增加一个版本标识，在基于数据库表的版本解决方案中，一般是通过为数据库表增加一个 &ldquo;version&rdquo; 字段来实现。 <br />读取出数据时，将此版本号一同读出，之后更新时，对此版本号加一。此时，将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对，如果提交的数据版本号大于数据库表当前版本号，则予以更新，否则认为是过期数据。 <br />对于上面修改用户帐户信息的例子而言，假设数据库中帐户信息表中有一个version 字段，当前值为 1 ；而当前帐户余额字段（balance）为 $100 。 <br />1 操作员 A 此时将其读出（version=1），并从其帐户余额中扣除 $50（$100-$50）。 <br />2 在操作员 A 操作的过程中，操作员 B 也读入此用户信息（version=1），并从其帐户余额中扣除 $20 （$100-$20）。 <br />3 操作员 A 完成了修改工作，将数据版本号加一（version=2），连同帐户扣除后余额（balance=$50），提交至数据库更新，此时由于提交数据版本大于数据库记录当前版本，数据被更新，数据库记录 version 更新为 2 。 <br />4 操作员 B 完成了操作，也将版本号加一（version=2）试图向数据库提交数据（balance=$80），但此时比对数据库记录版本时发现，操作员 B 提交的数据版本号为 2 ，数据库记录当前版本也为 2 ，不满足&ldquo; 提交版本必须大于记录当前版本才能执行更新&ldquo; 的乐观锁策略，因此，操作员 B 的提交被驳回。这样，就避免了操作员 B 用基于 version=1 的旧数据修改的结果覆盖操作员 A 的操作结果的可能。 <br />从上面的例子可以看出，乐观锁机制避免了长事务中的数据库加锁开销（操作员 A 和操作员 B 操作过程中，都没有对数据库数据加锁），大大提升了大并发量下的系统整体性能表现。需要注意的是，乐观锁机制往往基于系统中的数据存储逻辑，因此也具备一定的局限性，如在上例中，由于乐观锁机制是在我们的系统中实现，来自外部系统的用户余额更新操作不受我们系统的控制，因此可能会造成脏数据被更新到数据库中。在系统设计阶段，我们应该充分考虑到这些情况出现的可能性，并进行相应调整（如将乐观锁策略在数据库存储过程中实现，对外只开放基于此存储过程的数据更新途径，而不是将数据库表直接对外公开）。 <br />Hibernate 在其数据访问引擎中内置了乐观锁实现。如果不用考虑外部系统对数据库的更新操作，利用 Hibernate 提供的透明化乐观锁实现，将大大提升我们的生产力。 <br />Hibernate 中可以通过 class 描述符的 optimistic-lock 属性结合 version描述符指定。<br /><br />现在，我们为之前示例中的 TUser 加上乐观锁机制。<br />1 ． 首先为 TUser 的 class 描述符添加 optimistic-lock 属性： <br /><span style="font-family: Verdana, Arial, Helvetica, sans-serif;"><span style="font-size: 10pt;"><span style="color: #3333ff; font-family: Verdana, Arial, Helvetica, sans-serif;">&lt;hibernate-mapping&gt;<br />&lt;class name="org.hibernate.sample.TUser" table="t_user" dynamic-update="true"<br />dynamic-insert="true" optimistic-lock="version"&gt;<br />&hellip;&hellip;<br />&lt;/class&gt;<br />&lt;/hibernate-mapping&gt;</span><br /></span></span>optimistic-lock 属性有如下可选取值：&nbsp;<br /><strong>none：</strong>无乐观锁&nbsp;<br /><strong>version：</strong>通过版本机制实现乐观锁 <br /><strong>dirty：</strong>通过检查发生变动过的属性实现乐观锁 <br /><strong>all：</strong>通过检查所有属性实现乐观锁 <br />其中通过 version 实现的乐观锁机制是 Hibernate 官方推荐的乐观锁实现，同时也是 Hibernate 中，目前唯一在数据对象脱离 Session 发生修改的情况下依然有效的锁机制。因此，一般情况下，我们都选择 version 方式作为 Hibernate 乐观锁实现机制。 <br />2 ． 添加一个 Version 属性描述符 <br /><span style="color: #3333ff;"><span style="font-size: 10pt; font-family: Verdana, Arial, Helvetica, sans-serif;">&lt;hibernate-mapping&gt;<br />&lt;class name="org.hibernate.sample.TUser" table="t_user" dynamic-update="true" dynamic-insert="true"<br />optimistic-lock="version"&gt;<br />&lt;id name="id" column="id" type="java.lang.Integer"&gt;<br />&lt;generator class="native"&gt; <br />&lt;/generator&gt;<br />&lt;/id&gt;<br />&lt;version column="version" name="version" type="java.lang.Integer"/&gt;<br />&hellip;&hellip;<br />&lt;/class&gt;<br />&lt;/hibernate-mapping&gt;</span><br /></span>注意 version 节点必须出现在 ID 节点之后。这里我们声明了一个 version 属性，用于存放用户的版本信息，保存在 TUser 表的version 字段中。 <br />此时如果我们尝试编写一段代码，更新 TUser 表中记录数据，如： <br /><span style="font-size: 10pt; color: #3333ff; font-family: Verdana, Arial, Helvetica, sans-serif;">Criteria criteria = session.createCriteria(TUser.class);<br />criteria.add(Expression.eq("name","Erica"));<br />List userList = criteria.list();<br />TUser user =(TUser)userList.get(0);<br />Transaction tx = session.beginTransaction();<br />user.setUserType(1); // 更新 UserType 字段 <br />tx.commit();</span><br />每次对 TUser 进行更新的时候，我们可以发现，数据库中的 version 都在递增。而如果我们尝试在 tx.commit 之前，启动另外一个 Session ，对名为 Erica 的用户进行操作，以模拟并发更新时的情形： <br /><span style="font-size: 10pt; color: #3333ff; font-family: Verdana, Arial, Helvetica, sans-serif;">Session session= getSession();<br />Criteria criteria = session.createCriteria(TUser.class);<br />criteria.add(Expression.eq("name","Erica"));<br />Session session2 = getSession();<br />Criteria criteria2 = session2.createCriteria(TUser.class);<br />criteria2.add(Expression.eq("name","Erica"));<br />List userList = criteria.list();<br />List userList2 = criteria2.list();TUser user =(TUser)userList.get(0);<br />TUser user2 =(TUser)userList2.get(0);<br />Transaction tx = session.beginTransaction();<br />Transaction tx2 = session2.beginTransaction();<br />user2.setUserType(99);<br />tx2.commit();<br />user.setUserType(1);<br />tx.commit();<br /></span>执行以上代码，代码将在 tx.commit() 处抛出 StaleObjectStateException 异常，并指出版本检查失败，当前事务正在试图提交一个过期数据。通过捕捉这个异常，我们就可以在乐观锁校验失败时进行相应处理。</p>
<p>&nbsp;</p>
<p>转载人员：Nicholas</p><br>文章来源:<a href='http://www.cnblogs.com/obpm/archive/2010/07/11/1775120.html'>http://www.cnblogs.com/obpm/archive/2010/07/11/1775120.html</a>  <img src ="http://www.blogjava.net/obpm/aggbug/330200.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/obpm/" target="_blank">obpm</a> 2010-07-11 09:58 <a href="http://www.blogjava.net/obpm/archive/2010/07/11/330200.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>