Lotus Notes其實也是有Schema的。取得方式如下: | Form form = db.getForm("fmFoo"); Vector<String> vec = form.getFields(); System.out.println(vec.toString()); |
之前討論過Form只是Document裡一個Item名,而getForm顯然是Notes Database物件特別針對Form屬性進行搜尋。其getFields是列出隸屬於該Form的Field。一度以為Item等同於Field,但顯然Form / Field與Document / Item之間還是有差異: Form / Field:其實就等同Table的Schema,類似於Java Class地位。 Document / Item:類似於Java Instance地位。第一位document可能多出一些item,是第二份document所沒有,但item name只要不等於field name,基本上就只是屬該instance所有。 結論是: Form is an Item、Fields所列出的名字都是Item Name。但Item Name不一定就是Field Name,除非該Item Name同時為getFields裡的一個名字。 業務邏輯上,Form代表一種Document的類型,而非實體。 Jemmy 發表在 痞客邦 留言(0) 人氣(64)
DateTime dateTime = session.createDateTime("2010-11-27:10:00:09"); // from java.sql.Date DateTime dateTime = session.createDateTime("2010-11-27:10:00:09.000000000"); // from java.sql.TimeStamp DateTime dateTime = session.createDateTime(new java.util.Date()); 在取得lotus.domino.DateTime物件後,就可以replaceItemValue("DateField", dateTime);。如此在NotesClient才會顯示Date/Time而非Text型態。 要search的話得借助Function了,如下: db.search("Form=\"fmFoo\" & @TextToTime(\"2010-11-27:10:00:09.000000000\")=DateField"); 基本上若參數只到日期,精準度會比對到日期,若列出時分秒,精準度則比對到時分秒。 Jemmy 發表在 痞客邦 留言(0) 人氣(89)
Lotus Notes的Java API給我感覺很不統一,以新增Document為例,官網所附的sample code是用appendItemValue(Item-name, Item-value)。可是在API上寫的Usage卻用replaceItemValue(Item-name, Item-value)。而且這兩個方法似乎都可以用,此外,要是Item-name是Null,這兩個method丟出的Exception也不同。
appendItemValue會丟出NotesException,訊息是:You must provide an item name 而replaceItemValue丟出的卻是NullPointerException 這兩者的差別是,appendItemValue會對Item產生multi-value,也就是假設Item1有值a,做一次insert或update item1,值也是a,則在Notes Client看到會是a,a。而若一開始沒有Item1,卻replaceItemValue,則Item1值會被產出。總結是不管insert、update,使用appendItemValue對已存在的item做append,會變成multi-value,而repalceItemValue則不會,但若Item1不存在,就會append。這和RDBMS觀念確實有差距。 此外這兩個method第二個參數,即Item value,其資料型態會對到Notes的資料型態,有以下四種: TEXT:對映Java String,若是其它類別,可能是toString的值。 NUMBER:對映Java Number類別,如Double、Integer DateTime:應是對到Java Date類別。 Vector:內含上述三個型態值的List。對到Java的Collection類別。Jemmy 發表在 痞客邦 留言(0) 人氣(110)
佩服Rex鍥而不捨的測試,Notes在Java的運作真的有夠OOXX,Session、Database和Form這祖孫三代物件,完全不像一脈相承的物件。就列舉這三代物件每一代若設錯會如何?
建立Session要Server URL和登入者帳密,若Server URL是不正確或找不到的,丟出的Exception如下,是最正常的反應了。 | NotesException: Could not get IOR from Domino Server: java.net.UnknownHostException: NOTESHOST at lotus.domino.NotesFactory.requestIORPlain(Unknown Source) at lotus.domino.NotesFactory.requestIORUsingArgs(Unknown Source) at lotus.domino.NotesFactory.getIOR(Unknown Source) at lotus.domino.NotesFactory.createSessionUP(Unknown Source) at lotus.domino.NotesFactory.createSession(Unknown Source) |
取得Database要Server URL和Database Name,要是Database不存在,Database db = session.getDatabase應該會return null,但有時不會,一定要對db物件做method存取,如:db.getLastModified(),才會丟出如下Exception,才能確保在程式裡確認DB真的不存在。 | NotesException: Database TESTDB.NSF has not been opened yet at lotus.domino.NotesExceptionHelper.read(Unknown Source) at lotus.domino.NotesExceptionHolder._read(Unknown Source) at lotus.priv.CORBA.iiop.RepImpl.invoke(Unknown Source) at lotus.priv.CORBA.portable.ObjectImpl._invoke(Unknown Source) at lotus.domino.corba._IDatabaseStub.getLastModified(Unknown Source) at lotus.domino.cso.Database.getLastModified(Unknown Source) |
以前解釋過Form只是一筆record裡的欄位,同樣Form值群聚成一個Form。所以Form沒有存不存在的問題,沒有這個Form值就會新增這個Form值,不會丟出Exception。Jemmy 發表在 痞客邦 留言(0) 人氣(148)
本來沒有想過權限議題,因為無法Update Notes Server幾乎是因為無法連線,但在有連線狀態下無法Update資料,大概只有權限了。Notes本身架構算完整了,對映到的Java API的物件體系也很完整,但不敢恭維的是在Exception表示方式,千篇一律像以下的Exception Stack: | NotesException: Notes error: ?????????? at lotus.domino.NotesExceptionHelper.read(Unknown Source) at lotus.domino.NotesExceptionHolder._read(Unknown Source) at lotus.priv.CORBA.iiop.RepImpl.invoke(Unknown Source) at lotus.priv.CORBA.portable.ObjectImpl._invoke(Unknown Source) at lotus.domino.corba._IDocumentStub.save(Unknown Source) at lotus.domino.cso.Document.save(Unknown Source) at lotus.domino.cso.Document.save(Unknown Source) |
上面的Exception是對Notes DB做create document因權限不足引發Exception,而做Update、Delete一定要先search,但也因權限不足導致找不到相符資料可以Update、Delete,也不會有Exception。 Notes的權限架構在Database物件的ACL,Sample如下: | Session s = NotesFactory.createSession("1.2.3.4:63148", "admin", "password"); Database db = s.getDatabase("pc1234", "TESTDB.NSF"); System.out.println("CurrentAccessLevel:" + db.getCurrentAccessLevel()); ACL acl = db.getACL(); ACLEntry entry = acl.getFirstEntry(); while (entry != null) { System.out.println(entry.getName() + ":" + entry.getLevel()); entry = acl.getNextEntry(); } |
在Database物件設有七個存取等級: 0 - ACLLEVEL_NOACCESS (設為0,導致連search都找不到資料) 1 - ACLLEVEL_DEPOSITOR 2 - ACLLEVEL_READER 3 - ACLLEVEL_AUTHOR 4 - ACLLEVEL_EDITOR 5 - ACLLEVEL_DESIGNER 6 - ACLLEVEL_MANAGER 之後可以用Iterator方式取得每個ACL Entry,得到每個資源(像LDAP的描述式如:CN=admin/O=mycompany)對該DB的存取權限。更高階的議題下次遇到再說。 Jemmy 發表在 痞客邦 留言(0) 人氣(102)
前有一篇說過,用Java對Notes的Document做完新增修改後再做刪除會丟出Exception,類似如下內容:
| NotesException: Notes error: ?????????? at lotus.domino.NotesExceptionHelper.read(Unknown Source) at lotus.domino.NotesExceptionHolder._read(Unknown Source) at lotus.priv.CORBA.iiop.RepImpl.invoke(Unknown Source) at lotus.priv.CORBA.portable.ObjectImpl._invoke(Unknown Source) at lotus.domino.corba._IDocumentStub.remove(Unknown Source) at lotus.domino.cso.Document.remove(Unknown Source) |
當時提到解法是再切換Operation時,將Session物件予以recycle便不會丟出,其實還有個但書:就是Session的建立要用host,用IP還是會出Exception。 錯:
| Session s = NotesFactory.createSession("1.2.3.4", "admin", "password"); Database db = s.getDatabase("1.2.3.4", "TESTDB.NSF"); |
對:
| Session s = NotesFactory.createSession("jemmy-pc", "admin", "password"); Database db = s.getDatabase("jemmy-pc", "TESTDB.NSF"); |
Jemmy 發表在 痞客邦 留言(0) 人氣(94)
到了壓力測試的撰寫,如何測試對Notes做完大量資料增改的執行時間?如何得知Notes已完成所有執行?而且有個難題是Notes的Domino Server時間未必和本機同步。取得Domino Server時間如下(紅字部份):
| import lotus.domino.Database; import lotus.domino.NotesFactory; import lotus.domino.Session; public class QueryNotesInfo { public static void main(String[] args) throws Exception { Session s = NotesFactory.createSession("1.2.3.4:63148", "admin", "password"); DateTime dt = s.createDateTime("Today"); dt.setNow(); System.out.println(dt.toJavaDate()); } } |
Notes的API還提供toJavaDate,可以轉成java.util.Date型態。此外使用Session物件的evaluate(
'@NOW')也可以取得現在時間,回傳一個Vector內藏日期時間字串,格式根據Locale顯示,目前無法得知如何對@NOW做format,所以還是用上述方式為佳。 接得如何得知Notes已完成所有執行?其實不管最後是增改刪哪個動作,Database物件最後一定改變最後修改時間。如:db.getLastModified(),回傳也是DateTime型態,自然也可以用toJavaDate。
Jemmy 發表在 痞客邦 留言(0) 人氣(75)
今天測出一個奇怪的事情:對Notes新增一筆A,再修改A沒問題,然後再刪除A就丟出NotesException,而Error Message竟是一堆問號。反觀如果Notes本來就有B這筆資料,先新增A再刪除B或先刪B再增A就沒有問題。所以矛頭就指向了Session,使用Lotus Script操作Notes,其物件階層是Session==>Database==>Document,每個物件都有recycle的method,在API定義是寫道:無條件歸還記憶體給系統。在對Notes做createDocument後,執行Document、Database的recycle再remove都還是丟出NotesException,直到執行Session的recycle後再執行remove就正常work,而且Session被recycle後毋須再createSession,可以同一份instance繼續使用。 相對於上次提到逐筆取得Document再進行remove,今天發現有批次刪除的方式,看來會更有效能:
| DocumentCollection dc = this.notesDB.search(cond); if (dc.getCount() > 0) { dc.removeAll(true); } |
根據Database物件依cond條件search到DocumentCollection後,可以直接對DocumentCollection進行remove(true)就可以完全清除。而Boolean參數為true,應是直接從.NSF檔刪除這些Document,false則只是標記為刪除,但並沒有真的刪除。
Jemmy 發表在 痞客邦 留言(0) 人氣(23)
在Microsoft的spaces寫部落格最大的好處,除了即時分享給msn的朋友之外,對岸的同好也能搜尋到。而最大的壞處,不能像Google的Blog可以多重分類標籤,而且分類標籤居然有上限,和字母同字數。原本Lotus Notes被Java call的,只好放在Java類裡>"<。 Survey了一段時間,對Lotus Notes的增改刪查有些掌握。Notes的資料庫是階層式資料庫,不同於關聯式資料庫(RDBMS),其Database是一個後綴名的.NSF檔。Notes和RDBMS的比較對照如下:
| RDBMS | Notes物件 | Description |
| Database | Database (NSF檔) | |
| Table | Form | Notes根本沒Table觀念,Form其實是一個特定的Item名,用於Notes UI的欄位。 |
| Record (Row) | Document | 通常會有名為Form的Item(不表示一定有),同一個Form值表示隸屬同一個Form(Table)。 |
| Column | Item | 除了Form,也許有其它特定的Item名(目前未知),其它由User自訂。 |
| Key | (嗯…) | 允許Document重複,沒有PK值。基本上Notes是有索引,目前還沒深入研究。 |
從上面比較來看,我指定一個Item名叫Author去搜尋,是可以跨不同的Form(Table)去找的,所以search通常還要再指定Item名為Form的值去搜尋,才能做到類似RDBMS的Select table。
起手式:連DB需要建Connection,連Notes則要建Session,不同的是Notes的Session沒有close等method,看來也沒有Connection Pool的問題: | Session s = NotesFactory.createSession("1.2.3.4", "admin", "password"); Database db = s.getDatabase("server_url", "TEST.NSF"); |
新增:是所有操作裡最快的,因為沒有PK值的緣故。 | Document doc = db.createDocument(); doc.appendItemValue("item_name", "item_data"); // 設置欄位值 boolean ok = doc.save(); |
查詢: 類似於Where條件式如右:Form = "myForm" & Item1 = "test",&等同and,值需要用雙引號括起。 | DocumentCollection dc = db.search("Form = \"myForm\" & Item1 = \"test\""); if (dc.getCount() > 0) { Document doc = dc.getFirstDocument(); while (null != doc) { // … 處理修改或刪除的程式,對doc(Document物件)操作 doc = dc.getNextDocument(); } } |
刪除:doc.remove(true)。得逐筆remove,沒有SQL的Where一般多筆刪除,但不必再加doc.save()動作。 修改:也是逐筆update。 | doc.replaceItemValue("item_name", "item_data"); // 置換欄位值 boolean ok = doc.save(); |
Jemmy 發表在 痞客邦 留言(0) 人氣(142)
Notes的物件體系中,Database下的Document和Agent地位是並排的,DocumentCollection只是個介於Database與Document的集合,算不上一個層次。我推測發Mail的動作何以那麼複雜,係因為發Mail的動作是一支Agent程式,而Mail內容則是Document物件,兼具了Agent、Document性質。因為執行一般的Agent,根本不用繼承AgentBase,直接這樣寫就行:
| public static void main(String[] args) throws Exception { Session session = NotesFactory.createSession("1.2.3.4", "admin", "password"); Database db = session.getDatabase(session.getServerName(), "TEST.NSF", true); Vector<Agent> v = db.getAgents(); for (Agent agent : v) { System.out.println(agent.getName()); // ex: 測試Mail | agMail if (agent.getName().endsWith("| agMail")) { agent.run(); System.out.println("Finish"); } } Agent agent = db.getAgent("agMail"); System.out.println("null ? " + (agent == null)); // return true } |
根據上述程式看,直接使用NotesFactory去建立Session就行。但還有一個弔詭之處,就是Database物件的getAgent方法無法取得想要的Agent,反而要用getAgents依次取得每個Agent,對它的name進行比對,在Lotus Notes物件建立方式是:物件名稱 | 物件別名。中間弄個中線區隔,而key是物件別名,可是即時用getAgent傳的是物件名稱或是全名(如上頭藍字),依然無法取得Agent的instance來Run,著實詭異。
Jemmy 發表在 痞客邦 留言(0) 人氣(73)