这里以'景点+门票类型'的用例为示例,其对应关系为1-N,在数据库中门票类型表包含了对应门票的外键。使用Hibernate的的级联操作从数据库中取出的景点对象scenery,scenery对象包含门票类型列表(List类型)tbSceneryTickets。景点的hbm文件的有如下的one-to-many设置:
<!-- 这里需要Parent一端设置inverse="true" cascade="all"--> <list name="tbSceneryTickets" lazy="false" inverse="true" cascade="all"> <key><column name="SCENERY_ID" /></key> <index column="ID" type="java.lang.String" /> <one-to-many class="TbSceneryTickets" /> </list>
需要注意的是这里通过index属性设置了List的索引为门票类型的ID(所以这里需要设置其主键ID为数字)。
在JSP中取得tbSceneryTickets中每个对象的值时候,使用struts2的iterator标签,通过OGNL表达可以设置其value属性的值为“scenery.tbSceneryTickets”,通过设置其id属性来标识每个对象,代码如下:
<s:iterator value="scenery.tbSceneryTickets" id="h"> <s:if test="#h != null"> <tr id="oldTicket<s:property value='#h.id'/>"> <td> 门票类型:<input type="text" name="scenery.tbSceneryTickets[<s:property value='#h.id'/>].typeName" class="input" value='<s:property value="typeName"/>' /> </td> <td> 门票价格:<input type="text" name="scenery.tbSceneryTickets[<s:property value='#h.id'/>].price" class="input" value='<s:property value="price"/>' /> </td> </tr> </s:if> </s:iterator>
在取得tbSceneryTickets中每个门票类型对象的值的时候,可以将其索引和属性作为页面标签的name属性值,如门票类型值:
<input type="text" name="scenery.tbSceneryTickets[<s:property value='#h.id'/>].typeName"
这里注意的是索引的取法,这里的#h.id对应的是List中该门票类型对象的索引值,同时也是该门票类型对象在数据库中的主键值。所以修改提交时,Struts2通过ONGL表达式将每个门票类型对象的属性更新到服务器端的tbSceneryTickets列表中,由于存在hibernate的映射和级联设置,也可以顺利的更新到数据库中。
如果这时有新的门票类型在修改景点对象的时候插入,可用的策略是在浏览器端用JavaScript来控制输入框的添加,并且维持一个唯一的索引,代码如下(代码中的索引i为大于0的动态数值):
门票类型:<input type="text" name="newSceneryTickets[i].typeName" />
在服务器端维持一个名为newSceneryTickets的门票类型的List,前段提交后,Struts2通过ONGL表达式将每个新增的门票类型对象存入到服务器端的newSceneryTickets列表中,然后在将newSceneryTickets的中的每个门票类型对象存入tbSceneryTickets列表中,到由于存在hibernate的映射和级联设置,也可以顺利的插入到数据库中,并且自动建立新增门票类型对象和景点对象的对对应关系。