發(fā)布于:2021-02-01 10:10:20
0
285
0
如果您愿意在其中加入一些Groovy,構(gòu)建桌面應(yīng)用程序可能是一種愉快的體驗(yàn)。Griffon是一個(gè)應(yīng)用程序框架,它遵循Grails的精神,為桌面開(kāi)發(fā)帶來(lái)樂(lè)趣。
桌面應(yīng)用程序開(kāi)發(fā),這個(gè)術(shù)語(yǔ)現(xiàn)在不像web開(kāi)發(fā)那樣為人所熟知,并發(fā)和并行也不是。然而,這并不意味著它已經(jīng)死了。確實(shí),在某些行業(yè),桌面應(yīng)用程序是解決特定問(wèn)題的最佳選擇;在其他一些環(huán)境中,出于安全原因,桌面應(yīng)用程序是唯一的選擇。想想金融機(jī)構(gòu)、銀行、健康產(chǎn)業(yè)、生物研究、化學(xué)實(shí)驗(yàn)室、衛(wèi)星操作和軍隊(duì);僅舉幾個(gè)例子。所有這些都對(duì)桌面應(yīng)用程序優(yōu)于web應(yīng)用程序施加了一組特定的限制,例如安全性、對(duì)本地資源的訪問(wèn)、設(shè)備和端口通信。他們的另一個(gè)共同點(diǎn)是Griffon。是的,Griffon框架幫助所有這些行業(yè)和領(lǐng)域的團(tuán)隊(duì)完成了工作。
你可能聽(tīng)說(shuō)過(guò)Griffon ,但仍然想知道它是什么。簡(jiǎn)而言之,它是JVM的桌面應(yīng)用程序平臺(tái)。它深深扎根于Groovy社區(qū),因?yàn)檫@個(gè)項(xiàng)目是Groovy Swing團(tuán)隊(duì)的智囊團(tuán)成員:Danno Ferrin、James Williams和我自己。盡管如此,如果我在解釋Griffon的一些特征時(shí)有點(diǎn)激動(dòng)的話,你可以原諒我,因?yàn)槲曳浅O矚g這個(gè)項(xiàng)目??蚣茉O(shè)計(jì)背后的一個(gè)關(guān)鍵驅(qū)動(dòng)力是,Java開(kāi)發(fā)人員應(yīng)該很容易理解它。它還應(yīng)該支持快速編碼周期,同時(shí)保持源代碼整潔。最后,生產(chǎn)力的提高和樂(lè)趣因素必須立即察覺(jué)。
出于這些原因,團(tuán)隊(duì)決定遵循Grails框架及其社區(qū)的步驟。兩個(gè)框架之間有很多相似之處。例如,兩者都有一個(gè)命令行界面,可以幫助您完成創(chuàng)建、構(gòu)建、打包和部署應(yīng)用程序的常規(guī)任務(wù)。兩個(gè)框架都利用Groovy語(yǔ)言作為各自軟件棧的粘合劑。工具集成也相當(dāng)不錯(cuò),因?yàn)橹饕猧de和流行的文本編輯器為處理此類(lèi)項(xiàng)目提供了良好的支持。
但理論已經(jīng)夠多了,讓我們來(lái)練習(xí)一下吧!本文的其余部分將致力于構(gòu)建一個(gè)簡(jiǎn)單的地址簿應(yīng)用程序。我們肯定不會(huì)在剩下的幾頁(yè)中構(gòu)建一個(gè)完整的應(yīng)用程序,但我希望所有要討論的事情都能給你足夠的指導(dǎo),讓你繼續(xù)使用這個(gè)框架。
設(shè)置和配置
第一步是在您的計(jì)算機(jī)上下載和配置Griffon;有幾種選擇。如果您從下載頁(yè)面選擇通用安裝程序,它將解壓二進(jìn)制文件并為您配置路徑環(huán)境,特別是在Windows平臺(tái)上?;蛘撸绻贚inux機(jī)器上,您可以嘗試使用RPM或基于Debian的軟件包。ZIP或TGZ包可能是您最后的選擇。只需下載軟件包,將其解壓縮到您選擇的目錄中,最好是沒(méi)有空格的目錄。接下來(lái),配置一個(gè)環(huán)境變量GRIFFONu HOME,指向GRIFFON二進(jìn)制發(fā)行版解包的目錄。最后,確保PATH環(huán)境變量包含對(duì)GRIFFONu HOME/bin的引用。如果一切順利,在–version標(biāo)志打開(kāi)的情況下調(diào)用griffon命令應(yīng)該會(huì)顯示與下面類(lèi)似的輸出。
初始步驟
首先,我們?nèi)绾蝿?chuàng)建應(yīng)用程序?通常,您可以選擇基于Maven的方法并選擇適當(dāng)?shù)脑蛠?lái)引導(dǎo)項(xiàng)目?;蛘撸鷥H可以簡(jiǎn)單地創(chuàng)建一個(gè)新目錄,獲取一些Ant腳本并使用它來(lái)完成?;蛘咦屇尚刨?lài)的IDE做出決定。選擇,選擇,選擇。griffon命令行工具可以為您提供幫助。通過(guò)調(diào)用以下命令,每個(gè)Griffon應(yīng)用程序都以相同的方式啟動(dòng)。
$ griffon create-app addressbook
$ cd addressbook
您會(huì)注意到輸出中有一連串的行。如果需要,請(qǐng)繼續(xù)檢查新創(chuàng)建的應(yīng)用程序的內(nèi)容。create app命令通過(guò)創(chuàng)建幾個(gè)目錄和一些文件來(lái)初始化應(yīng)用程序。其中一個(gè)目錄是特別重要的,它的名字是格里芬應(yīng)用程序。在這個(gè)目錄中,您將找到另一組有助于保持源代碼井然有序的目錄。圖1顯示了剛才創(chuàng)建的griffon應(yīng)用程序目錄的擴(kuò)展內(nèi)容。
如您所知,Griffon利用MVC模式來(lái)安排組成應(yīng)用程序的元素。創(chuàng)建應(yīng)用程序后,您還將獲得一個(gè)初始MVC組,其名稱(chēng)與應(yīng)用程序的名稱(chēng)匹配。每個(gè)MVC成員中都有足夠的代碼來(lái)使應(yīng)用程序運(yùn)行。是的,信不信由你,該應(yīng)用程序已準(zhǔn)備好啟動(dòng)。返回控制臺(tái)并執(zhí)行以下命令。
$ griffon run-app
這應(yīng)該編譯應(yīng)用程序源,程序包資源,匯總依賴(lài)關(guān)系并啟動(dòng)應(yīng)用程序。在幾秒鐘內(nèi),您應(yīng)該會(huì)看到一個(gè)彈出窗口, 如圖2所示。
誠(chéng)然,它看起來(lái)并不多,但是我們還沒(méi)有編寫(xiě)任何代碼!清單1顯示了在打開(kāi)文件 griffon-app / views / addressbook / AddressbookView.groovy 時(shí)可以找到的內(nèi)容。
清單1–AddressbookView
package addressbookapplication(title: 'addressbook', preferredSize: [320, 240], pack: true, //location: [50,50], locationByPlatform:true, iconImage: imageIcon('/griffon-icon-48x48.png').image, iconImages: [imageIcon('/griffon-icon-48x48.png').image, imageIcon('/griffon-icon-32x32.png').image, imageIcon('/griffon-icon-16x16.png').image]) { // add content here label('Content Goes Here') // delete me}
我們?cè)谶@里看到的是一種基于Swing的領(lǐng)域特定語(yǔ)言(簡(jiǎn)稱(chēng)DSL),它基于一種流行的Groovy特性:builders。在我們的特殊情況下,我們處理的是SwingBuilder。構(gòu)建器只是一個(gè)節(jié)點(diǎn)和規(guī)則的集合,它們知道如何構(gòu)建層次結(jié)構(gòu)。Swing UI恰好由組件樹(shù)組成。在視圖中,我們可以觀察到一個(gè)名為“application”的頂級(jí)節(jié)點(diǎn)以及應(yīng)用于它的一些屬性。接下來(lái)我們將看到一個(gè)名為“l(fā)abel”的子節(jié)點(diǎn),其中包含一個(gè)文本條目。您可以識(shí)別代碼結(jié)構(gòu),如圖2所示。這就是Swing DSL的威力。代碼和UI非常相似,在閱讀DSL時(shí)很容易理解組件的結(jié)構(gòu)。
構(gòu)建UI
現(xiàn)在,我們已經(jīng)在視圖中看到了一些代碼,讓我們繼續(xù)這個(gè)MVC成員,我們稍后將介紹另外兩個(gè)。本著保持簡(jiǎn)單的精神,我們將更新UI,使其外觀如圖3所示。
清單2–AddressbookView更新
package addressbookapplication(title: 'Addressbook', pack: true, resizable: false, locationByPlatform:true, iconImage: imageIcon('/griffon-icon-48x48.png').image, iconImages: [imageIcon('/griffon-icon-48x48.png').image, imageIcon('/griffon-icon-32x32.png').image, imageIcon('/griffon-icon-16x16.png').image]) { menuBar { menu('Contacts') { controller.griffonClass.actionNames.each { name -> menuItem(getVariable(name)) } } } migLayout(layoutConstraints: 'fill') list(model: eventListModel(source: model.contacts), constraints: 'west, w 180! ', border: titledBorder(title: 'Contacts'), selectionMode: ListSelectionModel.SINGLE_SELECTION, keyReleased: { e -> // enter/return key if (e.keyCode != KeyEvent.VK_ENTER) return int index = e.source.selectedIndex if (index > -1) model.selectedIndex = index }, mouseClicked: { e -> // double click if (e.clickCount != 2) return int index = e.source.locationToIndex(e.point) if (index > -1) model.selectedIndex = index }) panel(constraints: 'center', border: titledBorder(title: 'Contact')) { migLayout(layoutConstraints: 'fill') for(propName in Contact.PROPERTIES) { label(text: GriffonNameUtils.getNaturalName(propName) + ': ', constraints: 'right') textField(columns: 30, constraints: 'grow, wrap', text: bind(propName, source: model.currentContact, mutual: true)) } } panel(constraints: 'east', border: titledBorder(title: 'Actions')) { migLayout() controller.griffonClass.actionNames.each { name -> button(getVariable(name), constraints: 'growx, wrap') } }}
接下來(lái)我們將更新griffon app/models/addressbook中的模型/AddressbookModel.groovy地址簿模型. 在這里,我們將確保模型在內(nèi)存中保留聯(lián)系人列表;它還將保存對(duì)當(dāng)前正在編輯的聯(lián)系人的引用。我們將使用glazedlist(Swing開(kāi)發(fā)人員中的一個(gè)流行選擇)來(lái)組織聯(lián)系人列表。清單3顯示了構(gòu)建列表和保留對(duì)當(dāng)前編輯聯(lián)系人的引用所需的所有代碼?,F(xiàn)在,聯(lián)系人列表有一個(gè)與元素相關(guān)的特殊綁定。每當(dāng)編輯一個(gè)元素時(shí),它都會(huì)發(fā)布一個(gè)更改事件,列表將截獲該事件;列表反過(guò)來(lái)會(huì)更新對(duì)列表更改感興趣的人?;仡櫱鍐?,您可以看到列表定義使用了listEventModel節(jié)點(diǎn)。這正是一個(gè)組件,它將在更新可用時(shí)通知UI,從而重新繪制受影響的區(qū)域。我們只需要連接一對(duì)組件就可以實(shí)現(xiàn)!AddressbookModel使用兩個(gè)特定于域的類(lèi):Contact和ContactPresentationModel。第一個(gè)可以看作是一個(gè)普通的域類(lèi),因?yàn)樗煤?jiǎn)單的屬性定義了聯(lián)系人應(yīng)該是什么。后者是Contact類(lèi)的可觀察包裝器。表示模型通常是能夠支持綁定操作的裝飾器。我們一會(huì)兒就來(lái)看看這兩門(mén)課。
清單3
package addressbookimport groovy.beans.Bindableimport ca.odell.glazedlists.*import griffon.transform.PropertyListenerimport java.beans.PropertyChangeEventimport java.beans.PropertyChangeListenerclass AddressbookModel { final EventListcontacts = new ObservableElementList( GlazedLists.threadSafeList( new BasicEventList()), GlazedLists.beanConnector(ContactPresentationModel) ) final ContactPresentationModel currentContact = new ContactPresentationModel() @PropertyListener(selectionUpdater) @Bindable int selectedIndex = -1 private selectionUpdater = { e ->currentContact.contact = contacts[selectedIndex].contact } AddressbookModel() { currentContact.addPropertyChangeListener(new ModelUpdater()) } private class ModelUpdater implements PropertyChangeListener { void propertyChange(PropertyChangeEvent e) { if(e.propertyName == ‘contact’ || selectedIndex < 0) return contacts[selectedIndex][e.propertyName] = e.newValue } } void removeContact(Contact contact) { currentContact.contact = null ContactPresentationModel toDelete = contacts.find { it.contact == contact } if(toDelete != null) contacts.remove(toDelete) }}
但是在我們展示域之前,讓我們先介紹一下最后一個(gè)MVC成員:控制器??刂破鞯墓ぷ魇菍?duì)用戶輸入做出反應(yīng)并協(xié)調(diào)信息流?,F(xiàn)在,我們只需要填空就可以使應(yīng)用程序再次工作,例如,將清單4中所示的代碼粘貼到griffon app/controllers/addresbook/A中ddressbookController.groovy地址。
清單4
package addressbookclass AddressbookController { def model void newAction(evt) { } void saveAction(evt) { } void deleteAction(evt) { }
你還記得模型在控制器和視圖之間傳遞數(shù)據(jù)嗎?這就是為什么控制器類(lèi)上有一個(gè)model屬性。Griffon提供了一種基本的依賴(lài)注入機(jī)制,只要在MVC成員各自的類(lèi)中定義了某些屬性,就可以保證每個(gè)MVC成員都可以與其他兩個(gè)成員進(jìn)行通信。
域
如果您是一名Grails開(kāi)發(fā)人員,您可能已經(jīng)注意到我們并沒(méi)有從域建模開(kāi)始,這是處理Grails應(yīng)用程序時(shí)的常見(jiàn)情況。做出這種選擇有兩個(gè)原因。首先,向您展示MVC成員的基本知識(shí)以及它們?nèi)绾蜗嗷プ饔?。第二,Griffon不支持開(kāi)箱即用的域類(lèi),至少不像Grails所理解的那樣,也就是說(shuō),Griffon還沒(méi)有g(shù)ormapi。但是我們可以通過(guò)編寫(xiě)簡(jiǎn)單的域類(lèi)來(lái)管理自己。清單5例如顯示了Contact域類(lèi)的外觀。
package addressbook@groovy.transform.EqualsAndHashCodeclass Contact { long id String name String lastname String address String company String email String toString() { "$name $lastname : $email" } static final ListPROPERTIES = ['name', 'lastname', 'address', 'company', 'email']
這個(gè)類(lèi)可以在src/main/addressbook文件中定義/聯(lián)系人:groovy. 接下來(lái)我們將在src/main/addressbook/Cont中定義伴隨的表示模型actPresentationModel.groovy公司清單6中的代碼。
package addressbookimport groovy.beans.Bindable@griffon.transform.PropertyListener(propertyUpdater)class ContactPresentationModel { // attributes @Bindable String name @Bindable String lastname @Bindable String address @Bindable String company @Bindable String email // model reference @Bindable Contact contact = new Contact() private propertyUpdater = { e ->if(e.propertyName == 'contact') { for(property in Contact.PROPERTIES) { def bean = e.newValue delegate[property] = bean != null ? bean[property] : null } } } String toString() { "$name $lastname" } void updateContact() { if(contact) { for(property in Contact.PROPERTIES) { contact[property] = this[property] } } }}
正如我們前面提到的,domain類(lèi)的設(shè)計(jì)很簡(jiǎn)單;它只需要關(guān)注我們想要保留的數(shù)據(jù)。另一方面,表示模型通過(guò)具有相同的屬性來(lái)鏡像域類(lèi),但稍作修改:每個(gè)屬性都是可觀察的。這意味著只要這些屬性中的任何一個(gè)的值發(fā)生更改,就會(huì)觸發(fā)一個(gè)事件。這些事件是啟用綁定的事件。Griffon使用@Bindable注釋來(lái)指示Groovy編譯器將一組指令注入字節(jié)碼,從而使這個(gè)類(lèi)成為可觀察的類(lèi)。@Bindable屬于Groovy語(yǔ)言中的一組特殊接口,為字節(jié)碼操作打開(kāi)了大門(mén)。這一組稱(chēng)為AST變換。在這段代碼中發(fā)現(xiàn)了另一個(gè)AST轉(zhuǎn)換,它是@PropertyListener。這種轉(zhuǎn)換是在特定類(lèi)和屬性上定義和附加PropertyChangeListener的一種奇特方法。在我們的例子中,我們附加了一個(gè)PropertyChangeListener,它對(duì)所有屬性更改都做出反應(yīng)。清單6中所有代碼的下一個(gè)效果是,當(dāng)Contact實(shí)例附加到ContactPresentationModels實(shí)例時(shí),所有屬性值都將從Contact復(fù)制到模型。當(dāng)調(diào)用updateContact()方法時(shí),反向操作將生效。
我們幾乎準(zhǔn)備好再次運(yùn)行該應(yīng)用程序。但是在我們這樣做之前,我們必須安裝一組插件,這將使我們的生活更輕松。我們說(shuō)過(guò)我們將使用GlazedLists。該庫(kù)由插件提供,因此我們將對(duì)其進(jìn)行安裝。在視圖中,我們使用了MigLayout,因此我們還將為其安裝一個(gè)插件。最后,我們將安裝另一個(gè)插件,使基于控制器方法創(chuàng)建UI動(dòng)作變得輕而易舉。轉(zhuǎn)到控制臺(tái)提示符,然后鍵入以下命令:
$ griffon install-plugin glazedlists
$ griffon install-plugin miglayout
$ griffon install-plugin actions
保持聯(lián)系
是時(shí)候完成申請(qǐng)了。我們前面有幾項(xiàng)任務(wù):
填寫(xiě)每個(gè)控制器操作所需的代碼
將聯(lián)系人列表保存到數(shù)據(jù)庫(kù)
確保在應(yīng)用程序啟動(dòng)時(shí)從數(shù)據(jù)庫(kù)加載聯(lián)系人
填充動(dòng)作是一項(xiàng)簡(jiǎn)單的操作,因?yàn)槲覀円呀?jīng)設(shè)置的綁定已經(jīng)處理了大部分?jǐn)?shù)據(jù)操作。清單7顯示了AddressbookController的最終代碼。
清單7–AddressbookController
package addressbookclass AddressbookController { def model def storageService void newAction(evt) { model.selectedIndex = -1 model.currentContact.contact = new Contact() } void saveAction(evt) { // push changes to domain object model.currentContact.updateContact() boolean isNew = model.currentContact.contact.id < 1 // save to db storageService.store(model.currentContact.contact) // if is a new contact, add it to the list if(isNew) { def cpm = new ContactPresentationModel() cpm.contact = model.currentContact.contact model.contacts << cpm } } void deleteAction(evt) { if(model.currentContact.contact && model.currentContact.contact.id) { // delete from db storageService.remove(model.currentContact.contact) // remove from contact list execInsideUIAsync { model.removeContact(model.currentContact.contact) model.selectedIndex = -1 } } } void dumpAction(evt) { storageService.dump() } void mvcGroupInit(Map args) { execFuture { List<ContactPresentationModel> list = storageService.load().collect([]) { new ContactPresentationModel(contact: it) } execInsideUIAsync { model.contacts.addAll(list) } } }}
第一個(gè)操作newAction涉及重置當(dāng)前選擇(如果有的話)并創(chuàng)建一個(gè)空聯(lián)系人。saveAction()應(yīng)該將表示模型的更改推回到域?qū)ο螅瑢?shù)據(jù)存儲(chǔ)在數(shù)據(jù)庫(kù)中,如果這是一個(gè)新聯(lián)系人,則將其添加到聯(lián)系人列表中。請(qǐng)注意,在處理數(shù)據(jù)庫(kù)問(wèn)題時(shí),我們將委托給另一個(gè)名為storageService的組件,在完成對(duì)控制器的描述后,我們將立即看到這個(gè)組件。第三個(gè)操作首先從數(shù)據(jù)庫(kù)中刪除聯(lián)系人,然后從聯(lián)系人列表中刪除。我們添加了第四個(gè)操作,用于將數(shù)據(jù)庫(kù)內(nèi)容轉(zhuǎn)儲(chǔ)到控制臺(tái)中。在代碼中找到的最后一條信息與從數(shù)據(jù)庫(kù)加載數(shù)據(jù)和填寫(xiě)聯(lián)系人列表有關(guān)。方法名很特別,因?yàn)樗荕VC生命周期的一個(gè)鉤子。這個(gè)特殊的方法將在所有MVC成員被實(shí)例化之后被調(diào)用,可以把它看作一個(gè)成員初始值設(shè)定項(xiàng)。我們已經(jīng)準(zhǔn)備好查看storageService組件。
Griffon中的服務(wù)只不過(guò)是普通的類(lèi),但它們從框架中得到了特殊的處理。例如,它們被視為單例,只要MVC成員定義了一個(gè)名稱(chēng)與服務(wù)名稱(chēng)匹配的屬性,它們就會(huì)被自動(dòng)注入到MVC成員中。利用這些知識(shí),我們將創(chuàng)建一個(gè)StorageService類(lèi),如下所示:
$ griffon create-service storage
這將在griffon app/services/addressbook中創(chuàng)建一個(gè)文件/存儲(chǔ)服務(wù).groovy使用默認(rèn)內(nèi)容。清單8顯示了應(yīng)用程序必須放入該文件中才能工作的代碼。
清單8
package addressbookclass StorageService { List<Contact> load() { withSql { dsName, sql -> List tmpList = [] sql.eachRow('SELECT * FROM contacts') { rs -> tmpList << new Contact( id: rs.id, name: rs.name, lastname: rs.lastname, address: rs.address, company: rs.company, email: rs.email ) } tmpList } } void store(Contact contact) { if(contact.id < 1) { // save withSql { dsName, sql -> String query = 'select max(id) max from contacts' contact.id = (sql.firstRow(query).max as long) + 1 List params = [contact.id] for(property in Contact.PROPERTIES) { params << contact[property] } String size = Contact.PROPERTIES.size() String columnNames = 'id, ' + Contact.PROPERTIES.join(', ') String placeHolders = (['?'] * size + 1)).join(',') sql.execute("""insert into contacts ($columnNames) values ($placeHolders""", params) } } else { // update withSql { dsName, sql -> List params = [] for(property in Contact.PROPERTIES) { params << contact[property] } params << contact.id String clauses = Contact.PROPERTIES.collect([]) { prop -> "$prop = ?" }.join(', ') sql.execute("""update contacts set $clauses where id = ?""", params) } } } void remove(Contact contact) { withSql { dsName, sql -> sql.execute('delete from contacts where id = ?', [contact.id]) } } void dump() { withSql { dsName, sql -> sql.eachRow('SELECT * FROM contacts') { rs -> println rs } } }}
每個(gè)服務(wù)方法都使用一個(gè)名為withSql的方法。如果我們安裝另一個(gè)插件,這個(gè)方法就可用了。讓我們現(xiàn)在就這么做:
$ griffon install-plugin gsql
現(xiàn)在,我們?cè)谶@個(gè)小應(yīng)用程序中啟用了 Groovy SQL支持。Groovy SQL是常規(guī)SQL之上的另一個(gè)DSL。使用它,您可以使用與對(duì)象和對(duì)象圖非常相似的編程API進(jìn)行SQL調(diào)用。實(shí)際上,您甚至可以應(yīng)用Groovy閉包,Groovy字符串和其他Groovy技巧,如 StorageService 類(lèi) 的實(shí)現(xiàn)中所示。在再次啟動(dòng)該應(yīng)用程序之前,我們必須注意另外兩項(xiàng)。我們必須告訴GSQL插件,必須將withSql 方法應(yīng)用于服務(wù)。其次,我們必須定義數(shù)據(jù)庫(kù)模式。如前所述,還沒(méi)有GORM API,必須手動(dòng)定義數(shù)據(jù)庫(kù)模式。
通過(guò)編輯文件 griffon-app / conf / Config.groovy 并添加以下行來(lái)完成第一個(gè)任務(wù)。
griffon.datasource.injectInto = [‘service’]
通過(guò)在 griffon-app / resources / schema.ddl中 創(chuàng)建一個(gè)具有以下內(nèi)容的文件來(lái)完成第二個(gè)任務(wù):
DROP TABLE IF EXISTS contacts;
CREATE TABLE contacts(
id INTEGER NOT NULL PRIMARY KEY,
name VARCHAR(30) NOT NULL,
lastname VARCHAR(30) NOT NULL,
address VARCHAR(100) NOT NULL,
company VARCHAR(50) NOT NULL,
email VARCHAR(100) NOT NULL
);
如何用一些初始數(shù)據(jù)為數(shù)據(jù)庫(kù)播種呢?在Grails中,這是通過(guò)編輯名為 BootStrap.groovy的文件來(lái)完成的;在Griffon中,這是通過(guò)編輯 griffon-app / conf / BootstrapGsql.groovy來(lái)完成的。讓我們向contacts表添加一個(gè)條目,如清單9所示。
<span style="font-family: Courier, 'Courier New', monospace;">import groovy.sql.Sqlclass BootstrapGsql { def init = { String dataSourceName = 'default', Sql sql -> def contacts = sql.dataSet('contacts') contacts.add( id: 1, name: 'Andres', lastname: 'Almiray', address: 'Kirschgartenstrasse 5 CH-4051 Switzerland', company: 'Canno Engineering AG', email: 'andres.almiray@canoo.com' ) } def destroy = { String dataSourceName = ‘default’, Sql sql -> }} </span>
現(xiàn)在我們真的完成了。再次啟動(dòng)該應(yīng)用程序。您應(yīng)該在聯(lián)系人列表中看到一個(gè)條目, 如圖4 所示。用鼠標(biāo)選擇它,然后按 Enter 或雙擊它。這將使聯(lián)系成為活動(dòng)聯(lián)系,并將其所有值置于中間形式。編輯其某些屬性,然后單擊“保存”按鈕。創(chuàng)建一個(gè)新聯(lián)系人并保存它?,F(xiàn)在單擊轉(zhuǎn)儲(chǔ)按鈕。您應(yīng)該在輸出中看到與在聯(lián)系人列表中可以找到的條目一樣多的行。
這個(gè)應(yīng)用程序的完整源代碼可以在GitHub上找到。我們可以向這個(gè)應(yīng)用程序添加更多的內(nèi)容。例如,沒(méi)有任何錯(cuò)誤處理。如果SQL不是你喜歡的呢?沒(méi)問(wèn)題,您可以選擇任何受支持的NoSQL選項(xiàng)?;蛘咔锴Р贿m合你。沒(méi)問(wèn)題,Griffon也支持SWT和JavaFX。更改UI工具包將意味著在保持其他組件幾乎完好無(wú)損的情況下主要更改視圖。你面前肯定有很多選擇。
作者介紹
熱門(mén)博客推薦