I have a domain object that represents a 1: n ratio between database tables.
public class ObservationWithData { private Observation observation; private Map<Integer,ElementValue> elementValues;
Both Observation and ElementValue contain
private int datetimeId; private int stationId;
I have a select query that joins two tables. I would like to use resultMap to group ElementValues ββwith observations based on datetimeId and stationId, as in this example (non-working).
<resultMap id="observationWithDataMap" class="ObservationWithData" groupBy="observation.datetimeId,observation.stationId"> <result property="observation" resultMap="ObservationSql.observationMap" /> <result property="elementValues" resultMap="ElementSql.elementValueMap"/> </resultMap>
There are two problems with this. First, the groupBy tag does not allow nested syntax, and the second iBATIS requires that the grouped property be part of the Java collections API, and Map does not meet this criterion.
I can get around the first problem by adding datetimeId and accessId, and the second element can be solved by creating a collection for writing, and then add all the elements to the map after popping the data.
<resultMap id="observationWithDataMap" class="ObservationWithData" groupBy="datetimeId,stationId"> <result property="stationId" column="station_id" /> <result property="datetimeId" column="datetime_id" /> <result property="observation" resultMap="ObservationSql.observationMap" /> <result property="elementValueCollection" resultMap="ElementSql.elementValueMap"/> </resultMap> public class ObservationWithData { private Observation observation; private Map<Integer,ElementValue> elementValues; // this collection is used for database retrieval only; do not add to it private Collection<ElementValue> elementValueCollection; public void setStationId(int id) { } public int getStationId() { return observation==null?0:observation.getStationId(); } public void setDatetimeId(int id) { } public int getDatetimeId() { return observation==null?0:observation.getDatetimeId(); } public Map<Integer,ElementValue> getElementValues() { if (elementValues.size()==0 && elementValueCollection.size()>0) { for (ElementValue val : elementValueCollection) { elementValues.put(val.getElementId(), val); } elementValueCollection.clear(); } return elementValues; } public Collection<ElementValue> getElementValueCollection() { if (elementValueCollection.size()==0 && elementValues.size()>0) { elementValueCollection.addAll(elementValues.values()); elementValues.clear(); } return elementValueCollection; } ... }
I am not very happy with this solution, because now there are many public methods that I do not want people to use their code. Identifiers are noops because these identifiers are set in the watch. I could have ObserveWithData extend Observation, but I designed this class to support composition over inheritance for efficient Java. I could also synchronize the map and the collection in different ways, but the idea is that I will let iBATIS fill the collection, and then when access is made to it, I will move all the values ββto it, keeping only one link.
Can someone recommend a more elegant solution?
Edit:
As a result, I extracted the service level for this. The service level makes two database calls through the DAO instead of one; the first retrieves all Observations, and the second retrieves all ElementValues. It creates ObservationWithData objects from these collections. It also throttles the query as it is a large data set.
This is a bit awkward and inefficient because I manually create objects. However, since it is βhiddenβ at the service level, I feel that it is less intrusive for API users who get an object with an uncluttered domain to work with.