一個(gè)圖書管理系統(tǒng)中的兩個(gè)對(duì)象:Book(書籍),BookType(書籍類型)。Book和BookType之間是多對(duì)一關(guān)系。
說到主從表的關(guān)聯(lián)關(guān)系,自然而然地想起的一種實(shí)現(xiàn)方式就是選擇框,比如在Book的編輯界面是使用一個(gè)類型的下拉選擇框,選擇一個(gè)類型,然后保存。于是就有下例代碼:
<h:selectOneMenu id="type" value="#{bookHome.instance.bookType.typeId}">
<f:selectItems value="#{bookTypeList.typeSelectItems}" />
</h:selectOneMenu>
這段代碼的作者(我)原本的想法是讓這個(gè)下拉框與bookHome.instance里的bookType.typeId幫定。比如當(dāng)前的book類型id是1,修改后將book類型的id改為2,更新到數(shù)據(jù)庫。但是很不幸。這段代碼并不能執(zhí)行預(yù)期的行為,或者說它還附加了其他行為。即Seam已經(jīng)覺察到了在這個(gè)book中的類型的一個(gè)屬性(主鍵值)已經(jīng)改變了。于是,試圖更新這個(gè)類型。但是JPA的規(guī)范中是不允許更改主鍵的,這就引起了一個(gè)錯(cuò)誤。不信可以試下哦,呵呵。(以上描述我已經(jīng)盡可能說清楚我的想法,但可能還是不怎么清楚,希望大家看不懂的說說哪里看不懂,我好改正)。我想說的是,不要直接幫定到外鍵,而是現(xiàn)幫定到一個(gè)臨時(shí)變量,比如在bookHome中多寫一個(gè)變量:<f:selectItems value="#{bookTypeList.typeSelectItems}" />
</h:selectOneMenu>
public class BookHome extends EntityHome<Book>{
private Long typeId;
//..getter setter
}
然后將下拉框邦定到這個(gè)變量上,比如:private Long typeId;
//..getter setter
}
<h:selectOneMenu id="type" value="#{bookHome.typeId}">
<f:selectItems value="#{bookTypeList.typeSelectItems}" />
</h:selectOneMenu>
。然后在重載的persist或者update方法中寫上:<f:selectItems value="#{bookTypeList.typeSelectItems}" />
</h:selectOneMenu>
BookType newType = getEntityManager().find(BookType.class, typeId);
instance.setType(newType);
return super.persist();
就完成了。我叫它“轉(zhuǎn)移邦定”,呵呵instance.setType(newType);
return super.persist();
Seam的解決方案:
其實(shí)Seam有另一種解決方案。比如如果你是自動(dòng)生成代碼的方式,在BookEdit.xhtml中就會(huì)看到這樣的代碼:
<div class="association" id="bookTypeParent">
<h:outputText value="There is no bookType associated with this book."
rendered="#{bookHome.instance.bookType == null}"/>
<rich:dataTable var="bookType"
value="#{bookHome.instance.bookType}"
rendered="#{bookHome.instance.bookType != null}"
rowClasses="rvgRowOne,rvgRowTwo"
id="bookTypeTable">
<h:column>
<f:facet name="header">typeId</f:facet>
#{bookType.typeId}
</h:column>
<h:column>
<f:facet name="header">bookType typeId</f:facet>
#{bookType.bookType.typeId}
</h:column>
<h:column>
<f:facet name="header">typeName</f:facet>
#{bookType.typeName}
</h:column>
<h:column>
<f:facet name="header">action</f:facet>
<s:link view="/BookType.xhtml"
id="viewbookType"
value="View"
propagation="none">
<f:param name="bookTypeTypeId"
value="#{bookType.typeId}"/>
</s:link>
</h:column>
</rich:dataTable>
<div class="actionButtons">
<s:button value="Select bookType"
view="/BookTypeList.xhtml">
<f:param name="from" value="BookEdit"/>
</s:button>
</div>
</div>
點(diǎn)擊“Select bookType”頁面就自動(dòng)跳轉(zhuǎn)到BookTypeList頁面,列出所有的類型,每個(gè)類型后面都有一個(gè)select連接,點(diǎn)擊這個(gè)連接就可選中這個(gè)類型。然后回到BookEdit.xhtml。很Seam很強(qiáng)大吧,呵呵。其實(shí)為何這樣能完成一個(gè)選擇都是在BookEdit.page.xml里配置的。配置大概如下:<h:outputText value="There is no bookType associated with this book."
rendered="#{bookHome.instance.bookType == null}"/>
<rich:dataTable var="bookType"
value="#{bookHome.instance.bookType}"
rendered="#{bookHome.instance.bookType != null}"
rowClasses="rvgRowOne,rvgRowTwo"
id="bookTypeTable">
<h:column>
<f:facet name="header">typeId</f:facet>
#{bookType.typeId}
</h:column>
<h:column>
<f:facet name="header">bookType typeId</f:facet>
#{bookType.bookType.typeId}
</h:column>
<h:column>
<f:facet name="header">typeName</f:facet>
#{bookType.typeName}
</h:column>
<h:column>
<f:facet name="header">action</f:facet>
<s:link view="/BookType.xhtml"
id="viewbookType"
value="View"
propagation="none">
<f:param name="bookTypeTypeId"
value="#{bookType.typeId}"/>
</s:link>
</h:column>
</rich:dataTable>
<div class="actionButtons">
<s:button value="Select bookType"
view="/BookTypeList.xhtml">
<f:param name="from" value="BookEdit"/>
</s:button>
</div>
</div>
<begin-conversation join="true"/>
<action execute="#{bookHome.wire}"/>
<param name="bookFrom"/>
<param name="bookBookId" value="#{bookHome.bookBookId}"/>
<param name="bookTypeFrom"/>
<param name="bookTypeTypeId" value="#{bookTypeHome.bookTypeTypeId}"/>
<begin-conversation join="true"/> :開始一個(gè)conversation(我暫稱之為“頁面流”),如果已存在,就加入。而不重新創(chuàng)建。<action execute="#{bookHome.wire}"/>
<param name="bookFrom"/>
<param name="bookBookId" value="#{bookHome.bookBookId}"/>
<param name="bookTypeFrom"/>
<param name="bookTypeTypeId" value="#{bookTypeHome.bookTypeTypeId}"/>
<action execute="#{bookHome.wire}"/> :一最重要的這個(gè)。如果沒有執(zhí)行這個(gè)方法這段跳轉(zhuǎn)就沒有任何效果了。,先來看下這個(gè)方法是怎么寫的吧:
public void wire() {
getInstance();//獲取instance,放在這里是為了加在instance
BookType bookType = bookTypeHome.getDefinedInstance();//獲取類型。
if (bookType != null) {//如果選擇的類型不為null
getInstance().setBookType(bookType);//設(shè)置書籍類型
}
}
那bookTypeHome從哪來的呢?天上掉下的?呵呵,當(dāng)然不是。就在BookHome的上部分:getInstance();//獲取instance,放在這里是為了加在instance
BookType bookType = bookTypeHome.getDefinedInstance();//獲取類型。
if (bookType != null) {//如果選擇的類型不為null
getInstance().setBookType(bookType);//設(shè)置書籍類型
}
}
@In(create = true)
BookTypeHome bookTypeHome;
BookTypeHome bookTypeHome;
前面我說過了。這個(gè)@In就是拿來做雙向注入的。bookTypeHome是要注入的組件名稱。
其他的都是參數(shù)了,沒啥好解釋的。
但為什么在BookTypeList頁面點(diǎn)select,怎么就會(huì)自動(dòng)跳轉(zhuǎn)到BookEdit.xhtml呢?奧秘就在這段代碼里(BookTypeList.xhtml):
<s:link view="/#{empty from ? 'BookType' : from}.xhtml"
value="Select"
id="bookType">
<f:param name="bookTypeTypeId"
value="#{bookType.typeId}"/>
</s:link>
從BookEdit里傳來一個(gè)from。value="Select"
id="bookType">
<f:param name="bookTypeTypeId"
value="#{bookType.typeId}"/>
</s:link>
<s:button value="Select bookType"
view="/BookTypeList.xhtml">
<f:param name="from" value="BookEdit"/>
</s:button>
就告訴了BookTypeList,是從BookEdit里來的,點(diǎn)Select的時(shí)候就不要去其他地方了。直接回去。view="/BookTypeList.xhtml">
<f:param name="from" value="BookEdit"/>
</s:button>
好了,今天到這,困了,上面講的不明白的歡迎email或qq聯(lián)系我。。