一個圖書管理系統中的兩個對象:Book(書籍),BookType(書籍類型)。Book和BookType之間是多對一關系。
說到主從表的關聯關系,自然而然地想起的一種實現方式就是選擇框,比如在Book的編輯界面是使用一個類型的下拉選擇框,選擇一個類型,然后保存。于是就有下例代碼:
<h:selectOneMenu id="type" value="#{bookHome.instance.bookType.typeId}">
<f:selectItems value="#{bookTypeList.typeSelectItems}" />
</h:selectOneMenu>
這段代碼的作者(我)原本的想法是讓這個下拉框與bookHome.instance里的bookType.typeId幫定。比如當前的book類型id是1,修改后將book類型的id改為2,更新到數據庫。但是很不幸。這段代碼并不能執行預期的行為,或者說它還附加了其他行為。即Seam已經覺察到了在這個book中的類型的一個屬性(主鍵值)已經改變了。于是,試圖更新這個類型。但是JPA的規范中是不允許更改主鍵的,這就引起了一個錯誤。不信可以試下哦,呵呵。(以上描述我已經盡可能說清楚我的想法,但可能還是不怎么清楚,希望大家看不懂的說說哪里看不懂,我好改正)。我想說的是,不要直接幫定到外鍵,而是現幫定到一個臨時變量,比如在bookHome中多寫一個變量:<f:selectItems value="#{bookTypeList.typeSelectItems}" />
</h:selectOneMenu>
public class BookHome extends EntityHome<Book>{
private Long typeId;
//..getter setter
}
然后將下拉框邦定到這個變量上,比如: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();
就完成了。我叫它“轉移邦定”,呵呵instance.setType(newType);
return super.persist();
Seam的解決方案:
其實Seam有另一種解決方案。比如如果你是自動生成代碼的方式,在BookEdit.xhtml中就會看到這樣的代碼:
<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>
點擊“Select bookType”頁面就自動跳轉到BookTypeList頁面,列出所有的類型,每個類型后面都有一個select連接,點擊這個連接就可選中這個類型。然后回到BookEdit.xhtml。很Seam很強大吧,呵呵。其實為何這樣能完成一個選擇都是在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"/> :開始一個conversation(我暫稱之為“頁面流”),如果已存在,就加入。而不重新創建。<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}"/> :一最重要的這個。如果沒有執行這個方法這段跳轉就沒有任何效果了。,先來看下這個方法是怎么寫的吧:
public void wire() {
getInstance();//獲取instance,放在這里是為了加在instance
BookType bookType = bookTypeHome.getDefinedInstance();//獲取類型。
if (bookType != null) {//如果選擇的類型不為null
getInstance().setBookType(bookType);//設置書籍類型
}
}
那bookTypeHome從哪來的呢?天上掉下的?呵呵,當然不是。就在BookHome的上部分:getInstance();//獲取instance,放在這里是為了加在instance
BookType bookType = bookTypeHome.getDefinedInstance();//獲取類型。
if (bookType != null) {//如果選擇的類型不為null
getInstance().setBookType(bookType);//設置書籍類型
}
}
@In(create = true)
BookTypeHome bookTypeHome;
BookTypeHome bookTypeHome;
前面我說過了。這個@In就是拿來做雙向注入的。bookTypeHome是要注入的組件名稱。
其他的都是參數了,沒啥好解釋的。
但為什么在BookTypeList頁面點select,怎么就會自動跳轉到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里傳來一個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里來的,點Select的時候就不要去其他地方了。直接回去。view="/BookTypeList.xhtml">
<f:param name="from" value="BookEdit"/>
</s:button>
好了,今天到這,困了,上面講的不明白的歡迎email或qq聯系我。。