When adding a new element to a persistent list using add(Object) with a list index position assigned to the end of the list, Hibernate adds the element and assigns the list index position equal to the the largest index+1 (equal to the new table size). This has the effect of inserting a null element at the next to last position in the list. It is remedied using add(int,Object) to specify the position during the add. This behavior changed from version 4.1, which would insert the element at the end of the list. This occurs for lazy lists that are not read prior to flush(). Tracing the SQL operations: 1. An insert is performed with the new list element (and its assigned list index). 2. select max (list index column) + 1 3. Update new element list index (Not that no element with the originally assigned list index exists) Here is an example mapping file and source:
<class name="name.willdarby.trading.account.TradeDate" table="ACCOUNT_TRADE_DATE">
<id name="id" column="ID" type="integer">
<generator class="increment" />
</id>
<natural-id>
<many-to-one name="user" column="UNAME" class="name.willdarby.trading.account.UserAccount"
cascade="none" not-null="true"/>
<property name="date" column="TRADE_DATE" type="date" not-null="true"/>
</natural-id>
<!-- Identify the trades reconciled with eMarket -->
<property name="tradesComplete" type="boolean" >
<column name="TRADES_COMPLETE" unique="false" index="TRADES_COMPLETE_IDX" not-null="true"/>
</property>
<list name="trades" table="ACCOUNT_TRADE" inverse="true"
cascade="all-delete-orphan" lazy="extra">
<key column="TRADE_DATE_ID" not-null="true" />
<list-index column="DATE_IDX" />
<one-to-many class="name.willdarby.trading.account.Trade" />
</list>
</class>
<!-- Individual trades -->
<class name="name.willdarby.trading.account.Trade" table="ACCOUNT_TRADE">
<id name="id" column="ID" type="integer">
<generator class="increment" /> <!-- Only for TEST use -->
</id>
<many-to-one name="tradeDate" column="TRADE_DATE_ID" class="name.willdarby.trading.account.TradeDate"
cascade="none" not-null="true"/>
<property name="idx" column="DATE_IDX" type="short" not-null="true" />
...
</class>
List<Trade> tradeList = tradeDate.getTrades();
final int initSize = tradeList.size();
Trade newTrade = new Trade();
newTrade.setTradeDate( tradeDate );
newTrade.setIdx( initSize );
tradeList.add( newTrade );
session.flush();
tradeDate = acct.getTradeDate( trades.getDate() );
ListIterator<Trade> tradeIter = tradeDate.getTrades().listIterator( initSize );
If the list was not empty prior to executing this code, then tradeIter will point at an empty element and the trade list will now contain initSize+2 elements. |