I have a domain object which represents a 1:n relationship between database tables.
public class ObservationWithData {
private Observation observation;
private Map<Integer,ElementValue> elementValues;
// accessor methods
... }
Both Observation and ElementValue contain
pr开发者_Python百科ivate int datetimeId;
private int stationId;
// accessor methods
...
I have a selection query which joins the two tables. I'd like to use a resultMap to group the ElementValues with the Observations based on the datetimeId and stationId, as in this (nonworking) example.
<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 the nested syntax, and second iBATIS requires the grouped property to be part of the Java Collections API, and Map does not meet that criterion.
I can work around the first problem by adding datetimeId and stationId accessors, and the second item can be solved by creating a collection to write to and then add all the items to the map after the data pull.
<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'm not very happy with this solution, though, because now there are a lot of public methods that I don't want people to use in their code. The id setters are noops because these ids get set in the observation. I could have ObservationWithData extend Observation, but I designed this class to favor composition over inheritance per Effective Java. I could also synchronize the map and collection differently, but the idea is that I'll let iBATIS populate the collection, and then when the map is accessed, I'll move all the values into it, keeping just one reference.
Can someone recommend a more elegant solution?
Edit:
I ended up extracting a service layer to handle this. The service layer makes two database calls through the DAOs instead of one; the first retrieves all the Observations and the second retrieves all the ElementValues. It constructs the ObservationWithData objects from these collections. It also throttles the request since this is a large dataset.
This is a little clunky and inefficient, because I'm "manually" constructing the objects. However, since this is "hidden" in the service layer, I feel that it's less intrusive to the API users, who get an uncluttered domain object to work with.
Did you try writing your DAO so that this is a 2-stage proces, where you first fetch the ObservationWithData objects and then fill them with elementValues? It will not scale for huge numbers of rows, but depending on what you need to fetch it may make it a little cleaner on the outside for now.
Another option would be to fetch the data from the database by mapping results to package private objects with getters and setters, and then returning them to the caller as different class without the setters. Not sure if that will help though, I think it maybe even add more mess to the solution.
We have mapped the complex collection properties by using groupBy attribute of ibatis as used below-
<resultMap id="actionResultsMap" class="Content" groupBy="objectID">
<result property="objectID" column="object_id"/>
<result property="bvReferenceNo" column="reference_no"/>
.......
<result property="countryId" resultMap="MyActions.countryMap"/>
</resultMap>
<resultMap id="countryMap" class="java.lang.Integer">
<result property="value" column="country"/>
</resultMap>
<select id="myActions" resultMap="actionResultsMap"
parameterClass="java.util.HashMap">
SELECT
object_id,
reference_no,
........
country
from sysObject so left outer join countryTable ct on ct.object_id = so.object_id
</select>
Here countryId is of type List<Integer>
.
精彩评论