James Gosling : Java之父 作為Java之父,James Gosling的名字可謂是耳熟能詳。當(dāng)人們評論一種編程語言時,總喜歡捎帶著把下蛋的母雞一起帶上。Java做為中國的編程語言學(xué)習(xí)者餐桌上有限的那么幾樣餐點(diǎn)中的流行款式,自然是讓James Gosling風(fēng)光不已。雖然James Gosling現(xiàn)在已經(jīng)不是領(lǐng)導(dǎo)Java發(fā)展潮流的領(lǐng)軍人物了,做為Sun的開發(fā)者產(chǎn)品組的CTO,怎么算來也是身居高位了,俗事纏身吧,但是這并不妨礙其對于Java一如既往的愛護(hù),表達(dá)著各式各樣鮮明的觀點(diǎn),引發(fā)一場又一場的爭論。 Bill Joy : 軟件業(yè)的愛迪生 Joy生于1954年,1982年與Vinod Khosla, Scott McNealy和Andy Bechtolsheim一起創(chuàng)建了Sun Microsystems,并從那時起擔(dān)任首席科學(xué)家,直到2003年離開。他是一位令人崇敬的軟件天才,他在軟件和硬件的歷史上留下了無數(shù)令人仰止的傳奇。 Joshua Bloch : Java 2 元勛 早在1996年,適逢Java剛剛嶄露頭角,年內(nèi)好事連連。先是1月份發(fā)布JDK 1.0,然后是5月底在舊金山召開首屆JavaOne大會,年末又是JDK 1.1緊跟其后。正是在Java技術(shù)如火如荼、大展拳腳的背景之下,Joshua Bloch來到了Sun,開始了他帶領(lǐng)Java社區(qū)步入“迦南美地”的漫長歷程。
Bruce Eckel原本是一位普通的匯編程序員。不知道是什么因緣際會,他轉(zhuǎn)行去寫計算機(jī)技術(shù)圖書,卻在此大紅大紫。他成功的秘訣不外乎兩點(diǎn):超人的表達(dá)能力和捕捉機(jī)會的能力。他最早的一本書是1990年代初期的《C++ Inside & Out》,隨后,在1995年他寫出了改變自己命運(yùn)的《Thinking in C++》。如果說這本書充分表現(xiàn)了他作為優(yōu)秀技術(shù)作家的一面,那么隨后他寫作《Thinking in Java》并因此步入頂級技術(shù)作家行列,則體現(xiàn)了他作為優(yōu)秀的機(jī)會主義分子善于捕捉機(jī)會的另一面。寫作中擅長舉淺顯直接的小例子來說明問題,語言生動,娓娓道來,特別適合于缺乏實(shí)踐經(jīng)驗(yàn)的初學(xué)者。因此《Thinking in Java》儼然成為天字第一號的Java教科書,對Java的普及與發(fā)展發(fā)揮著不可忽略的作用。不過公允地說,Bruce Eckel的書欠深刻。比如在“Thinking in…”系列中對設(shè)計模式的解說就有失大師水準(zhǔn)。這一方面是因?yàn)闀亩ㄎ环浅G逦硪环矫嬉彩且驗(yàn)锽ruce太過分心趕潮流,未能深入之故。TIJ之后,他預(yù)言Python將火,就匆匆跑去寫了半本《Thinking in Python》。后來Python并未如期而旺,于是他也就把書稿撂在那里不過問了,機(jī)會主義的一面暴露無遺。我們也可以善意的猜測一下,他的下一個投機(jī)對象會是什么呢?Ruby?.NET?MDA?總之,是什么我都不奇怪。
Oberg的作品很多,流行的代碼生成工具XDoclet和MVC框架WebWork都出自他的手筆。這兩個框架有一個共同的特點(diǎn),即它們的功能雖然簡單,但設(shè)計都非常優(yōu)雅靈活,能夠很方便地擴(kuò)展新功能甚至移植到新環(huán)境下使用。優(yōu)雅的設(shè)計源自O(shè)berg的過人才華,簡單的功能則折射出他玩世不恭的人生態(tài)度。正是這兩種特質(zhì)的融合,才造就了這個不世出的奇才。 Doug Lea : 世界上對Java影響力最大的個人 如果IT的歷史,是以人為主體串接起來的話,那么肯定少不了Doug Lea。這個鼻梁掛著眼鏡,留著德王威廉二世的胡子,臉上永遠(yuǎn)掛著謙遜靦腆笑容,服務(wù)于紐約州立大學(xué)Oswego分校計算器科學(xué)系的老大爺。 Scott McNealy :SUN十年來的掌舵者 McNealy,Sun的CEO、總裁兼董事長。他曾經(jīng)狂傲的說:“摧毀微軟是我們每個人的任務(wù)。”這位英勇的硅谷英雄,似乎帶頭起義,試圖組織一個反微軟陣線聯(lián)盟,以對抗微軟這股龐大的托拉斯惡勢力。他時常口出驚人之語,在公開場合大肆的批評微軟,并曾經(jīng)說微軟的.NET是.NOT。
Rod在悉尼大學(xué)不僅獲得了計算機(jī)學(xué)位,同時還獲得了音樂學(xué)位。更令人吃驚的是在回到軟件開發(fā)領(lǐng)域之前,他還獲得了音樂學(xué)的博士學(xué)位。有著相當(dāng)豐富的C/C++技術(shù)背景的Rod早在1996年就開始了對Java服務(wù)器端技術(shù)的研究。他是一個在保險、電子商務(wù)和金融行業(yè)有著豐富經(jīng)驗(yàn)的技術(shù)顧問,同時也是JSR-154(Servlet 2.4)和JDO 2.0的規(guī)范專家、JCP的積極成員。
Alan Kay :Java的精神先鋒 Sun的官方Java教材中有一句話,說Java是“C++的語法與Smalltalk語義的結(jié)合”。而Smalltalk的創(chuàng)造者就是Alan Kay。
Beck全家似乎都彌漫著技術(shù)的味道。生長在硅谷, 有著一個對無線電癡迷的祖父,以及一個電器工程師父親。從小就引導(dǎo)Kent Beck成為了業(yè)余無線電愛好者。 |
功能:在線拍照
簡介:用flex與java結(jié)合實(shí)現(xiàn)在線拍照
需求:為了滿足希望通過攝像頭拍照的圖片,然后通過服務(wù)器來展示需要
效果:
后臺:
前臺:
實(shí)現(xiàn)代碼:
flex:
java:
源碼:/Files/obpm/onlinetakephoto.rar
原創(chuàng)人員:Denny
LDAP快速入門
LDAP(輕量級目錄訪問協(xié)議,Lightweight Directory Access Protocol)是實(shí)現(xiàn)提供被稱為目錄服務(wù)的信息服務(wù)。目錄服務(wù)是一種特殊的數(shù)據(jù)庫系統(tǒng),其專門針對讀取,瀏覽和搜索操作進(jìn)行了特定的優(yōu)化。目錄一般用來包含描述性的,基于屬性的信息并支持精細(xì)復(fù)雜的過濾能力。目錄一般不支持通用數(shù)據(jù)庫針對大量更新操作操作需要的復(fù)雜的事務(wù)管理或回卷策略。而目錄服務(wù)的更新則一般都非常簡單。這種目錄可以存儲包括個人信息、web鏈結(jié)、jpeg圖像等各種信息。為了訪問存儲在目錄中的信息,就需要使用運(yùn)行在TCP/IP 之上的訪問協(xié)議—LDAP。
LDAP目錄中的信息是是按照樹型結(jié)構(gòu)組織,具體信息存儲在條目(entry)的數(shù)據(jù)結(jié)構(gòu)中。條目相當(dāng)于關(guān)系數(shù)據(jù)庫中表的記錄;條目是具有區(qū)別名DN (Distinguished Name)的屬性(Attribute),DN是用來引用條目的,DN相當(dāng)于關(guān)系數(shù)據(jù)庫表中的關(guān)鍵字(Primary Key)。屬性由類型(Type)和一個或多個值(Values)組成,相當(dāng)于關(guān)系數(shù)據(jù)庫中的字段(Field)由字段名和數(shù)據(jù)類型組成,只是為了方便檢索的需要,LDAP中的Type可以有多個Value,而不是關(guān)系數(shù)據(jù)庫中為降低數(shù)據(jù)的冗余性要求實(shí)現(xiàn)的各個域必須是不相關(guān)的。LDAP中條目的組織一般按照地理位置和組織關(guān)系進(jìn)行組織,非常的直觀。LDAP把數(shù)據(jù)存放在文件中,為提高效率可以使用基于索引的文件數(shù)據(jù)庫,而不是關(guān)系數(shù)據(jù)庫。類型的一個例子就是mail,其值將是一個電子郵件地址。
LDAP的信息是以樹型結(jié)構(gòu)存儲的,在樹根一般定義國家(c=CN)或域名(dc=com),在其下則往往定義一個或多個組織 (organization)(o=Acme)或組織單元(organizational units) (ou=People)。一個組織單元可能包含諸如所有雇員、大樓內(nèi)的所有打印機(jī)等信息。此外,LDAP支持對條目能夠和必須支持哪些屬性進(jìn)行控制,這是有一個特殊的稱為對象類別(objectClass)的屬性來實(shí)現(xiàn)的。該屬性的值決定了該條目必須遵循的一些規(guī)則,其規(guī)定了該條目能夠及至少應(yīng)該包含哪些屬性。例如:inetorgPerson對象類需要支持sn(surname)和cn(common name)屬性,但也可以包含可選的如郵件,電話號碼等屬性。
設(shè)計目錄結(jié)構(gòu)是LDAP最重要的方面之一。下面我們將通過一個簡單的例子來說明如何設(shè)計合理的目錄結(jié)構(gòu)。該例子將通過Netscape地址薄來訪文。假設(shè)有一個位于美國US(c=US)而且跨越多個州的名為Acme(o=Acme)的公司。Acme希望為所有的雇員實(shí)現(xiàn)一個小型的地址薄服務(wù)器。
我們從一個簡單的組織DN開始:
dn: o=Acme, c=US
Acme所有的組織分類和屬性將存儲在該DN之下,這個DN在該存儲在該服務(wù)器的目錄是唯一的。Acme希望將其雇員的信息分為兩類:管理者(ou= Managers)和普通雇員(ou=Employees),這種分類產(chǎn)生的相對區(qū)別名(RDN,relative distinguished names。表示相對于頂點(diǎn)DN)就shi :
dn: ou=Managers, o=Acme, c=US
dn: ou=Employees, o=Acme, c=US
在下面我們將會看到分層結(jié)構(gòu)的組成:頂點(diǎn)是US的Acme,下面是管理者組織單元和雇員組織單元。因此包括Managers和Employees的DN組成為:
dn: cn=Jason H. Smith, ou=Managers, o=Acme, c=US
dn: cn=Ray D. Jones, ou=Employees, o=Acme, c=US
dn: cn=Eric S. Woods, ou=Employees, o=Acme, c=US
為了引用Jason H. Smith的通用名(common name )條目,LDAP將采用cn=Jason H. Smith的RDN。然后將前面的父條目結(jié)合在一起就形成如下的樹型結(jié)構(gòu):
cn=Jason H. Smith
+ ou=Managers
+ o=Acme
+ c=US
-> dn: cn=Jason H. Smith,ou=Managers,o=Acme,c=US
現(xiàn)在已經(jīng)定義好了目錄結(jié)構(gòu),下一步就需要導(dǎo)入目錄信息數(shù)據(jù)。目錄信息數(shù)據(jù)將被存放在LDIF文件中,其是導(dǎo)入目錄信息數(shù)據(jù)的默認(rèn)存放文件。用戶可以方便的編寫Perl腳本來從例如/etc/passwd、NIS等系統(tǒng)文件中自動創(chuàng)建LDIF文件。
下面的實(shí)例保存目錄信息數(shù)據(jù)為testdate.ldif文件,該文件的格式說明將可以在man ldif中得到。
在添加任何組織單元以前,必須首先定義Acme DN:
dn: o=Acme, c=US
objectClass: organization
這里o屬性是必須的
o: Acme
下面是管理組單元的DN,在添加任何管理者信息以前,必須先定義該條目。
dn: ou=Managers, o=Acme, c=US
objectClass: organizationalUnit
這里ou屬性是必須的。
ou: Managers
第一個管理者DN:
dn: cn=Jason H. Smith, ou=Managers, o=Acme, c=US
objectClass: inetOrgPerson
cn和sn都是必須的屬性:
cn: Jason H. Smith
sn: Smith
但是還可以定義一些可選的屬性:
telephoneNumber: 111-222-9999
mail: headhauncho@acme.com
localityName:
可以定義另外一個組織單元:
dn: ou=Employees, o=Acme, c=US
objectClass: organizationalUnit
ou: Employees
并添加雇員信息如下:
dn: cn=Ray D. Jones, ou=Employees, o=Acme, c=US
objectClass: inetOrgPerson
cn: Ray D. Jones
sn: Jones
telephoneNumber: 444-555-6767
mail: jonesrd@acme.com
localityName:
dn: cn=Eric S. Woods, ou=Employees, o=Acme, c=US
objectClass: inetOrgPerson
cn: Eric S. Woods
sn: Woods
telephoneNumber: 444-555-6768
mail: woodses@acme.com
localityName:
本文實(shí)踐了在 Windows 下安裝配 openldap,并添加一個條目,LdapBrowser 瀏覽,及 Java 程序連接 openldap 的全過程。
1. 下載安裝 openldap for windows,當(dāng)前版本
相關(guān)鏈接:http://lucas.bergmans.us/hacks/openldap/
安裝很簡單,一路 next 即可,假設(shè)我們安裝在 c:\openldap
2. 配置 openldap,編輯 sldap.conf 文件
1) 打開 c:\openldap\sldap.conf,找到
include C:/openldap/etc/schema/core.schema,在它后面添加
include C:/openldap/etc/schema/cosine.schema
include C:/openldap/etc/schema/inetorgperson.schema
接下來的例子只需要用到以上三個 schema,當(dāng)然,如果你覺得需要的話,你可以把其他的 schema 全部添加進(jìn)來
include C:/openldap/etc/schema/corba.schema
include C:/openldap/etc/schema/dyngroup.schema
include C:/openldap/etc/schema/java.schema
include C:/openldap/etc/schema/misc.schema
include C:/openldap/etc/schema/nis.schema
include C:/openldap/etc/schema/openldap.schema
2) 還是在 sldap.conf 文件中,找到
suffix "dc=my-domain,dc=com"
rootdn "cn=Manager,dc=my-domain,dc=com"
把這兩行改為
suffix "o=teemlink,c=cn"
rootdn "cn=Manager,o=teemlink,dc=cn"
suffix 就是看自己如何定義了,后面步驟的 ldif 文件就必須與它定義了。還要注意到這個配置文件中有一個 rootpw secret,這個 secret 是 cn=Manager 的密碼,以后會用到,不過這里是明文密碼,你可以用命令: slappasswd -h {MD5} -s secret 算出加密的密碼 {MD5}Xr4ilOzQ4PCOq3aQ0qbuaQ== 取代配置中的 secret。
3. 啟動 openldap
CMD 進(jìn)入到 c:\openldap 下,運(yùn)行命令 sldapd -d 1
用可以看到控制臺下打印一片信息,openldap 默認(rèn)是用的 Berkeley DB 數(shù)據(jù)庫存儲目錄數(shù)據(jù)的。
4. 建立條目,編輯導(dǎo)入 ldif 文件
1) 新建一個 ldif(LDAP Data Interchanged Format) 文件(純文本格式),例如 test.ldif,文件內(nèi)容如下:
dn: o=teemlink
objectclass: top
objectclass: organization
o: develop
2) 執(zhí)行命令:ldapadd -l test.ldif
5. 使用LDAP Browser進(jìn)行訪問
5.1安裝LDAP Browser2.6軟件,進(jìn)行如下操作:
5.2顯示效果
package cn.myapps.test;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
public class LdapTest {
public void JNDILookup() {
String root = "o=teemlink,c=cn";
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://192.168.0.30/" + root);
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, "cn=Nicholas,ou=develop,o=teemlink,c=cn");
env.put(Context.SECURITY_CREDENTIALS, "123456");
DirContext ctx = null;
try {
ctx = new InitialDirContext(env);
Attributes attrs = ctx.getAttributes("cn=Nicholas,ou=develop");
System.out.println("Last Name: " + attrs.get("sn").get());
System.out.println("認(rèn)證成功");
} catch (javax.naming.AuthenticationException e) {
e.printStackTrace();
System.out.println("認(rèn)證失敗");
} catch (Exception e) {
System.out.println("認(rèn)證出錯:");
e.printStackTrace();
}
if (ctx != null) {
try {
ctx.close();
} catch (NamingException e) {
// ignore
}
}
}
public static void main(String[] args) {
LdapTest LDAPTest = new LdapTest();
LDAPTest.JNDILookup();
}
}
訪問地址:http://www.openldap.org/jldap/ 并下載相關(guān)lib
import com.novell.ldap.*;
import java.io.UnsupportedEncodingException;
public class List
{
public static void main(String[] args)
{
int ldapPort = LDAPConnection.DEFAULT_PORT;
int searchScope = LDAPConnection.SCOPE_ONE;
int ldapVersion = LDAPConnection.LDAP_V3;
boolean attributeOnly = false;
String attrs[] = null;
String ldapHost = "192.168.0.30";
String loginDN = "cn=Manager,o=teemlink,c=cn";
String password = "secret";
String searchBase = "ou=develop,o=teemlink,c=cn";
String searchFilter = "objectClass=*";
LDAPConnection lc = new LDAPConnection();
try {
// connect to the server
lc.connect(ldapHost, ldapPort);
// bind to the server
lc.bind(ldapVersion, loginDN, password.getBytes("UTF8"));
LDAPSearchResults searchResults =
lc.search(searchBase, // container to search
searchScope, // search scope
searchFilter, // search filter
attrs, // "1.1" returns entry name only
attributeOnly); // no attributes are returned
// print out all the objects
while (searchResults.hasMore()) {
LDAPEntry nextEntry = null;
try {
nextEntry = searchResults.next();
System.out.println("\n" + nextEntry.getDN());
System.out.println(nextEntry.getAttributeSet());
} catch (LDAPException e) {
System.out.println("Error: " + e.toString());
// Exception is thrown, go for next entry
continue;
}
}
// disconnect with the server
lc.disconnect();
} catch (LDAPException e) {
System.out.println("Error: " + e.toString());
} catch (UnsupportedEncodingException e) {
System.out.println("Error: " + e.toString());
}
System.exit(0);
}
}
訪問地址:http://www.openldap.org/jdbcldap/ 并下載相關(guān)lib
package jdbcldap;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class JdbcLdap {
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
Class.forName("com.octetstring.jdbcLdap.sql.JdbcLdapDriver");
String ldapConnectString = "jdbc:ldap://192.168.0.30/o=teemlink,c=cn?SEARCH_SCOPE:=subTreeScope";
Connection con = DriverManager.getConnection(ldapConnectString, "cn=Manager,o=teemlink,c=cn", "secret");
String sql = "SELECT * FROM ou=develop,o=teemlink,c=cn";
Statement sat = con.createStatement();
ResultSet rs = sta.executeQuery(sql);
while (rs.next()) {
System.out.println(rs.getString(1));
}
if (con != null)
con.close();
}
}
原創(chuàng)人員:Nicholas
以子類取代類型編碼
Replace Type Code with Subclasses
對軟件內(nèi)部結(jié)構(gòu)的一種調(diào)整,目的是在不改變「軟件之可察行為」前提下,提高其可理解性,降低其修改成本。
使用一系列重構(gòu)準(zhǔn)則(手法),在不改變「軟件之可察行為」前提下,調(diào)整其結(jié)構(gòu)。
同樣完成一件事,設(shè)計不良的程序往往需要更多代碼,這常常是因?yàn)榇a在不同的地方使用完全相同的語句做同樣的事。因此改進(jìn)設(shè)計的一個重要方向就是消除重復(fù)代碼(Duplicate Code)
你的源碼還有其它讀者:數(shù)個月之后可能會有另一位程序員嘗試讀懂你的代碼并做一些修改。我們很容易忘記這第二位讀者,但他才是最重要的。計算器是否多花了數(shù)個鐘頭進(jìn)行編譯,又有什么關(guān)系呢?如果一個程序員花費(fèi)一周時間來修改某段代碼,那才關(guān)系重大— 如果他理解你的代碼,這個修改原本只需一小時
Kent Beck 經(jīng)常形容自己的一句話:『我不是個偉大的程序員;我只是個有著一些優(yōu)秀習(xí)慣的好程序員而已。』重構(gòu)能夠幫助我更有效地寫出強(qiáng)固穩(wěn)健(robust)的代碼。
終于,前面的一切都?xì)w結(jié)到了這最后一點(diǎn):重構(gòu)幫助你更快速地開發(fā)程序。聽起來有違反直覺。當(dāng)我談到重構(gòu),人們很容易看出它能夠提高質(zhì)量。改善設(shè)計、提升可讀性、減少錯誤,這些都是提高質(zhì)量。但這難道不會降低開發(fā)速度嗎?我強(qiáng)烈相信:良好設(shè)計是快速軟件開發(fā)的根本。事實(shí)上擁有良好設(shè)計才可能達(dá)成快速的開發(fā)。如果沒有良好設(shè)計,或許某一段時間內(nèi)你的進(jìn)展迅速,但惡劣的設(shè)計很快就讓你的速度慢下來。你會把時間花在調(diào)試上面,無法添加新功能。修改時間愈來愈長,因?yàn)槟惚仨毣ㄓ鷣碛嗟臅r間去理解系統(tǒng)、尋找重復(fù)代碼。隨著你給最初程序打上一個又一個的補(bǔ)丁(patch),新特性需要更多代碼才能實(shí)現(xiàn)。真是個惡性循環(huán)。
重構(gòu)本來就不是一件「特別撥出時間做」的事情,重構(gòu)應(yīng)該隨時隨地進(jìn)行。你不應(yīng)該為重構(gòu)而重構(gòu),你之所以重構(gòu),是因?yàn)槟阆胱鰟e的什么事,而重構(gòu)可以幫助你把那些事做好
Don Roberts 給了我一條準(zhǔn)則:第一次做某件事時只管去做;第二次做類似的事會產(chǎn)生反感,但無論如何還是做了;第三次再做類似的事,你就應(yīng)該重構(gòu)。
☆ 事不過三,三則重構(gòu)。(Three strikes and you refactor.)
添加功能時一并重構(gòu)
修補(bǔ)錯誤時一并重構(gòu)
復(fù)審代碼時一并重構(gòu)
-以上章節(jié)摘抄自《重構(gòu)-改善既有代碼的設(shè)計》
1. 實(shí)例模塊為View,重構(gòu)元素為ViewAction、ViewProcessBean、View,以下為關(guān)系圖。
2. 由于View存在多種editMode(編輯模式),而每個調(diào)用的地方都需要進(jìn)行type code(類型碼)判斷,然后再進(jìn)行相應(yīng)的業(yè)務(wù)邏輯處理,最終在每個調(diào)用的地方都形成了大量的if-else代碼,大大減弱了代碼的可讀性,和邏輯清晰度。
3. 調(diào)用的地方:
如上圖所示,將每種type code重構(gòu)成subclass,加強(qiáng)了每種類型處理業(yè)務(wù)邏輯的能力。
View-版本1566代碼片段:
public EditMode getEditModeType() {
if (EDIT_MODE_CODE_DQL.equals(getEditMode())) {
return new DQLEditMode(this);
} else if (EDIT_MODE_CODE_SQL.equals(getEditMode())) {
return new SQLEditMode(this);
} else if (EDIT_MODE_DESIGN.equals(getEditMode())) {
return new DesignEditMode(this);
}
return new NullEditMode(this);
}
說明:調(diào)用者無需了解具體的類型,由View自身作判斷,返回EditMode接口,從而實(shí)現(xiàn)多態(tài)調(diào)用。
ViewProcessBean代碼片段:
重構(gòu)前-版本1503:
public String expDocToExcel(String viewid, WebUser user, ParamsTable params) throws Exception {
if (view.getEditMode().equals(View.EDIT_MODE_DESIGN)) {
datas = dp.queryBySQLPage(sql, params, tempPage, LINES, user.getDomainid());
} else if (view.getEditMode().equals(View.EDIT_MODE_CODE_DQL)) {
datas = dp.queryByDQLPage(dql, params, tempPage, LINES, user.getDomainid());
} else if (view.getEditMode().endsWith(View.EDIT_MODE_CODE_SQL)) {
datas = dp.queryBySQLPage(sql, params, tempPage, LINES, user.getDomainid());
}
}
重構(gòu)后-版本1566:
public String expDocToExcel(String viewid, WebUser user, ParamsTable params) throws Exception {
datas = view.getEditModeType().getDataPackage(params, tempPage, LINES, user, currdoc);
//其他業(yè)務(wù)邏輯
}
由上述案例可看到,引入subclass代替type code可以大大減少if-else判斷,而且可以把責(zé)任內(nèi)聚到每種type中,使代碼結(jié)構(gòu)更清晰易懂。
在本案例中引入了空類型概念,即NullEditMode,代碼如下:
/**
*
* @author nicholas zhen
*
*/
public class NullEditMode extends AbstractEditMode implements EditMode {
public NullEditMode(View view) {
super(view);
}
public String getQueryString(ParamsTable params, WebUser user, Document sDoc) {
return "";
}
public DataPackage getDataPackage(ParamsTable params, WebUser user, Document doc) throws Exception {
return new DataPackage();
}
public DataPackage getDataPackage(ParamsTable params, int page, int lines, WebUser user, Document doc) throws Exception {
return new DataPackage();
}
public long count(ParamsTable params, WebUser user, Document doc) throws Exception {
return 0;
}
}
說明:空類型保證了調(diào)用每個方法都有默認(rèn)值返回,而不需要進(jìn)行非空判斷,當(dāng)View沒有類型時,即返回默認(rèn)的空類型
原創(chuàng)人員:Nicholas
不會畫流程圖的,看了這個就懂了,絕對經(jīng)典!
轉(zhuǎn)載人員:Nicholas
在obpm系統(tǒng)后臺表單右上角有一個“一鍵生成視圖”功能。實(shí)現(xiàn)它的真正目的是為了后臺管理人員方便從實(shí)現(xiàn)好的表單中快速生成所有帶值的列的視圖。這樣管理人員就不需要手工新建視圖,然后再添加視圖中的帶值的列。
實(shí)現(xiàn)原理圖:
在實(shí)現(xiàn)原理圖中,我們發(fā)現(xiàn)沒有視圖中并沒有不帶值Field4相應(yīng)的Column4在視圖中,這是因?yàn)樵谝晥D中是要根據(jù)不同Column顯示不同的值的。如果Column是不帶值的話,那么視圖中就不應(yīng)該要這個Column,即使是要了,在視圖中沒有意義了。
實(shí)現(xiàn)原理代碼:
其中代碼路徑是:src-java-cn-myapps-core-dynaform-form-ejb-FormProcessBean.java
/**
* 根據(jù)表單編號來生成視圖
* @param formid 表單編號
* @throws Exception
*/
public Form oneKeyCreateView(String formid) throws Exception {
FormProcess formPross = (FormProcess) ProcessFactory.createProcess(FormProcess.class);
ViewProcess viewPross = (ViewProcess) ProcessFactory.createProcess(ViewProcess.class);
Form form = (Form) formPross.doView(formid);//獲得form
Collection formfield=form.getValueStoreFields();//獲得form存儲值的field
//新建視圖
View view = new View();
if (view.getId() == null || view.getId().trim().length() <= 0) {
view.setId(Sequence.getSequence());//設(shè)置視圖的ID
view.setSortId(Sequence.getTimeSequence());//設(shè)置視圖的排序ID }
view.setName(form.getName());//把表單的名字賦給視圖
view.setOpenType(view.OPEN_TYPE_NORMAL); //設(shè)置視圖打開類型-普通類型
view.setLastmodifytime(new Date());//最后修改日期
view.setApplicationid(form.getApplicationid());//把表單應(yīng)用程序Id賦給視圖的應(yīng)用程序Id
view.setModule(form.getModule());//把表單模塊Id賦給視圖的模塊ID
view.setPagelines("10");//設(shè)置視圖的分頁每頁顯示10條數(shù)據(jù)
view.setShowTotalRow(true); //是否顯示總共條數(shù)數(shù)據(jù)
view.setPagination(true); //是否分頁顯示
view.setRelatedForm(form.getId());//把表單ID賦給視圖的映射表單,從而映射了該表單
//將表單中對應(yīng)有值的列轉(zhuǎn)換為視圖的列
int i=0;
for(Iterator iterator=formfield.iterator();iterator.hasNext();){
FormField field=(FormField)iterator.next();
Column column = new Column();
if (column.getId() == null || column.getId().trim().length() <= 0) {
column.setId(Sequence.getSequence());
column.setOrderno(i);
}
if(field.getDiscript()!=null && !field.getDiscript().equals("")){//如果該表單中帶值Field有描述的話,就作為視圖Column,否則的用Field名稱
column.setName(field.getDiscript());
}else{
column.setName(field.getName());
}
column.setFormid(form.getId());//把表單中的ID賦給Column的表單ID
column.setApplicationid(form.getApplicationid());//把表單中應(yīng)用程序的ID賦給Column的表單應(yīng)用程序ID
column.setFieldName(field.getName()); //把表單中的名稱賦給Column的表單名稱
column.setParentView(view.getId());//將視圖ID賦給Column的父視圖
view.getColumns().add(column); //將視圖和Column關(guān)聯(lián)
i++;
}
//分別創(chuàng)建兩個按鈕 新建,刪除
Activity activityCreate = new Activity();
if (activityCreate.getId() == null || activityCreate.getId().trim().length() <= 0) {
activityCreate.setId(Sequence.getSequence());
activityCreate.setOrderno(0);
}
activityCreate.setApplicationid(form.getApplicationid());
activityCreate.setName("新建");
activityCreate.setParentView(view.getId());
activityCreate.setType(ActivityType.DOCUMENT_CREATE);
activityCreate.setOnActionForm(form.getId());
view.getActivitys().add(activityCreate); //將視圖和新建按鈕關(guān)聯(lián)
Activity activityDelete = new Activity();
if (activityDelete.getId() == null || activityDelete.getId().trim().length() <= 0) {
activityDelete.setId(Sequence.getSequence());
activityDelete.setOrderno(1);
}
activityDelete.setApplicationid(form.getApplicationid());
activityDelete.setName("刪除");
activityDelete.setParentView(view.getId());
activityDelete.setType(ActivityType.DOCUMENT_DELETE);
view.getActivitys().add(activityDelete); //將視圖和刪除按鈕關(guān)聯(lián)
viewPross.doCreate(view); //創(chuàng)建視圖
return form;
}
后臺效果圖:
表單:
視圖:
視圖列:
視圖按鈕:
前臺效果:
視圖:
表單:
原創(chuàng)人員:Denny
鎖( locking )
業(yè)務(wù)邏輯的實(shí)現(xiàn)過程中,往往需要保證數(shù)據(jù)訪問的排他性。如在金融系統(tǒng)的日終結(jié)算處理中,我們希望針對某個 cut-off 時間點(diǎn)的數(shù)據(jù)進(jìn)行處理,而不希望在結(jié)算進(jìn)行過程中(可能是幾秒種,也可能是幾個小時),數(shù)據(jù)再發(fā)生變化。此時,我們就需要通過一些機(jī)制來保證這些數(shù)據(jù)在某個操作過程中不會被外界修改,這樣的機(jī)制,在這里,也就是所謂的 “鎖” ,即給我們選定的目標(biāo)數(shù)據(jù)上鎖,使其無法被其他程序修改。Hibernate 支持兩種鎖機(jī)制:即通常所說的 “悲觀鎖( Pessimistic Locking )”和 “樂觀鎖( Optimistic Locking )” 。
悲觀鎖( Pessimistic Locking )
悲觀鎖,正如其名,它指的是對數(shù)據(jù)被外界(包括本系統(tǒng)當(dāng)前的其他事務(wù),以及來自外部系統(tǒng)的事務(wù)處理)修改持保守態(tài)度,因此,在整個數(shù)據(jù)處理過程中,將數(shù)據(jù)處于鎖定狀態(tài)。悲觀鎖的實(shí)現(xiàn),往往依靠數(shù)據(jù)庫提供的鎖機(jī)制(也只有數(shù)據(jù)庫層提供的鎖機(jī)制才能真正保證數(shù)據(jù)訪問的排他性,否則,即使在本系統(tǒng)中實(shí)現(xiàn)了加鎖機(jī)制,也無法保證外部系統(tǒng)不會修改數(shù)據(jù))。
一個典型的倚賴數(shù)據(jù)庫的悲觀鎖調(diào)用:
select * from account where name=”Erica” for update
這條 sql 語句鎖定了 account 表中所有符合檢索條件(name=”Erica”)的記錄。本次事務(wù)提交之前(事務(wù)提交時會釋放事務(wù)過程中的鎖),外界無法修改這些記錄。Hibernate 的悲觀鎖,也是基于數(shù)據(jù)庫的鎖機(jī)制實(shí)現(xiàn)。
下面的代碼實(shí)現(xiàn)了對查詢記錄的加鎖:
String hqlStr ="from TUser as user where user.name='Erica'";
Query query = session.createQuery(hqlStr);
query.setLockMode("user",LockMode.UPGRADE); // 加鎖
List userList = query.list();// 執(zhí)行查詢,獲取數(shù)據(jù)
query.setLockMode 對查詢語句中,特定別名所對應(yīng)的記錄進(jìn)行加鎖(我們?yōu)門User 類指定了一個別名 “user” ),這里也就是對返回的所有 user 記錄進(jìn)行加鎖。
觀察運(yùn)行期 Hibernate 生成的 SQL 語句:
select tuser0_.id as id, tuser0_.name as name, tuser0_.group_id
as group_id, tuser0_.user_type as user_type, tuser0_.sex as sex
from t_user tuser0_ where (tuser0_.name='Erica' ) for update
這里 Hibernate 通過使用數(shù)據(jù)庫的 for update 子句實(shí)現(xiàn)了悲觀鎖機(jī)制。
Hibernate 的加鎖模式有:
LockMode.NONE : 無鎖機(jī)制。
LockMode.WRITE : Hibernate 在 Insert 和 Update 記錄的時候會自動獲取。
LockMode.READ : Hibernate 在讀取記錄的時候會自動獲取。
以上這三種鎖機(jī)制一般由 Hibernate 內(nèi)部使用,如 Hibernate 為了保證 Update過程中對象不會被外界修改,會在 save 方法實(shí)現(xiàn)中自動為目標(biāo)對象加上 WRITE 鎖。
LockMode.UPGRADE :利用數(shù)據(jù)庫的 for update 子句加鎖。
LockMode. UPGRADE_NOWAIT : Oracle 的特定實(shí)現(xiàn),利用 Oracle 的 for update nowait 子句實(shí)現(xiàn)加鎖。
上面這兩種鎖機(jī)制是我們在應(yīng)用層較為常用的,加鎖一般通過以下方法實(shí)現(xiàn):
Criteria.setLockMode
Query.setLockMode
Session.lock
注意,只有在查詢開始之前(也就是 Hiberate 生成 SQL 之前)設(shè)定加鎖,才會真正通過數(shù)據(jù)庫的鎖機(jī)制進(jìn)行加鎖處理,否則,數(shù)據(jù)已經(jīng)通過不包含 for update 子句的 Select SQL 加載進(jìn)來,所謂數(shù)據(jù)庫加鎖也就無從談起。
樂觀鎖( Optimistic Locking )
相對悲觀鎖而言,樂觀鎖機(jī)制采取了更加寬松的加鎖機(jī)制。悲觀鎖大多數(shù)情況下依靠數(shù)據(jù)庫的鎖機(jī)制實(shí)現(xiàn),以保證操作最大程度的獨(dú)占性。但隨之而來的就是數(shù)據(jù)庫性能的大量開銷,特別是對長事務(wù)而言,這樣的開銷往往無法承受。
如一個金融系統(tǒng),當(dāng)某個操作員讀取用戶的數(shù)據(jù),并在讀出的用戶數(shù)據(jù)的基礎(chǔ)上進(jìn)行修改時如更改用戶帳戶余額),如果采用悲觀鎖機(jī)制,也就意味著整個操作過程中(從操作員讀出數(shù)、開始修改直至提交修改結(jié)果的全過程,甚至還包括操作員中途去煮咖啡的時間),數(shù)據(jù)庫記錄始終處于加鎖狀態(tài),可以想見,如果面對幾百上千個并發(fā),這樣的情況將導(dǎo)致怎樣的后果。樂觀鎖機(jī)制在一定程度上解決了這個問題。樂觀鎖,大多是基于數(shù)據(jù)版本( Version )記錄機(jī)制實(shí)現(xiàn)。何謂數(shù)據(jù)版本?即為數(shù)據(jù)增加一個版本標(biāo)識,在基于數(shù)據(jù)庫表的版本解決方案中,一般是通過為數(shù)據(jù)庫表增加一個 “version” 字段來實(shí)現(xiàn)。
讀取出數(shù)據(jù)時,將此版本號一同讀出,之后更新時,對此版本號加一。此時,將提交數(shù)據(jù)的版本數(shù)據(jù)與數(shù)據(jù)庫表對應(yīng)記錄的當(dāng)前版本信息進(jìn)行比對,如果提交的數(shù)據(jù)版本號大于數(shù)據(jù)庫表當(dāng)前版本號,則予以更新,否則認(rèn)為是過期數(shù)據(jù)。
對于上面修改用戶帳戶信息的例子而言,假設(shè)數(shù)據(jù)庫中帳戶信息表中有一個version 字段,當(dāng)前值為 1 ;而當(dāng)前帳戶余額字段(balance)為 $100 。
1 操作員 A 此時將其讀出(version=1),并從其帳戶余額中扣除 $50($100-$50)。
2 在操作員 A 操作的過程中,操作員 B 也讀入此用戶信息(version=1),并從其帳戶余額中扣除 $20 ($100-$20)。
3 操作員 A 完成了修改工作,將數(shù)據(jù)版本號加一(version=2),連同帳戶扣除后余額(balance=$50),提交至數(shù)據(jù)庫更新,此時由于提交數(shù)據(jù)版本大于數(shù)據(jù)庫記錄當(dāng)前版本,數(shù)據(jù)被更新,數(shù)據(jù)庫記錄 version 更新為 2 。
4 操作員 B 完成了操作,也將版本號加一(version=2)試圖向數(shù)據(jù)庫提交數(shù)據(jù)(balance=$80),但此時比對數(shù)據(jù)庫記錄版本時發(fā)現(xiàn),操作員 B 提交的數(shù)據(jù)版本號為 2 ,數(shù)據(jù)庫記錄當(dāng)前版本也為 2 ,不滿足“ 提交版本必須大于記錄當(dāng)前版本才能執(zhí)行更新“ 的樂觀鎖策略,因此,操作員 B 的提交被駁回。這樣,就避免了操作員 B 用基于 version=1 的舊數(shù)據(jù)修改的結(jié)果覆蓋操作員 A 的操作結(jié)果的可能。
從上面的例子可以看出,樂觀鎖機(jī)制避免了長事務(wù)中的數(shù)據(jù)庫加鎖開銷(操作員 A 和操作員 B 操作過程中,都沒有對數(shù)據(jù)庫數(shù)據(jù)加鎖),大大提升了大并發(fā)量下的系統(tǒng)整體性能表現(xiàn)。需要注意的是,樂觀鎖機(jī)制往往基于系統(tǒng)中的數(shù)據(jù)存儲邏輯,因此也具備一定的局限性,如在上例中,由于樂觀鎖機(jī)制是在我們的系統(tǒng)中實(shí)現(xiàn),來自外部系統(tǒng)的用戶余額更新操作不受我們系統(tǒng)的控制,因此可能會造成臟數(shù)據(jù)被更新到數(shù)據(jù)庫中。在系統(tǒng)設(shè)計階段,我們應(yīng)該充分考慮到這些情況出現(xiàn)的可能性,并進(jìn)行相應(yīng)調(diào)整(如將樂觀鎖策略在數(shù)據(jù)庫存儲過程中實(shí)現(xiàn),對外只開放基于此存儲過程的數(shù)據(jù)更新途徑,而不是將數(shù)據(jù)庫表直接對外公開)。
Hibernate 在其數(shù)據(jù)訪問引擎中內(nèi)置了樂觀鎖實(shí)現(xiàn)。如果不用考慮外部系統(tǒng)對數(shù)據(jù)庫的更新操作,利用 Hibernate 提供的透明化樂觀鎖實(shí)現(xiàn),將大大提升我們的生產(chǎn)力。
Hibernate 中可以通過 class 描述符的 optimistic-lock 屬性結(jié)合 version描述符指定。
現(xiàn)在,我們?yōu)橹笆纠械?TUser 加上樂觀鎖機(jī)制。
1 . 首先為 TUser 的 class 描述符添加 optimistic-lock 屬性:
<hibernate-mapping>
<class name="org.hibernate.sample.TUser" table="t_user" dynamic-update="true"
dynamic-insert="true" optimistic-lock="version">
……
</class>
</hibernate-mapping>
optimistic-lock 屬性有如下可選取值:
none:無樂觀鎖
version:通過版本機(jī)制實(shí)現(xiàn)樂觀鎖
dirty:通過檢查發(fā)生變動過的屬性實(shí)現(xiàn)樂觀鎖
all:通過檢查所有屬性實(shí)現(xiàn)樂觀鎖
其中通過 version 實(shí)現(xiàn)的樂觀鎖機(jī)制是 Hibernate 官方推薦的樂觀鎖實(shí)現(xiàn),同時也是 Hibernate 中,目前唯一在數(shù)據(jù)對象脫離 Session 發(fā)生修改的情況下依然有效的鎖機(jī)制。因此,一般情況下,我們都選擇 version 方式作為 Hibernate 樂觀鎖實(shí)現(xiàn)機(jī)制。
2 . 添加一個 Version 屬性描述符
<hibernate-mapping>
<class name="org.hibernate.sample.TUser" table="t_user" dynamic-update="true" dynamic-insert="true"
optimistic-lock="version">
<id name="id" column="id" type="java.lang.Integer">
<generator class="native">
</generator>
</id>
<version column="version" name="version" type="java.lang.Integer"/>
……
</class>
</hibernate-mapping>
注意 version 節(jié)點(diǎn)必須出現(xiàn)在 ID 節(jié)點(diǎn)之后。這里我們聲明了一個 version 屬性,用于存放用戶的版本信息,保存在 TUser 表的version 字段中。
此時如果我們嘗試編寫一段代碼,更新 TUser 表中記錄數(shù)據(jù),如:
Criteria criteria = session.createCriteria(TUser.class);
criteria.add(Expression.eq("name","Erica"));
List userList = criteria.list();
TUser user =(TUser)userList.get(0);
Transaction tx = session.beginTransaction();
user.setUserType(1); // 更新 UserType 字段
tx.commit();
每次對 TUser 進(jìn)行更新的時候,我們可以發(fā)現(xiàn),數(shù)據(jù)庫中的 version 都在遞增。而如果我們嘗試在 tx.commit 之前,啟動另外一個 Session ,對名為 Erica 的用戶進(jìn)行操作,以模擬并發(fā)更新時的情形:
Session session= getSession();
Criteria criteria = session.createCriteria(TUser.class);
criteria.add(Expression.eq("name","Erica"));
Session session2 = getSession();
Criteria criteria2 = session2.createCriteria(TUser.class);
criteria2.add(Expression.eq("name","Erica"));
List userList = criteria.list();
List userList2 = criteria2.list();TUser user =(TUser)userList.get(0);
TUser user2 =(TUser)userList2.get(0);
Transaction tx = session.beginTransaction();
Transaction tx2 = session2.beginTransaction();
user2.setUserType(99);
tx2.commit();
user.setUserType(1);
tx.commit();
執(zhí)行以上代碼,代碼將在 tx.commit() 處拋出 StaleObjectStateException 異常,并指出版本檢查失敗,當(dāng)前事務(wù)正在試圖提交一個過期數(shù)據(jù)。通過捕捉這個異常,我們就可以在樂觀鎖校驗(yàn)失敗時進(jìn)行相應(yīng)處理。
轉(zhuǎn)載人員:Nicholas